Skip to content

Instantly share code, notes, and snippets.

@monteith
Last active August 26, 2022 16:30
Show Gist options
  • Save monteith/a602a6df707eaf882b5c64ef55d11c4b to your computer and use it in GitHub Desktop.
Save monteith/a602a6df707eaf882b5c64ef55d11c4b to your computer and use it in GitHub Desktop.
Segment to DY function
// Learn more about destination functions API at
// https://segment.com/docs/connections/destinations/destination-functions
/**
* Handle identify event
* @param {SegmentIdentifyEvent} event
* @param {FunctionSettings} settings
*/
let defaultEventBody = {
user: {
dyid: undefined, // dyId,
dyid_server: undefined, // dyId,
},
session: {
dy: undefined, // sessionId,
},
events: [],
context: {
page: {
type: 'OTHER',
data: [],
location: 'https://segment.io',
},
device: {
userAgent: 'Segment.io',
},
},
};
async function onIdentify(event, settings) {
const { dyApiKey, personaToken, personaSpaceId } = settings;
const { anonymousId, traits } = event;
if (!anonymousId) {
console.log(`No anonymousId found, exiting...`);
return;
}
let eventBody = { ...defaultEventBody };
const hashedEmail = getHash(traits['email']) || undefined;
if (hashedEmail) {
eventBody.events.push({
name: 'Segment Email',
properties: {
dyType: 'login-v1',
hashedEmail: hashedEmail,
},
});
}
const audienceKey = event.context.personas.computation_key;
const audienceValue = traits[audienceKey];
if (audienceValue) {
eventBody.events.push({
name: audienceKey,
properties: {
member: audienceValue,
anonymousId: anonymousId,
},
});
} else {
console.log(`No audienceValue found in traits`);
}
const dyId = await getUserDyId(anonymousId, personaSpaceId, personaToken);
if (!dyId) {
console.log(`No DY data returned, exiting...`);
return;
}
eventBody.user.dyid = dyId;
eventBody.user.dyid_server = dyId;
const sessionId = await getSessionId(dyApiKey);
if (!sessionId) {
console.log(`No sessionId found`);
}
eventBody.session.dy = sessionId;
await postDyEvent(eventBody, dyApiKey);
}
async function getSessionId(apiKey) {
const url = `https://dy-api.com/v2/serve/user/choose`;
const event = { ...defaultEventBody };
event.selector = { names: [''] };
try {
const res = await fetch(url, {
method: 'POST',
headers: {
'DY-API-key': apiKey,
'Content-Type': 'application/json',
'User-Agent': 'segment.io',
},
body: JSON.stringify(body),
});
const resJson = await res.json();
return Object.values(resJson.cookies)[1].value;
} catch (error) {
throw new RetryError(error.message);
}
}
async function getUserDyId(anonymousId, personaSpaceId, personaToken) {
const url = `https://profiles.segment.com/v1/spaces/${personaSpaceId}/collections/users/profiles/anonymous_id:${anonymousId}/external_ids?include=dy_id&limit=1`;
const res = await fetch(url, {
headers: new Headers({
Authorization: 'Basic ' + btoa(personaToken + ':'),
'Content-Type': 'application/json',
}),
method: 'GET',
});
if (res.ok) {
console.log(
'Profile API request successful. Response: ',
res.status,
res.statusText
);
} else if (res.status === 404) {
console.log('anonymous_id', anonymousId, 'does not exist.');
} else if (res.status === 429 || res.status >= 500) {
console.log(
'Something went wrong, this event will be retried. Error: ',
res.status,
res.statusText
);
throw new RetryError(
`Profile API not available. GET ${url} = ${res.status} ${res.statusText}`
);
} else {
console.log(
'Something went wrong, please check your settings. Error: ',
res.status,
res.statusText
);
throw new Error(
`Something went wrong. GET ${url} = ${res.status} ${res.statusText}`
);
}
const resJson = await res.json();
return resJson.data[0].id || null;
}
async function postDyEvent(event, apiKey) {
const url = 'https://dy-api.com/v2/collect/user/event';
try {
const res = await fetch(url, {
method: 'POST',
headers: {
'DY-API-key': apiKey,
'Content-Type': 'application/json',
'User-Agent': 'segment.io',
},
body: JSON.stringify(event),
});
} catch (error) {
throw new RetryError(error.message);
}
}
function getHash(path) {
if (path) {
return crypto.createHash('sha256').update(path).digest('hex');
} else {
return null;
}
}
/**
* Handle track event
* @param {SegmentTrackEvent} event
* @param {FunctionSettings} settings
*/
async function onTrack(event, settings) {
// Learn more at https://segment.com/docs/connections/spec/identify/
throw new EventNotSupported('identify is not supported');
}
/**
* Handle group event
* @param {SegmentGroupEvent} event
* @param {FunctionSettings} settings
*/
async function onGroup(event, settings) {
// Learn more at https://segment.com/docs/connections/spec/group/
throw new EventNotSupported('group is not supported');
}
/**
* Handle page event
* @param {SegmentPageEvent} event
* @param {FunctionSettings} settings
*/
async function onPage(event, settings) {
// Learn more at https://segment.com/docs/connections/spec/page/
throw new EventNotSupported('page is not supported');
}
/**
* Handle screen event
* @param {SegmentScreenEvent} event
* @param {FunctionSettings} settings
*/
async function onScreen(event, settings) {
// Learn more at https://segment.com/docs/connections/spec/screen/
throw new EventNotSupported('screen is not supported');
}
/**
* Handle alias event
* @param {SegmentAliasEvent} event
* @param {FunctionSettings} settings
*/
async function onAlias(event, settings) {
// Learn more at https://segment.com/docs/connections/spec/alias/
throw new EventNotSupported('alias is not supported');
}
/**
* Handle delete event
* @param {SegmentDeleteEvent} event
* @param {FunctionSettings} settings
*/
async function onDelete(event, settings) {
// Learn more at https://segment.com/docs/partners/spec/#delete
throw new EventNotSupported('delete is not supported');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment