From 726d281fb21ebc0e2b31ba9dea70ef1772804fd7 Mon Sep 17 00:00:00 2001 From: Joe Ayoub <45374896+joe-ayoub-segment@users.noreply.github.com> Date: Tue, 5 Mar 2024 11:23:49 +0000 Subject: [PATCH] Adding new kevel-audience Destination (#1910) * adding kevel-audience Destination * fixing spelling issue --- .../kevel-audience/generated-types.ts | 28 ++++++ .../src/destinations/kevel-audience/index.ts | 67 ++++++++++++++ .../syncKevelAudience/__tests__/index.test.ts | 87 +++++++++++++++++++ .../syncKevelAudience/generated-types.ts | 14 +++ .../kevel-audience/syncKevelAudience/index.ts | 55 ++++++++++++ .../src/destinations/kevel/index.ts | 4 +- .../syncAudience/__tests__/index.test.ts | 2 +- .../destinations/kevel/syncAudience/index.ts | 3 +- .../destinations/kevel/syncTraits/index.ts | 3 +- 9 files changed, 258 insertions(+), 5 deletions(-) create mode 100644 packages/destination-actions/src/destinations/kevel-audience/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/kevel-audience/index.ts create mode 100644 packages/destination-actions/src/destinations/kevel-audience/syncKevelAudience/__tests__/index.test.ts create mode 100644 packages/destination-actions/src/destinations/kevel-audience/syncKevelAudience/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/kevel-audience/syncKevelAudience/index.ts diff --git a/packages/destination-actions/src/destinations/kevel-audience/generated-types.ts b/packages/destination-actions/src/destinations/kevel-audience/generated-types.ts new file mode 100644 index 0000000000..57f3dadabc --- /dev/null +++ b/packages/destination-actions/src/destinations/kevel-audience/generated-types.ts @@ -0,0 +1,28 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Settings { + /** + * Your Kevel Audience root subdomain. For example: "cdp.yourdomain.com". + */ + audienceDomain: string + /** + * Kevel Audience User ID Type to map your Segment User ID to. For example: "crm". + */ + userIdType: string + /** + * The Kevel Audience client ID to identify the event. For example: "brand-name". + */ + clientId: string + /** + * The Kevel Audience site ID to identify the event. For example: "segment-app". + */ + siteId: string + /** + * The Kevel Audience API Key to authorize the requests. Get yours from your Kevel Customer Success representative. + */ + apiKey: string + /** + * The type of event to send to Kevel Audience. For example: "segmentSync". + */ + eventType: string +} diff --git a/packages/destination-actions/src/destinations/kevel-audience/index.ts b/packages/destination-actions/src/destinations/kevel-audience/index.ts new file mode 100644 index 0000000000..7686f91ec3 --- /dev/null +++ b/packages/destination-actions/src/destinations/kevel-audience/index.ts @@ -0,0 +1,67 @@ +import type { DestinationDefinition } from '@segment/actions-core' +import type { Settings } from './generated-types' + +import syncKevelAudience from './syncKevelAudience' + +const destination: DestinationDefinition = { + name: 'Kevel Audience (Actions)', + slug: 'actions-kevel-audience', + description: + 'Sync Segment user profile traits and Engage Audiences to Kevel Audiences. Only users with a Segment userId will be synced.', + mode: 'cloud', + + authentication: { + scheme: 'custom', + fields: { + audienceDomain: { + label: 'Kevel Audience Domain', + description: 'Your Kevel Audience root subdomain. For example: "cdp.yourdomain.com".', + type: 'string', + required: true + }, + userIdType: { + label: 'Kevel Audience User ID Type', + description: 'Kevel Audience User ID Type to map your Segment User ID to. For example: "crm".', + type: 'string', + required: true + }, + clientId: { + label: 'Kevel Audience client ID', + description: 'The Kevel Audience client ID to identify the event. For example: "brand-name".', + type: 'string', + required: true + }, + siteId: { + label: 'Kevel Audience site ID', + description: 'The Kevel Audience site ID to identify the event. For example: "segment-app".', + type: 'string', + required: true + }, + apiKey: { + label: 'Kevel Audience API Key', + description: + 'The Kevel Audience API Key to authorize the requests. Get yours from your Kevel Customer Success representative.', + type: 'string', + required: true + }, + eventType: { + label: 'Event Type', + description: 'The type of event to send to Kevel Audience. For example: "segmentSync".', + type: 'string', + required: true + } + } + }, + extendRequest() { + return { + headers: { + 'Content-Type': 'application/json' + } + } + }, + actions: { + syncKevelAudience + } +} + +export default destination diff --git a/packages/destination-actions/src/destinations/kevel-audience/syncKevelAudience/__tests__/index.test.ts b/packages/destination-actions/src/destinations/kevel-audience/syncKevelAudience/__tests__/index.test.ts new file mode 100644 index 0000000000..d49dc326ae --- /dev/null +++ b/packages/destination-actions/src/destinations/kevel-audience/syncKevelAudience/__tests__/index.test.ts @@ -0,0 +1,87 @@ +import nock from 'nock' +import { createTestEvent, createTestIntegration } from '@segment/actions-core' +import Destination from '../../index' + +const testDestination = createTestIntegration(Destination) + +const goodTrackEvent = createTestEvent({ + type: 'track', + userId: 'uid1', + context: { + personas: { + computation_class: 'audience', + computation_key: 'kevel_segment_test_name' + }, + traits: { + email: 'test@email.com' + } + }, + properties: { + audience_key: 'kevel_segment_test_name', + kevel_segment_test_name: true + } +}) + +const goodIdentifyEvent = createTestEvent({ + type: 'identify', + userId: 'uid1', + context: { + personas: { + computation_class: 'audience', + computation_key: 'kevel_segment_test_name' + } + }, + traits: { + audience_key: 'kevel_segment_test_name', + kevel_segment_test_name: true + }, + properties: undefined +}) + +describe('KevelAuddience.syncKevelAudience', () => { + it('should not throw an error if the audience creation succeed - track', async () => { + const baseUrl = 'https://tr.domain.brand.com/' + + nock(baseUrl) + .post('/events/server', (body) => body.customData.kevel_segment_test_name === true) + .reply(200) + + await expect( + testDestination.testAction('syncKevelAudience', { + event: goodTrackEvent, + settings: { + audienceDomain: 'domain.brand.com', + userIdType: 'email_sha256', + apiKey: 'api_key', + clientId: 'client_id', + siteId: 'site_id', + eventType: 'segmentSync' + }, + useDefaultMappings: true + }) + ).resolves.not.toThrowError() + }) + + it('should not throw an error if the audience creation succeed - identify', async () => { + const baseUrl = 'https://tr.domain.brand.com' + + nock(baseUrl) + .post('/events/server', (body) => body.customData.kevel_segment_test_name === true) + .reply(200) + + await expect( + testDestination.testAction('syncKevelAudience', { + event: goodIdentifyEvent, + settings: { + audienceDomain: 'domain.brand.com', + userIdType: 'email_sha256', + apiKey: 'api_key', + clientId: 'client_id', + siteId: 'site_id', + eventType: 'segmentSync' + }, + useDefaultMappings: true + }) + ).resolves.not.toThrowError() + }) +}) diff --git a/packages/destination-actions/src/destinations/kevel-audience/syncKevelAudience/generated-types.ts b/packages/destination-actions/src/destinations/kevel-audience/syncKevelAudience/generated-types.ts new file mode 100644 index 0000000000..9961472af7 --- /dev/null +++ b/packages/destination-actions/src/destinations/kevel-audience/syncKevelAudience/generated-types.ts @@ -0,0 +1,14 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * The user's unique ID + */ + segment_user_id: string + /** + * A computed object for track and identify events. This field should not need to be edited. + */ + traits_or_props: { + [k: string]: unknown + } +} diff --git a/packages/destination-actions/src/destinations/kevel-audience/syncKevelAudience/index.ts b/packages/destination-actions/src/destinations/kevel-audience/syncKevelAudience/index.ts new file mode 100644 index 0000000000..3354f84f14 --- /dev/null +++ b/packages/destination-actions/src/destinations/kevel-audience/syncKevelAudience/index.ts @@ -0,0 +1,55 @@ +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'Sync Kevel Audience', + description: + 'Sync Segment user profile traits and Engage Audiences to Kevel Audiences. Only users with a Segment userId will be synced.', + defaultSubscription: 'type = "track" or type = "identify"', + fields: { + segment_user_id: { + label: 'User ID', + description: "The user's unique ID", + type: 'string', + unsafe_hidden: true, + required: true, + default: { '@path': '$.userId' } + }, + traits_or_props: { + label: 'Traits or properties object', + description: 'A computed object for track and identify events. This field should not need to be edited.', + type: 'object', + required: true, + unsafe_hidden: true, + default: { + '@if': { + exists: { '@path': '$.properties' }, + then: { '@path': '$.properties' }, + else: { '@path': '$.traits' } + } + } + } + }, + perform: async (request, data) => { + const baseUrl = `https://tr.${data.settings.audienceDomain}/events/server` // TODO event tracker + const payload = { + clientId: data.settings.clientId, + siteId: data.settings.siteId, + type: 'custom', + customType: data.settings.eventType, + user: { + type: data.settings.userIdType, + id: data.payload.segment_user_id + }, + customData: data.payload.traits_or_props + } + + return request(`${baseUrl}`, { + json: payload, + method: 'POST' + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/kevel/index.ts b/packages/destination-actions/src/destinations/kevel/index.ts index fd12a664db..bd9fa22eb4 100644 --- a/packages/destination-actions/src/destinations/kevel/index.ts +++ b/packages/destination-actions/src/destinations/kevel/index.ts @@ -6,10 +6,10 @@ import syncAudience from './syncAudience' import syncTraits from './syncTraits' const destination: DestinationDefinition = { - name: 'Kevel (Actions)', + name: 'Kevel UserDB (Actions)', slug: 'actions-kevel', description: - 'Send Segment user profiles and Segment Audiences to Kevel. Only users with a Segment userId will be synced.', + 'Send Segment user profiles and Audiences to Kevel UserDB for campaign targeting. Only users with a Segment userId will be synced.', mode: 'cloud', authentication: { diff --git a/packages/destination-actions/src/destinations/kevel/syncAudience/__tests__/index.test.ts b/packages/destination-actions/src/destinations/kevel/syncAudience/__tests__/index.test.ts index 0b027ec2b2..16c5d9b032 100644 --- a/packages/destination-actions/src/destinations/kevel/syncAudience/__tests__/index.test.ts +++ b/packages/destination-actions/src/destinations/kevel/syncAudience/__tests__/index.test.ts @@ -76,7 +76,7 @@ describe('Kevel.syncAudience', () => { ).resolves.not.toThrowError() }) - it('should not throw an error if the audience creation succeed - track', async () => { + it('should not throw an error if the audience creation succeed - identify', async () => { const userId = 'uid1' const networkId1 = 'networkId1' const baseUrl = `https://e-${networkId1}.adzerk.net/udb/${networkId1}` diff --git a/packages/destination-actions/src/destinations/kevel/syncAudience/index.ts b/packages/destination-actions/src/destinations/kevel/syncAudience/index.ts index 59bee19d7b..29f3b8ef0b 100644 --- a/packages/destination-actions/src/destinations/kevel/syncAudience/index.ts +++ b/packages/destination-actions/src/destinations/kevel/syncAudience/index.ts @@ -4,7 +4,8 @@ import type { Payload } from './generated-types' const action: ActionDefinition = { title: 'Sync Audience', - description: 'Sync a Segment Engage Audience to a Kevel Segment. Only users with a Segment userId will be synced.', + description: + "Sync a Segment Engage Audience to a Kevel UserDB Interest. Only users with a Segment userId will be synced. See Kevel's [documentation for more details](https://dev.kevel.com/reference/add-interest-to-user).", defaultSubscription: 'type = "track" or type = "identify"', fields: { segment_computation_key: { diff --git a/packages/destination-actions/src/destinations/kevel/syncTraits/index.ts b/packages/destination-actions/src/destinations/kevel/syncTraits/index.ts index 7c51e40c92..157959ae08 100644 --- a/packages/destination-actions/src/destinations/kevel/syncTraits/index.ts +++ b/packages/destination-actions/src/destinations/kevel/syncTraits/index.ts @@ -4,7 +4,8 @@ import type { Payload } from './generated-types' const action: ActionDefinition = { title: 'Sync Traits', - description: 'Sync user profile traits from Segment to Kevel', + description: + "Sync user profile traits and Audiences from Segment to Kevel UserDB as `customProperties`. See Kevel's [documentation for more details](https://dev.kevel.com/reference/set-custom-properties-alternative).", defaultSubscription: 'type = "identify"', fields: { segment_user_id: {