-
Notifications
You must be signed in to change notification settings - Fork 253
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First push with CustomEvents Destination Action (#2570)
* Saving progress * Update packages/destination-actions/src/destinations/attentive/index.ts Co-authored-by: Joe Ayoub <[email protected]> * Update packages/destination-actions/src/destinations/attentive/customEvents/index.ts Co-authored-by: Joe Ayoub <[email protected]> * Update packages/destination-actions/src/destinations/attentive/customEvents/index.ts Co-authored-by: Joe Ayoub <[email protected]> * Update packages/destination-actions/src/destinations/attentive/customEvents/index.ts Co-authored-by: Joe Ayoub <[email protected]> * Update packages/destination-actions/src/destinations/attentive/customEvents/index.ts Co-authored-by: Joe Ayoub <[email protected]> * Update packages/destination-actions/src/destinations/attentive/customEvents/index.ts Co-authored-by: Joe Ayoub <[email protected]> * Update packages/destination-actions/src/destinations/attentive/customEvents/index.ts Co-authored-by: Joe Ayoub <[email protected]> * Update packages/destination-actions/src/destinations/attentive/customEvents/index.ts Co-authored-by: Joe Ayoub <[email protected]> * Update packages/destination-actions/src/destinations/attentive/customEvents/index.ts Co-authored-by: Joe Ayoub <[email protected]> * Update packages/destination-actions/src/destinations/attentive/customEvents/index.ts Co-authored-by: Joe Ayoub <[email protected]> * Update index.ts Added default mapping for phone field * refacting the fields and request * correcting yarn version * minor tweaks * minor tweaks * adding types * Update index.test.ts * Update index.test.ts * Update index.test.ts * Update index.test.ts * adding types * New endpoints * added of endpoints * bug fixes * config file added * index updates * updating snapshot and fixing a field type issue * adding a unit test * new unit test * fixing breaking test * removing snapshot --------- Co-authored-by: Joe Ayoub <[email protected]> Co-authored-by: Joe Ayoub <[email protected]>
- Loading branch information
1 parent
181af10
commit b1c9e56
Showing
6 changed files
with
353 additions
and
0 deletions.
There are no files selected for viewing
102 changes: 102 additions & 0 deletions
102
packages/destination-actions/src/destinations/attentive/customEvents/__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,102 @@ | ||
import nock from 'nock' | ||
import { createTestEvent, createTestIntegration, SegmentEvent, PayloadValidationError } from '@segment/actions-core' | ||
import Definition from '../../index' | ||
import { Settings } from '../../generated-types' | ||
|
||
let testDestination = createTestIntegration(Definition) | ||
const timestamp = '2024-01-08T13:52:50.212Z' | ||
|
||
const settings: Settings = { | ||
apiKey: 'test-api-key' | ||
} | ||
|
||
const validPayload = { | ||
timestamp: timestamp, | ||
event: 'Event Type 1', | ||
messageId: '123e4567-e89b-12d3-a456-426614174000', | ||
type: 'track', | ||
userId: '123e4567-e89b-12d3-a456-426614174000', | ||
context: { | ||
traits: { | ||
phone: '+3538675765689', | ||
email: '[email protected]' | ||
} | ||
}, | ||
properties: { | ||
tracking_url: 'https://tracking-url.com', | ||
product_name: 'Product X' | ||
} | ||
} as Partial<SegmentEvent> | ||
|
||
const mapping = { | ||
type: { '@path': '$.event' }, | ||
userIdentifiers: { | ||
phone: { '@path': '$.context.traits.phone' }, | ||
email: { '@path': '$.context.traits.email' }, | ||
clientUserId: { '@path': '$.userId' } | ||
}, | ||
properties: { '@path': '$.properties' }, | ||
externalEventId: { '@path': '$.messageId' }, | ||
occurredAt: { '@path': '$.timestamp' } | ||
} | ||
|
||
const expectedPayload = { | ||
type: 'Event Type 1', | ||
properties: { | ||
tracking_url: 'https://tracking-url.com', | ||
product_name: 'Product X' | ||
}, | ||
externalEventId: '123e4567-e89b-12d3-a456-426614174000', | ||
occurredAt: '2024-01-08T13:52:50.212Z', | ||
user: { | ||
phone: '+3538675765689', | ||
email: '[email protected]', | ||
externalIdentifiers: { | ||
clientUserId: '123e4567-e89b-12d3-a456-426614174000' | ||
} | ||
} | ||
} | ||
|
||
beforeEach((done) => { | ||
testDestination = createTestIntegration(Definition) | ||
nock.cleanAll() | ||
done() | ||
}) | ||
|
||
describe('Attentive.customEvents', () => { | ||
it('should send a custom event to Attentive', async () => { | ||
const event = createTestEvent(validPayload) | ||
|
||
nock('https://api.attentivemobile.com').post('/v1/events/custom', expectedPayload).reply(200, {}) | ||
|
||
const responses = await testDestination.testAction('customEvents', { | ||
event, | ||
settings, | ||
useDefaultMappings: true, | ||
mapping | ||
}) | ||
|
||
expect(responses.length).toBe(1) | ||
expect(responses[0].status).toBe(200) | ||
}) | ||
|
||
it('should throw error if no identifiers provided', async () => { | ||
const badPayload = { | ||
...validPayload | ||
} | ||
delete badPayload?.context?.traits?.phone | ||
delete badPayload?.context?.traits?.email | ||
badPayload.userId = undefined | ||
|
||
const event = createTestEvent(badPayload) | ||
|
||
await expect( | ||
testDestination.testAction('customEvents', { | ||
event, | ||
settings, | ||
useDefaultMappings: true, | ||
mapping | ||
}) | ||
).rejects.toThrowError(new PayloadValidationError('At least one user identifier is required.')) | ||
}) | ||
}) |
40 changes: 40 additions & 0 deletions
40
packages/destination-actions/src/destinations/attentive/customEvents/generated-types.ts
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
135 changes: 135 additions & 0 deletions
135
packages/destination-actions/src/destinations/attentive/customEvents/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,135 @@ | ||
import { ActionDefinition, PayloadValidationError } from '@segment/actions-core' | ||
import type { Settings } from '../generated-types' | ||
import type { Payload } from './generated-types' | ||
import { CustomEvent, User } from './types' | ||
|
||
const action: ActionDefinition<Settings, Payload> = { | ||
title: 'Custom Events', | ||
description: 'Send Segment analytics events to Attentive.', | ||
defaultSubscription: 'type = "track"', | ||
fields: { | ||
type: { | ||
label: 'Type', | ||
description: 'The type of event. This name is case sensitive. "Order shipped" and "Order Shipped" would be considered different event types.', | ||
type: 'string', | ||
required: true, | ||
default: { | ||
'@path': '$.event' | ||
} | ||
}, | ||
userIdentifiers: { | ||
label: 'User Identifiers', | ||
description: 'At least one identifier is required. Custom identifiers can be added as additional key:value pairs.', | ||
type: 'object', | ||
required: true, | ||
additionalProperties: true, | ||
defaultObjectUI: 'keyvalue:only', | ||
properties: { | ||
phone: { | ||
label: 'Phone', | ||
description: "The user's phone number in E.164 format.", | ||
type: 'string', | ||
required: false | ||
}, | ||
email: { | ||
label: 'Email', | ||
description: "The user's email address.", | ||
type: 'string', | ||
format: 'email', | ||
required: false | ||
}, | ||
clientUserId: { | ||
label: 'Client User ID', | ||
description: 'A primary ID for a user. Should be a UUID.', | ||
type: 'string', | ||
format: 'uuid', | ||
required: false | ||
} | ||
}, | ||
default: { | ||
phone: { | ||
'@if': { | ||
exists: { '@path': '$.context.traits.phone' }, | ||
then: { '@path': '$.context.traits.phone' }, | ||
else: { '@path': '$.properties.phone' } | ||
} | ||
}, | ||
email: { | ||
'@if': { | ||
exists: { '@path': '$.context.traits.email' }, | ||
then: { '@path': '$.context.traits.email' }, | ||
else: { '@path': '$.properties.email' } | ||
} | ||
}, | ||
clientUserId: { '@path': '$.userId' } | ||
} | ||
}, | ||
properties: { | ||
label: 'Properties', | ||
description: 'Metadata to associate with the event.', | ||
type: 'object', | ||
required: false, | ||
default: { | ||
'@path': '$.properties' | ||
} | ||
}, | ||
externalEventId: { | ||
label: 'External Event Id', | ||
description: 'A unique identifier representing this specific event. Should be a UUID format.', | ||
type: 'string', | ||
format: 'uuid', | ||
required: false, | ||
default: { | ||
'@path': '$.messageId' | ||
} | ||
}, | ||
occurredAt: { | ||
label: 'Occurred At', | ||
description: 'Timestamp for the event, ISO 8601 format.', | ||
type: 'string', | ||
required: false, | ||
default: { | ||
'@path': '$.timestamp' | ||
} | ||
} | ||
}, | ||
perform: (request, { payload }) => { | ||
const { | ||
externalEventId, | ||
type, | ||
properties, | ||
occurredAt, | ||
userIdentifiers: { phone, email, clientUserId, ...customIdentifiers } | ||
} = payload | ||
|
||
if (!email && !phone && !clientUserId && Object.keys(customIdentifiers).length === 0) { | ||
throw new PayloadValidationError('At least one user identifier is required.') | ||
} | ||
|
||
const json: CustomEvent = { | ||
type, | ||
properties, | ||
externalEventId, | ||
occurredAt, | ||
user: { | ||
phone, | ||
email, | ||
...(clientUserId || customIdentifiers | ||
? { | ||
externalIdentifiers: { | ||
...(clientUserId ? { clientUserId } : undefined), | ||
...(Object.entries(customIdentifiers).length>0 ? { customIdentifiers } : undefined) | ||
} | ||
} | ||
: {}) | ||
} as User | ||
} | ||
|
||
return request('https://api.attentivemobile.com/v1/events/custom', { | ||
method: 'post', | ||
json | ||
}) | ||
} | ||
} | ||
|
||
export default action |
16 changes: 16 additions & 0 deletions
16
packages/destination-actions/src/destinations/attentive/customEvents/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,16 @@ | ||
export interface CustomEvent { | ||
type: string | ||
properties?: Record<string, unknown> | ||
externalEventId?: string | ||
occurredAt?: string | ||
user: User | ||
} | ||
|
||
export interface User { | ||
phone?: string | ||
email?: string | ||
externalIdentifiers?: { | ||
clientUserId?: string | ||
[key: string]: string | undefined | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
packages/destination-actions/src/destinations/attentive/generated-types.ts
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
52 changes: 52 additions & 0 deletions
52
packages/destination-actions/src/destinations/attentive/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,52 @@ | ||
import { DestinationDefinition, defaultValues } from '@segment/actions-core' | ||
import type { Settings } from './generated-types' | ||
import customEvents from './customEvents' | ||
|
||
const destination: DestinationDefinition<Settings> = { | ||
name: 'Attentive', | ||
slug: 'actions-attentive', | ||
mode: 'cloud', | ||
description: 'Send Segment analytics events to Attentive.', | ||
authentication: { | ||
scheme: 'custom', | ||
fields: { | ||
apiKey: { | ||
label: 'API Key', | ||
description: 'Your Attentive API Key.', | ||
type: 'string', | ||
required: true | ||
} | ||
}, | ||
testAuthentication: async (request, { settings }) => { | ||
return request('https://api.attentivemobile.com/v1/me', { | ||
method: 'GET', | ||
headers: { | ||
Authorization: `Bearer ${settings.apiKey}` | ||
} | ||
}) | ||
} | ||
}, | ||
|
||
extendRequest({ settings }) { | ||
return { | ||
headers: { | ||
Authorization: `Bearer ${settings.apiKey}`, | ||
'Content-Type': 'application/json' | ||
} | ||
} | ||
}, | ||
actions: { | ||
customEvents | ||
}, | ||
presets: [ | ||
{ | ||
name: 'Track Event', | ||
subscribe: 'type = "track"', | ||
partnerAction: 'customEvents', | ||
mapping: defaultValues(customEvents.fields), | ||
type: 'automatic' | ||
} | ||
] | ||
} | ||
|
||
export default destination |