diff --git a/Readme.md b/Readme.md index 7217b28..3b4df46 100644 --- a/Readme.md +++ b/Readme.md @@ -35,7 +35,8 @@ this repo as the set of examples. - [Slack](./destinations/slack) - Adds a Gravatar icon to events with an email and sends messages to Slack - [Zendesk](./destinations/zendesk) - Create new Zendesk tickets triggered by events that you send - [Datadog](./destinations/datadog) - Sends a metric to datadog with high level message/event type as tags -- [Optimizely](./destinations/optimizely) - Sends conversion metrix to optimizely. +- [Optimizely](./destinations/optimizely) - Sends conversion metrix to optimizely +- [Retrack](./destinations/retrack) - Intercepts track event, updates user identity and enriches said event ## Development diff --git a/destinations/retrack/Readme.md b/destinations/retrack/Readme.md new file mode 100644 index 0000000..accff13 --- /dev/null +++ b/destinations/retrack/Readme.md @@ -0,0 +1,11 @@ +# Re-track Custom Destination Function + +Example of destination function which: +* Intercepts track event from Segment source attached to it, +* modifies userId and posts [alias](https://segment.com/docs/connections/spec/alias/) request, so Segment will know anticipated identity, +* enriches event properties and posts [track](https://segment.com/docs/connections/spec/track/) request, so Segment will know enriched event, +* all requests are being forwarded to another [Segment HTTP Tracking API Source](https://segment.com/docs/connections/sources/catalog/libraries/server/http-api/), given `apiKey` from function settings. + +## Settings + +- `apiKey` {String} ([write key of Segment HTTP Tracking API Source](https://segment.com/docs/connections/sources/catalog/libraries/server/http-api/)) diff --git a/destinations/retrack/handler.js b/destinations/retrack/handler.js new file mode 100644 index 0000000..e69dad2 --- /dev/null +++ b/destinations/retrack/handler.js @@ -0,0 +1,81 @@ +/** + * @typedef {Object} SegmentTrackEvent + * @see {@link https://segment.com/docs/connections/spec/track/} for full track event spec + * @see {@link https://segment.com/docs/connections/spec/common/} for full common fields spec + * @property {String} event Track event name + * @property {String} messageId Original API message identifier + * @property {String} userId Tracked user id that we want to modify and re-track + * @property {String} timestamp Timestamp of event in ISO 8601 format + * @property {Object} properties Track event arbitrary properties, we want to add 3 own + */ + +/** + * @typedef {Object} FunctionSettings + * @property {String} apiKey Write key of source to forward alias and track events to + */ + +/** + * Call Segment V1 API, using HTTP API Segment Source by write key + * @see {@link https://segment.com/docs/connections/sources/catalog/libraries/server/http-api/} for source doc + * @internal + * @param {String} apiKey + * @param {String<'alias'|'track'>} route + * @param {Object} body + * @return {Promise} + */ +async function callSegmentAPI(apiKey, route, body) { + let response; + + try { + response = await fetch(`https://api.segment.io/v1/${route}`, { + method: 'POST', + headers: { + Authorization: `Basic ${btoa(`${apiKey}:`)}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); + } catch (error) { + throw new RetryError(error.message); + } + + if (response.status >= 500 || response.status === 429) { + throw new RetryError(`Failed with ${response.status}`); + } +} + +/** + * Handle track event + * @param {SegmentTrackEvent} event + * @param {FunctionSettings} settings + * @return {Promise} + */ +async function onTrack(event, settings) { + const { apiKey } = settings; + + if (!apiKey) { + throw new ValidationError('Forward source write key is required'); + } + + const anticipatedId = crypto.createHash('md5').update(event.userId).digest('hex'); + + const [slug1, slug2, slug3] = event.userId.split('-'); + + await Promise.all([ + callSegmentAPI(apiKey, 'alias', { + previousId: event.userId, + userId: anticipatedId, + timestamp: event.timestamp, + }), + callSegmentAPI(apiKey, 'track', { + event: event.event, + userId: anticipatedId, + properties: { + ...event.properties, + slug1, + slug2, + slug3, + }, + }), + ]); +}