-
Notifications
You must be signed in to change notification settings - Fork 256
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adjust Action Destination: First implementation + unit tests. (#2144)
* First implementation + unit tests. * Not using `rawData` in the type. That breaks the build. * Just not checking `data` for a `rawData` property. * Update packages/destination-actions/src/destinations/adjust/index.ts Co-authored-by: Joe Ayoub <45374896+joe-ayoub-segment@users.noreply.github.com> * Update packages/destination-actions/src/destinations/adjust/functions.ts Co-authored-by: Joe Ayoub <45374896+joe-ayoub-segment@users.noreply.github.com> * Update packages/destination-actions/src/destinations/adjust/functions.ts Co-authored-by: Joe Ayoub <45374896+joe-ayoub-segment@users.noreply.github.com> * Modifications requested in PR review. * Unit test adjustments. * Not using snapshot tests. * Updating generated types. * ready for deploy --------- Co-authored-by: Joe Ayoub <45374896+joe-ayoub-segment@users.noreply.github.com> Co-authored-by: Joe Ayoub <joe.ayoub@segment.com>
- Loading branch information
Showing
7 changed files
with
358 additions
and
0 deletions.
There are no files selected for viewing
53 changes: 53 additions & 0 deletions
53
packages/destination-actions/src/destinations/adjust/functions.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { RequestClient } from '@segment/actions-core/create-request-client' | ||
import { IntegrationError } from '@segment/actions-core' | ||
import { AdjustPayload } from './types' | ||
import { Settings } from './generated-types' | ||
import { Payload } from './sendEvent/generated-types' | ||
|
||
export function validatePayload(payload: Payload, settings: Settings): AdjustPayload { | ||
if (!payload.app_token && !settings.default_app_token) { | ||
throw new IntegrationError( | ||
'One of app_token field or default_app_token setting fields must have a value.', | ||
'APP_TOKEN_VALIDATION_FAILED', | ||
400 | ||
) | ||
} | ||
|
||
if (!payload.event_token && !settings.default_event_token) { | ||
throw new IntegrationError( | ||
'One of event_token field or default_event_token setting fields must have a value.', | ||
'EVENT_TOKEN_VALIDATION_FAILED', | ||
400 | ||
) | ||
} | ||
|
||
const adjustPayload: AdjustPayload = { | ||
app_token: String(payload.app_token || settings.default_app_token), | ||
event_token: String(payload.event_token || settings.default_event_token), | ||
environment: settings.environment, | ||
s2s: 1, | ||
callback_params: JSON.stringify(payload), | ||
created_at_unix: payload.timestamp | ||
? parseInt((new Date(String(payload.timestamp)).getTime() / 1000).toFixed(0)) | ||
: undefined | ||
} | ||
|
||
return adjustPayload | ||
} | ||
|
||
/** | ||
* This is ready for batching, but batching is not implemented here for now. | ||
* @param request The request client. | ||
* @param events The events. | ||
* @returns An array of responses. | ||
*/ | ||
export async function sendEvents(request: RequestClient, events: AdjustPayload[]) { | ||
return await Promise.all( | ||
events.map((event) => | ||
request('https://s2s.adjust.com/event', { | ||
method: 'POST', | ||
body: JSON.stringify(event) | ||
}) | ||
) | ||
) | ||
} |
16 changes: 16 additions & 0 deletions
16
packages/destination-actions/src/destinations/adjust/generated-types.ts
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
45 changes: 45 additions & 0 deletions
45
packages/destination-actions/src/destinations/adjust/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import type { DestinationDefinition } from '@segment/actions-core' | ||
import type { Settings } from './generated-types' | ||
|
||
import sendEvent from './sendEvent' | ||
|
||
const destination: DestinationDefinition<Settings> = { | ||
name: 'Adjust (Actions)', | ||
slug: 'actions-adjust', | ||
mode: 'cloud', | ||
description: 'Send events to Adjust.', | ||
authentication: { | ||
scheme: 'custom', | ||
fields: { | ||
environment: { | ||
label: 'Environment', | ||
description: 'The environment for your Adjust account.', | ||
type: 'string', | ||
required: true, | ||
choices: [ | ||
{ label: 'Production', value: 'production' }, | ||
{ label: 'Sandbox', value: 'sandbox' } | ||
], | ||
default: 'production' | ||
}, | ||
default_app_token: { | ||
label: 'Default App Token', | ||
description: 'The app token for your Adjust account. This can be overridden in the event mapping.', | ||
type: 'string', | ||
required: false | ||
}, | ||
default_event_token: { | ||
label: 'Default Event Token', | ||
description: 'The default event token. This can be overridden in the event mapping.', | ||
type: 'string', | ||
required: false | ||
} | ||
} | ||
}, | ||
|
||
actions: { | ||
sendEvent | ||
} | ||
} | ||
|
||
export default destination |
105 changes: 105 additions & 0 deletions
105
packages/destination-actions/src/destinations/adjust/sendEvent/__tests__/index.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import nock from 'nock' | ||
// import { createTestEvent, createTestIntegration } from '@segment/actions-core' | ||
import { createTestEvent, createTestIntegration } from '@segment/actions-core' | ||
|
||
import Destination from '../../index' | ||
|
||
const testDestination = createTestIntegration(Destination) | ||
|
||
const DEVICE_ID = 'device-id' // References device.id | ||
const ADVERTISING_ID = 'foobar' // References device.advertisingId | ||
const DEVICE_TYPE = 'ios' // References device.type | ||
|
||
describe('Adjust.sendEvent', () => { | ||
describe('Success cases', () => { | ||
it('should send an event to Adjust, default mappings, default parameters in Settings', async () => { | ||
nock('https://s2s.adjust.com').post('/event').reply(200, { status: 'OK' }) | ||
|
||
const goodEvent = createTestEvent({ | ||
type: 'track', | ||
context: { | ||
device: { | ||
id: DEVICE_ID, | ||
advertisingId: ADVERTISING_ID, | ||
type: DEVICE_TYPE | ||
}, | ||
library: { | ||
name: 'analytics-ios', | ||
version: '4.0.0' | ||
} | ||
}, | ||
properties: { | ||
revenue: 10, | ||
currency: 'USD' | ||
} | ||
}) | ||
|
||
const responses = await testDestination.testAction('sendEvent', { | ||
event: goodEvent, | ||
useDefaultMappings: true, | ||
settings: { | ||
environment: 'sandbox', | ||
default_app_token: 'app-token', | ||
default_event_token: 'event-token' | ||
} | ||
}) | ||
|
||
expect(responses).toHaveLength(1) | ||
expect(responses[0].content).toBeDefined() | ||
expect(responses[0].content).toEqual(JSON.stringify({ status: 'OK' })) | ||
}) | ||
|
||
it('should send an event to Adjust, custom mapping, App Token and Event Token mapped as properties', async () => { | ||
nock('https://s2s.adjust.com').post('/event').reply(200, { status: 'OK' }) | ||
|
||
const goodEvent = createTestEvent({ | ||
type: 'track', | ||
context: { | ||
device: { | ||
id: DEVICE_ID, | ||
advertisingId: ADVERTISING_ID, | ||
type: DEVICE_TYPE | ||
}, | ||
library: { | ||
name: 'analytics-ios', | ||
version: '4.0.0' | ||
} | ||
}, | ||
properties: { | ||
revenue: 10, | ||
currency: 'USD', | ||
appToken: 'app-token', | ||
eventToken: 'event-token' | ||
} | ||
}) | ||
|
||
const responses = await testDestination.testAction('sendEvent', { | ||
event: goodEvent, | ||
mapping: { | ||
app_token: { | ||
'@path': '$.properties.appToken' | ||
}, | ||
event_token: { | ||
'@path': '$.properties.eventToken' | ||
}, | ||
device_id: { | ||
'@path': '$.context.device.id' | ||
}, | ||
advertising_id: { | ||
'@path': '$.context.device.advertisingId' | ||
}, | ||
device_type: { | ||
'@path': '$.context.device.type' | ||
} | ||
}, | ||
settings: { | ||
environment: 'sandbox' | ||
} | ||
}) | ||
|
||
expect(responses).toHaveLength(1) | ||
expect(responses[0].content).toBeDefined() | ||
expect(responses[0].content).toEqual(JSON.stringify({ status: 'OK' })) | ||
}) | ||
}) | ||
}) |
40 changes: 40 additions & 0 deletions
40
packages/destination-actions/src/destinations/adjust/sendEvent/generated-types.ts
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
91 changes: 91 additions & 0 deletions
91
packages/destination-actions/src/destinations/adjust/sendEvent/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import type { ActionDefinition } from '@segment/actions-core' | ||
import type { Settings } from '../generated-types' | ||
import type { Payload } from './generated-types' | ||
import { sendEvents, validatePayload } from '../functions' | ||
|
||
const action: ActionDefinition<Settings, Payload> = { | ||
title: 'Send Event', | ||
description: 'Sends an Event to Adjust.', | ||
defaultSubscription: 'type = "track" and event = "Conversion Completed"', | ||
fields: { | ||
timestamp: { | ||
label: 'Timestamp', | ||
description: 'Timestamp for when the event happened.', | ||
type: 'datetime', | ||
default: { | ||
'@path': '$.timestamp' | ||
}, | ||
required: false | ||
}, | ||
app_token: { | ||
label: 'App Token', | ||
description: 'The app token for your Adjust account. Overrides the Default App Token from Settings.', | ||
type: 'string', | ||
required: false | ||
}, | ||
event_token: { | ||
label: 'Event Token', | ||
description: 'The event token. Overrides the Default Event Token from Settings.', | ||
type: 'string', | ||
required: false | ||
}, | ||
device_id: { | ||
label: 'Device ID', | ||
description: 'The unique device identifier', | ||
type: 'string', | ||
default: { | ||
'@path': '$.context.device.id' | ||
}, | ||
required: true | ||
}, | ||
advertising_id: { | ||
label: 'Advertising ID', | ||
description: 'The advertising identifier ("idfa" for iOS, "gps_adid" for Android).', | ||
type: 'string', | ||
default: { | ||
'@path': '$.context.device.advertisingId' | ||
}, | ||
required: true | ||
}, | ||
device_type: { | ||
label: 'Device Type', | ||
description: 'The device type. Options: "ios" or "android".', | ||
type: 'string', | ||
default: { | ||
'@path': '$.context.device.type' | ||
}, | ||
required: true | ||
}, | ||
library_name: { | ||
label: 'Library Name', | ||
description: | ||
'The name of the Segment library used to trigger the event. E.g. "analytics-ios" or "analytics-android".', | ||
type: 'string', | ||
default: { | ||
'@path': '$.context.library.name' | ||
}, | ||
required: false | ||
}, | ||
revenue: { | ||
label: 'Revenue', | ||
description: | ||
'The revenue amount of the event. E.g. 75.5 for $75.50. Currency can be set with the "Currency field".', | ||
type: 'number', | ||
required: false, | ||
default: { '@path': '$.properties.revenue' } | ||
}, | ||
currency: { | ||
label: 'Currency', | ||
description: 'The revenue currency. Only set if revenue is also set. E.g. "USD" or "EUR".', | ||
type: 'string', | ||
required: false, | ||
default: { '@path': '$.properties.currency' } | ||
} | ||
}, | ||
perform: (request, data) => { | ||
const adjustPayload = validatePayload(data.payload, data.settings) | ||
return sendEvents(request, [adjustPayload]) | ||
} | ||
} | ||
|
||
export default action |
8 changes: 8 additions & 0 deletions
8
packages/destination-actions/src/destinations/adjust/types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export interface AdjustPayload { | ||
app_token: string | ||
event_token: string | ||
environment: string | ||
s2s: number | ||
callback_params?: string | ||
created_at_unix?: number | ||
} |