diff --git a/packages/destination-actions/src/destinations/linkedin-audiences/api/index.ts b/packages/destination-actions/src/destinations/linkedin-audiences/api/index.ts index 279025e0b2..f2b3d8cb2b 100644 --- a/packages/destination-actions/src/destinations/linkedin-audiences/api/index.ts +++ b/packages/destination-actions/src/destinations/linkedin-audiences/api/index.ts @@ -3,7 +3,7 @@ import type { RequestClient, ModifiedResponse } from '@segment/actions-core' import type { Settings } from '../generated-types' import type { Payload } from '../updateAudience/generated-types' import { BASE_URL, LINKEDIN_SOURCE_PLATFORM } from '../constants' -import type { ProfileAPIResponse, AdAccountUserResponse } from '../types' +import type { ProfileAPIResponse, AdAccountUserResponse, LinkedInAudiencePayload } from '../types' export class LinkedInAudiences { request: RequestClient @@ -57,7 +57,7 @@ export class LinkedInAudiences { }) } - async batchUpdate(dmpSegmentId: string, elements: Record[]): Promise { + async batchUpdate(dmpSegmentId: string, elements: LinkedInAudiencePayload[]): Promise { return this.request(`${BASE_URL}/dmpSegments/${dmpSegmentId}/users`, { method: 'POST', headers: { diff --git a/packages/destination-actions/src/destinations/linkedin-audiences/index.ts b/packages/destination-actions/src/destinations/linkedin-audiences/index.ts index 6f294e8367..8bcabbb806 100644 --- a/packages/destination-actions/src/destinations/linkedin-audiences/index.ts +++ b/packages/destination-actions/src/destinations/linkedin-audiences/index.ts @@ -1,9 +1,11 @@ -import type { DestinationDefinition } from '@segment/actions-core' -import { InvalidAuthenticationError } from '@segment/actions-core' +import https from 'https' + +import type { DestinationDefinition, ModifiedResponse } from '@segment/actions-core' +import { InvalidAuthenticationError, IntegrationError, ErrorCodes } from '@segment/actions-core' + import type { Settings } from './generated-types' import updateAudience from './updateAudience' import { LINKEDIN_API_VERSION } from './constants' -import https from 'https' import { LinkedInAudiences } from './api' import type { RefreshTokenResponse, @@ -12,8 +14,6 @@ import type { LinkedInRefreshTokenError, LinkedInTestAuthenticationError } from './types' -import type { ModifiedResponse } from '@segment/actions-core' -import { IntegrationError, ErrorCodes } from '@segment/actions-core' const destination: DestinationDefinition = { // We need to match `name` with the creationName in the db. The name used in the UI is "LinkedIn Audiences". diff --git a/packages/destination-actions/src/destinations/linkedin-audiences/types.ts b/packages/destination-actions/src/destinations/linkedin-audiences/types.ts index 84670540bf..9e3b9191a1 100644 --- a/packages/destination-actions/src/destinations/linkedin-audiences/types.ts +++ b/packages/destination-actions/src/destinations/linkedin-audiences/types.ts @@ -15,6 +15,16 @@ export interface AdAccountUserResponse { role: string } +export interface LinkedInAudiencePayload { + action: 'ADD' | 'REMOVE' + userIds: Record[] + firstName?: string + lastName?: string + title?: string + company?: string + country?: string +} + export class LinkedInRefreshTokenError extends HTTPError { response: Response & { data: { diff --git a/packages/destination-actions/src/destinations/linkedin-audiences/updateAudience/__tests__/index.test.ts b/packages/destination-actions/src/destinations/linkedin-audiences/updateAudience/__tests__/index.test.ts index b55c54227b..19875cdb95 100644 --- a/packages/destination-actions/src/destinations/linkedin-audiences/updateAudience/__tests__/index.test.ts +++ b/packages/destination-actions/src/destinations/linkedin-audiences/updateAudience/__tests__/index.test.ts @@ -300,6 +300,59 @@ describe('LinkedinAudiences.updateAudience', () => { '"{\\"elements\\":[{\\"action\\":\\"ADD\\",\\"userIds\\":[{\\"idType\\":\\"SHA256_EMAIL\\",\\"idValue\\":\\"584c4423c421df49955759498a71495aba49b8780eb9387dff333b6f0982c777\\"},{\\"idType\\":\\"GOOGLE_AID\\",\\"idValue\\":\\"123\\"}]}]}"' ) }) + + it('Full payload', async () => { + const eventWithTraits = createTestEvent({ + event: 'Audience Entered', + type: 'track', + properties: { + audience_key: 'personas_test_audience' + }, + traits: { + email: 'testing@testing.com', + firstName: 'John', + lastName: 'Doe', + title: 'CEO', + company: 'Acme', + country: 'US' + }, + context: { + device: { + advertisingId: '123' + } + } + }) + + nock(`${BASE_URL}/dmpSegments`) + .get(/.*/) + .query(() => true) + .reply(200, { elements: [{ id: 'dmp_segment_id' }] }) + + nock(`${BASE_URL}/dmpSegments/dmp_segment_id/users`) + .post(/.*/, (body) => body.elements[0].action === 'ADD') + .reply(200) + + const responses = await testDestination.testAction('updateAudience', { + event: eventWithTraits, + settings: { + ad_account_id: '123', + send_email: true, + send_google_advertising_id: true + }, + useDefaultMappings: true, + auth, + mapping: { + personas_audience_key: 'personas_test_audience', + dmp_user_action: 'ADD' + } + }) + + expect(responses).toBeTruthy() + expect(responses).toHaveLength(2) + expect(responses[1].options.body).toMatchInlineSnapshot( + '"{\\"elements\\":[{\\"action\\":\\"ADD\\",\\"userIds\\":[{\\"idType\\":\\"SHA256_EMAIL\\",\\"idValue\\":\\"584c4423c421df49955759498a71495aba49b8780eb9387dff333b6f0982c777\\"},{\\"idType\\":\\"GOOGLE_AID\\",\\"idValue\\":\\"123\\"}],\\"firstName\\":\\"John\\",\\"lastName\\":\\"Doe\\",\\"title\\":\\"CEO\\",\\"company\\":\\"Acme\\",\\"country\\":\\"US\\"}]}"' + ) + }) }) describe('Error cases', () => { diff --git a/packages/destination-actions/src/destinations/linkedin-audiences/updateAudience/generated-types.ts b/packages/destination-actions/src/destinations/linkedin-audiences/updateAudience/generated-types.ts index e18b24ce8f..fee3e748e4 100644 --- a/packages/destination-actions/src/destinations/linkedin-audiences/updateAudience/generated-types.ts +++ b/packages/destination-actions/src/destinations/linkedin-audiences/updateAudience/generated-types.ts @@ -13,6 +13,26 @@ export interface Payload { * The user's email address to send to LinkedIn. */ email?: string + /** + * The user's first name to send to LinkedIn. + */ + first_name?: string + /** + * The user's last name to send to LinkedIn. + */ + last_name?: string + /** + * The user's title to send to LinkedIn. + */ + title?: string + /** + * The user's company to send to LinkedIn. + */ + company?: string + /** + * The user's country to send to LinkedIn. This field accepts an ISO standardized two letter country code e.g. US. + */ + country?: string /** * The user's Google Advertising ID to send to LinkedIn. */ diff --git a/packages/destination-actions/src/destinations/linkedin-audiences/updateAudience/index.ts b/packages/destination-actions/src/destinations/linkedin-audiences/updateAudience/index.ts index c3d785961c..19ae1f31fa 100644 --- a/packages/destination-actions/src/destinations/linkedin-audiences/updateAudience/index.ts +++ b/packages/destination-actions/src/destinations/linkedin-audiences/updateAudience/index.ts @@ -3,6 +3,7 @@ import { RequestClient, RetryableError, IntegrationError, sha256SmartHash } from import type { Settings } from '../generated-types' import type { Payload } from './generated-types' import { LinkedInAudiences } from '../api' +import { LinkedInAudiencePayload } from '../types' const action: ActionDefinition = { title: 'Sync To LinkedIn DMP Segment', @@ -36,6 +37,47 @@ const action: ActionDefinition = { } } }, + first_name: { + label: 'User First Name', + description: "The user's first name to send to LinkedIn.", + type: 'string', + default: { + '@path': '$.traits.firstName' + } + }, + last_name: { + label: 'User Last Name', + description: "The user's last name to send to LinkedIn.", + type: 'string', + default: { + '@path': '$.traits.lastName' + } + }, + title: { + label: 'User Title', + description: "The user's title to send to LinkedIn.", + type: 'string', + default: { + '@path': '$.traits.title' + } + }, + company: { + label: 'User Company', + description: "The user's company to send to LinkedIn.", + type: 'string', + default: { + '@path': '$.traits.company' + } + }, + country: { + label: 'User Country', + description: + "The user's country to send to LinkedIn. This field accepts an ISO standardized two letter country code e.g. US.", + type: 'string', + default: { + '@path': '$.traits.country' + } + }, google_advertising_id: { label: 'User Google Advertising ID', description: "The user's Google Advertising ID to send to LinkedIn.", @@ -180,24 +222,46 @@ async function createDmpSegment( return headers['x-linkedin-id'] } -function extractUsers(settings: Settings, payloads: Payload[]) { - const elements: Record[] = [] +function extractUsers(settings: Settings, payloads: Payload[]): LinkedInAudiencePayload[] { + const elements: LinkedInAudiencePayload[] = [] payloads.forEach((payload: Payload) => { if (!payload.email && !payload.google_advertising_id) { return } - elements.push({ + const linkedinAudiencePayload: LinkedInAudiencePayload = { action: getAction(payload), userIds: getUserIds(settings, payload) - }) + } + + if (payload.first_name) { + linkedinAudiencePayload.firstName = payload.first_name + } + + if (payload.last_name) { + linkedinAudiencePayload.lastName = payload.last_name + } + + if (payload.title) { + linkedinAudiencePayload.title = payload.title + } + + if (payload.company) { + linkedinAudiencePayload.company = payload.company + } + + if (payload.country) { + linkedinAudiencePayload.country = payload.country + } + + elements.push(linkedinAudiencePayload) }) return elements } -function getAction(payload: Payload) { +function getAction(payload: Payload): 'ADD' | 'REMOVE' { const { dmp_user_action = 'AUTO' } = payload if (dmp_user_action === 'ADD') { @@ -217,26 +281,28 @@ function getAction(payload: Payload) { return 'REMOVE' } } + + return 'ADD' } function getUserIds(settings: Settings, payload: Payload): Record[] { - const users = [] + const userIds = [] if (payload.email && settings.send_email === true) { - users.push({ + userIds.push({ idType: 'SHA256_EMAIL', idValue: sha256SmartHash(payload.email) }) } if (payload.google_advertising_id && settings.send_google_advertising_id === true) { - users.push({ + userIds.push({ idType: 'GOOGLE_AID', idValue: payload.google_advertising_id }) } - return users + return userIds } export default action