Skip to content

initial Radar destination function #68

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ this repo as the set of examples.
- [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.
- [Radar](./destinations/radar) - Send events to Radar for enrichment with location context

## Development

Expand Down
9 changes: 9 additions & 0 deletions destinations/radar/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Radar Custom Destination Function

This function forwards events to Radar as a [`track` event](https://radar.com/documentation/api#track) for both anonymous and named users.

The function requires coordinates to be sent up following the common fields spec.

## Settings

- `radarPublishableKey` (string): [Radar Publishable Key](https://radar.com/documentation/)
155 changes: 155 additions & 0 deletions destinations/radar/handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// This example uses the Radar track API

/**
* Handle track event
* @param {SegmentTrackEvent} event
* @param {FunctionSettings} settings
*/
async function onTrack(event, settings) {
const endpoint = 'https://api.radar.io/v1/track';
let response;

try {
if (hasRequiredIdentifiers(event) && hasRequiredLocationContext(event)) {
radarTrackPayload = createPayload(event);
setIfDefined(event,'anonymousId',radarTrackPayload,'metadata.segmentAnonymousId');
setIfDefined(event, 'userId', radarTrackPayload, 'userId');
setIfDefined(event, 'context.os.version', radarTrackPayload, 'deviceOS');
setIfDefined(event,'context.device.manufacturer',radarTrackPayload,'deviceMake');
setIfDefined(event,'context.device.model',radarTrackPayload,'deviceModel');
response = await fetch(endpoint, {
method: 'POST',
headers: {
Authorization: `${settings.radarPublishableKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(radarTrackPayload)
});
} else {
throw new InvalidEventPayload('Missing required attributes');
}
} catch (error) {
// Retry on connection error
throw new RetryError(error.message);
}

if (response.status >= 500 || response.status === 429) {
// Retry on 5xx (server errors) and 429s (rate limits)
throw new RetryError(`Failed with ${response.status}`);
}
}

const setIfDefined = (
sourceObject,
sourcePath,
destObject,
destPath,
defaultValue
) => {
let value = _.get(sourceObject, sourcePath);
if (value !== undefined) {
_.set(destObject, destPath, value);
} else if (defaultValue !== undefined) {
_.set(destObject, destPath, defaultValue);
}
};

/**
* Handle identify event
* @param {SegmentIdentifyEvent} event
* @param {FunctionSettings} settings
*/
async function onIdentify(event, settings) {
throw new EventNotSupported('identify is not supported');
}

/**
* Handle group event
* @param {SegmentGroupEvent} event
* @param {FunctionSettings} settings
*/
async function onGroup(event, settings) {
throw new EventNotSupported('group is not supported');
}

/**
* Handle page event
* @param {SegmentPageEvent} event
* @param {FunctionSettings} settings
*/
async function onPage(event, settings) {
throw new EventNotSupported('page is not supported');
}

/**
* Handle screen event
* @param {SegmentScreenEvent} event
* @param {FunctionSettings} settings
*/
async function onScreen(event, settings) {
throw new EventNotSupported('screen is not supported');
}

/**
* Handle alias event
* @param {SegmentAliasEvent} event
* @param {FunctionSettings} settings
*/
async function onAlias(event, settings) {
throw new EventNotSupported('alias is not supported');
}

/**
* Handle delete event
* @param {SegmentDeleteEvent} event
* @param {FunctionSettings} settings
*/
async function onDelete(event, settings) {
throw new EventNotSupported('delete is not supported');
}

function hasRequiredLocationContext(event) {
if (event.context?.location?.latitude && event.context?.location?.longitude) {
return true;
} else {
return false;
}
}

function hasRequiredIdentifiers(event) {
if (event.context?.device?.id || event.anonymousId) {
return true;
} else {
return false;
}
}

function deviceTypeTransform(deviceType) {
if (deviceType) {
switch (deviceType.toLowerCase()) {
case 'ios':
return 'iOS';
case 'android':
return 'Android';
default:
return 'Web';
}
} else {
return 'Web';
}
}

const createPayload = e => {
var date = new Date();
return {
deviceId: e.context.device?.id ?? e.anonymousId,
latitude: e.context.location.latitude,
longitude: e.context.location.longitude,
accuracy: e.context.location.accuracy ?? 65,
deviceType: deviceTypeTransform(e.context.device?.type),
foreground: true,
stopped: true,
metadata: {},
updatedAt: e.sentAt ?? date.toISOString()
};
};