From 74ec16cce36bdc8e02adb1c49f3df0804bdc72f7 Mon Sep 17 00:00:00 2001 From: Harsh Joshi Date: Wed, 5 Jun 2024 16:10:53 +0530 Subject: [PATCH] Squashed commit of the following: commit c37259c317a45499ce9676fa2620e36c6ebed541 Author: Harsh Joshi Date: Wed Jun 5 16:05:46 2024 +0530 Update test cases commit 6ab6d577c4a4cac760a42dbc00356607338662e5 Author: Harsh Joshi Date: Tue Jun 4 15:15:20 2024 +0530 Add profile properties in Add profilr to ost event commit 524fc6cbfd6e3b818b9628e75f81e14a40932cc9 Author: Varadarajan V <109586712+varadarajan-tw@users.noreply.github.com> Date: Wed May 29 17:41:51 2024 +0530 Remove build-experience-team from pr labeler as internal team (#2060) commit 72adc6414e46847b26a1bc78f9f1024388b9bc86 Author: Joe Ayoub Date: Tue May 28 11:04:54 2024 +0100 Publish - @segment/actions-shared@1.93.0 - @segment/browser-destination-runtime@1.41.0 - @segment/actions-core@3.111.0 - @segment/action-destinations@3.269.0 - @segment/destinations-manifest@1.59.0 - @segment/analytics-browser-actions-1flow@1.24.0 - @segment/analytics-browser-actions-adobe-target@1.42.0 - @segment/analytics-browser-actions-algolia-plugins@1.19.0 - @segment/analytics-browser-actions-amplitude-plugins@1.42.0 - @segment/analytics-browser-actions-braze-cloud-plugins@1.45.0 - @segment/analytics-browser-actions-braze@1.45.0 - @segment/analytics-browser-actions-bucket@1.22.0 - @segment/analytics-browser-actions-cdpresolution@1.29.0 - @segment/analytics-browser-actions-commandbar@1.42.0 - @segment/analytics-browser-actions-devrev@1.29.0 - @segment/analytics-browser-actions-friendbuy@1.43.0 - @segment/analytics-browser-actions-fullstory@1.44.0 - @segment/analytics-browser-actions-google-analytics-4@1.48.0 - @segment/analytics-browser-actions-google-campaign-manager@1.32.0 - @segment/analytics-browser-actions-heap@1.42.0 - @segment/analytics-browser-hubble-web@1.28.0 - @segment/analytics-browser-actions-hubspot@1.42.0 - @segment/analytics-browser-actions-intercom@1.45.0 - @segment/analytics-browser-actions-iterate@1.42.0 - @segment/analytics-browser-actions-jimo@1.30.0 - @segment/analytics-browser-actions-koala@1.43.0 - @segment/analytics-browser-actions-logrocket@1.42.0 - @segment/analytics-browser-actions-pendo-web-actions@1.31.0 - @segment/analytics-browser-actions-playerzero@1.42.0 - @segment/analytics-browser-actions-replaybird@1.23.0 - @segment/analytics-browser-actions-ripe@1.42.0 - @segment/analytics-browser-actions-rupt@1.31.0 - @segment/analytics-browser-actions-screeb@1.43.0 - @segment/analytics-browser-actions-utils@1.42.0 - @segment/analytics-browser-actions-snap-plugins@1.23.0 - @segment/analytics-browser-actions-sprig@1.42.0 - @segment/analytics-browser-actions-stackadapt@1.42.0 - @segment/analytics-browser-actions-survicate@1.18.0 - @segment/analytics-browser-actions-tiktok-pixel@1.41.0 - @segment/analytics-browser-actions-upollo@1.42.0 - @segment/analytics-browser-actions-userpilot@1.42.0 - @segment/analytics-browser-actions-vwo@1.43.0 - @segment/analytics-browser-actions-wiseops@1.43.0 commit e4bb620f41f431fe55dff6deaf9f42990a468c6f Author: Prayansh Srivastava Date: Tue May 28 02:25:29 2024 -0700 add new excludeWhenNull directive and update tests (#2040) * add new excludeWhenNull directive and update tests * add readme docs * fix replace test case commit e7b424a9ddb1161a0d01fc54eee34845ca802d39 Author: harsh-joshi99 <129737395+harsh-joshi99@users.noreply.github.com> Date: Tue May 28 14:53:39 2024 +0530 Add override_list_id mapping in upsert Profile (#2046) * Add override_list_id mapping in upsert Profile, add function to group profiles based on list ID * fix payload * Fix payload condition * Refactor profile filter commit 6170334cd9a94e260ceb93bb21bd1caeeb8fe253 Author: Joe Ayoub Date: Tue May 28 10:18:32 2024 +0100 updating field description and updating generated types commit 1107970ce143d137381c5a377dd117dbc2dbe541 Author: Alice Mackel Date: Tue May 28 05:07:52 2024 -0400 Change StackAdapt destination name (#2055) commit 76c733da6650f582a0076b8a92f4294c0766786b Author: Leonel Sanches <113376080+seg-leonelsanches@users.noreply.github.com> Date: Tue May 28 02:07:21 2024 -0700 Preventing double-hashing for TikTok Offline Conversions. (#2034) commit a62117916ce8b26555962ab0d98a5610e5eb4312 Author: Nuno Sousa Date: Tue May 28 10:07:09 2024 +0100 Fix customer.io timestamp issue (#2054) commit 1c719875d9b5845df184be2453f1a0e4b837a7a1 Author: Leonel Sanches <113376080+seg-leonelsanches@users.noreply.github.com> Date: Tue May 28 02:06:52 2024 -0700 Preventing double-hashing for TikTok Conversions. (#2033) commit 4b27451874627c3e903a74b4e3e621591b413744 Author: Nick Campbell <661795+nickcampbell18@users.noreply.github.com> Date: Tue May 28 10:06:28 2024 +0100 Implement batch support for Attio Actions (#2057) * Implement batch APIs for Attio * Make received_at parameter optional and gracefully handle it * Make "enable_batching" parameter viewable, optional, default false There are tradeoffs associated with Segment event batching - there is no realtime feedback if the request was invalid, for example - so we should make it opt-in and controllable by the user. commit 0fd6c222271ef55d83d0a76bcd9de8377633e09b Author: Innovative-GauravKochar <117165746+Innovative-GauravKochar@users.noreply.github.com> Date: Tue May 28 14:33:36 2024 +0530 [STRATCONN-3468] | Introduce 'Amazon AMC' Audience Destination (#2036) * Updated the code for amazon ads syncaudience action * Fix the build failing * Updated the getAudience and createAudience * Code to update the value to hashed * Added the advertiserId field in audienceSetting * Added check for advertiserid * Fixing External Id issue , replace with quoted string * Resolve Conflicts * Resolve Conflicts * Resolve Conflicts * Worked on unit test case for createAUdience and syncAudience * Code cleaning * removed stats * Updated Snapshots * added headers in refresh Access token * Fix refresh access token API * Worked on handling cpmCents and ttl * Fixed Pr Comments * Rename destination slug nad action name * removed amazon-ads as we are changing action destination slug * Validate Schema in createAudience * registered amazon-amc and updated destination id * will handle this in seperate PR --------- Co-authored-by: Harsh Vardhan Co-authored-by: Gaurav Kochar commit 2fc41f9f468004716797dc1412b84858ede001ba Author: Varadarajan V <109586712+varadarajan-tw@users.noreply.github.com> Date: Fri May 24 11:49:25 2024 +0530 [snyk] bump jscodeshift from 0.13.0 to 0.14.0 (#2056) commit e7909493211cda15bb87f5bea4184936573f0bdb Author: alfrimpong <119889384+alfrimpong@users.noreply.github.com> Date: Wed May 22 00:59:22 2024 -0500 feat: add script for action-cli shared (#2050) --- .../addProfileToList/__tests__/index.test.ts | 101 +++++++++++++++++- .../addProfileToList/generated-types.ts | 39 +++++++ .../klaviyo/addProfileToList/index.ts | 28 ++++- .../src/destinations/klaviyo/functions.ts | 8 +- .../src/destinations/klaviyo/properties.ts | 97 +++++++++++++++++ yarn.lock | 31 +++++- 6 files changed, 293 insertions(+), 11 deletions(-) diff --git a/packages/destination-actions/src/destinations/klaviyo/addProfileToList/__tests__/index.test.ts b/packages/destination-actions/src/destinations/klaviyo/addProfileToList/__tests__/index.test.ts index f1bbb46487..9caee67042 100644 --- a/packages/destination-actions/src/destinations/klaviyo/addProfileToList/__tests__/index.test.ts +++ b/packages/destination-actions/src/destinations/klaviyo/addProfileToList/__tests__/index.test.ts @@ -64,6 +64,16 @@ const importJobPayload = { } } +const profileProperties = { + first_name: 'John', + last_name: 'Doe', + image: 'http://example.com/image.jpg', + title: 'Developer', + organization: 'Segment', + location: { city: 'San Francisco' }, + properties: { key: 'value' } +} + describe('Add List To Profile', () => { it('should throw error if no email or External Id is provided', async () => { const event = createTestEvent({ @@ -189,6 +199,55 @@ describe('Add List To Profile', () => { testDestination.testAction('addProfileToList', { event, mapping, settings }) ).resolves.not.toThrowError() }) + + it('should add profile to list if successful with email, external id and profile properties', async () => { + nock(`${API_URL}`) + .post('/profiles/', { + data: { + type: 'profile', + attributes: { email: 'demo@segment.com', external_id: 'testing_123', ...profileProperties } + } + }) + .reply(200, { + data: { + id: 'XYZABC' + } + }) + + nock(`${API_URL}/lists/${listId}`) + .post('/relationships/profiles/', requestBody) + .reply( + 200, + JSON.stringify({ + content: requestBody + }) + ) + + const event = createTestEvent({ + type: 'track', + userId: '123', + properties: { + external_id: 'testing_123' + }, + context: { + traits: { + email: 'demo@segment.com', + ...profileProperties + } + } + }) + const mapping = { + list_id: listId, + email: 'demo@segment.com', + external_id: 'testing_123', + ...profileProperties + } + + await expect( + testDestination.testAction('addProfileToList', { event, mapping, settings }) + ).resolves.not.toThrowError() + }) + it('should add to list if profile is already created', async () => { nock(`${API_URL}`) .post('/profiles/', profileData) @@ -272,7 +331,9 @@ describe('Add Profile To List Batch', () => { batch_size: 10000, list_id: listId, email: 'valid@example.com', - enable_batching: true + enable_batching: true, + location: {}, + properties: {} } ], listId @@ -314,6 +375,44 @@ describe('Add Profile To List Batch', () => { ) }) + it('should send an import job request with the generated payload and profile properties', async () => { + const events = [ + createTestEvent({ + context: { personas: { list_id: listId }, traits: { email: 'valid@example.com' } } + }) + ] + const mapping = { + list_id: listId, + external_id: 'fake-external-id', + email: { + '@path': '$.context.traits.email' + }, + ...profileProperties + } + + nock(API_URL).post('/profile-bulk-import-jobs/', importJobPayload).reply(200, { success: true }) + + await testDestination.testBatchAction('addProfileToList', { + events, + settings, + mapping, + useDefaultMappings: true + }) + expect(Functions.createImportJobPayload).toBeCalledWith( + [ + { + batch_size: 10000, + email: 'valid@example.com', + enable_batching: true, + external_id: 'fake-external-id', + list_id: listId, + ...profileProperties + } + ], + listId + ) + }) + it('should send an import job request with the generated payload', async () => { const events = [ createTestEvent({ diff --git a/packages/destination-actions/src/destinations/klaviyo/addProfileToList/generated-types.ts b/packages/destination-actions/src/destinations/klaviyo/addProfileToList/generated-types.ts index e6149fb403..c9a8272f43 100644 --- a/packages/destination-actions/src/destinations/klaviyo/addProfileToList/generated-types.ts +++ b/packages/destination-actions/src/destinations/klaviyo/addProfileToList/generated-types.ts @@ -21,4 +21,43 @@ export interface Payload { * Maximum number of events to include in each batch. Actual batch sizes may be lower. */ batch_size?: number + /** + * Individual's first name. + */ + first_name?: string + /** + * Individual's last name. + */ + last_name?: string + /** + * URL pointing to the location of a profile image. + */ + image?: string + /** + * Individual's job title. + */ + title?: string + /** + * Name of the company or organization within the company for whom the individual works. + */ + organization?: string + /** + * Individual's address. + */ + location?: { + address1?: string | null + address2?: string | null + city?: string | null + region?: string | null + zip?: string | null + latitude?: string | null + longitude?: string | null + country?: string | null + } + /** + * An object containing key/value pairs for any custom properties assigned to this profile. + */ + properties?: { + [k: string]: unknown + } } diff --git a/packages/destination-actions/src/destinations/klaviyo/addProfileToList/index.ts b/packages/destination-actions/src/destinations/klaviyo/addProfileToList/index.ts index acfcdc0702..1da1146fec 100644 --- a/packages/destination-actions/src/destinations/klaviyo/addProfileToList/index.ts +++ b/packages/destination-actions/src/destinations/klaviyo/addProfileToList/index.ts @@ -2,7 +2,20 @@ import { ActionDefinition, PayloadValidationError } from '@segment/actions-core' import type { Settings } from '../generated-types' import { Payload } from './generated-types' import { createProfile, addProfileToList, createImportJobPayload, sendImportJobRequest } from '../functions' -import { email, external_id, list_id, enable_batching, batch_size } from '../properties' +import { + email, + external_id, + list_id, + enable_batching, + batch_size, + first_name, + last_name, + organization, + title, + image, + location, + properties +} from '../properties' const action: ActionDefinition = { title: 'Add Profile to List (Engage)', @@ -13,14 +26,21 @@ const action: ActionDefinition = { list_id: { ...list_id }, external_id: { ...external_id }, enable_batching: { ...enable_batching }, - batch_size: { ...batch_size } + batch_size: { ...batch_size }, + first_name: { ...first_name }, + last_name: { ...last_name }, + image: { ...image }, + title: { ...title }, + organization: { ...organization }, + location: { ...location }, + properties: { ...properties } }, perform: async (request, { payload }) => { - const { email, list_id, external_id } = payload + const { email, list_id, external_id, enable_batching, batch_size, ...additionalAttributes } = payload if (!email && !external_id) { throw new PayloadValidationError('One of Email or External Id is required') } - const profileId = await createProfile(request, email, external_id) + const profileId = await createProfile(request, email, external_id, additionalAttributes) return await addProfileToList(request, profileId, list_id) }, performBatch: async (request, { payload }) => { diff --git a/packages/destination-actions/src/destinations/klaviyo/functions.ts b/packages/destination-actions/src/destinations/klaviyo/functions.ts index d50963d1d0..edb0cf6e89 100644 --- a/packages/destination-actions/src/destinations/klaviyo/functions.ts +++ b/packages/destination-actions/src/destinations/klaviyo/functions.ts @@ -71,7 +71,8 @@ export async function removeProfileFromList(request: RequestClient, ids: string[ export async function createProfile( request: RequestClient, email: string | undefined, - external_id: string | undefined + external_id: string | undefined, + additionalAttributes: Record ) { try { const profileData: ProfileData = { @@ -79,7 +80,8 @@ export async function createProfile( type: 'profile', attributes: { email, - external_id + external_id, + ...additionalAttributes } } } @@ -92,7 +94,7 @@ export async function createProfile( return rs.data.id } catch (error) { const { response } = error as KlaviyoAPIError - if (response.status == 409) { + if (response?.status == 409) { const rs = await response.json() return rs.errors[0].meta.duplicate_profile_id } diff --git a/packages/destination-actions/src/destinations/klaviyo/properties.ts b/packages/destination-actions/src/destinations/klaviyo/properties.ts index 86367f4e5e..913c28f477 100644 --- a/packages/destination-actions/src/destinations/klaviyo/properties.ts +++ b/packages/destination-actions/src/destinations/klaviyo/properties.ts @@ -42,3 +42,100 @@ export const batch_size: InputField = { unsafe_hidden: true, default: 10000 } + +export const first_name: InputField = { + label: 'First Name', + description: `Individual's first name.`, + type: 'string', + default: { '@path': '$.context.traits.firstName' } +} + +export const last_name: InputField = { + label: 'Last Name', + description: `Individual's last name.`, + type: 'string', + default: { '@path': '$.context.traits.lastName' } +} + +export const organization: InputField = { + label: 'Organization', + description: `Name of the company or organization within the company for whom the individual works.`, + type: 'string', + default: { '@path': '$.context.traits.company.name' } +} + +export const title: InputField = { + label: 'Title', + description: `Individual's job title.`, + type: 'string', + default: { '@path': '$.context.traits.title' } +} + +export const image: InputField = { + label: 'Image', + description: `URL pointing to the location of a profile image.`, + type: 'string', + default: { '@path': '$.context.traits.avatar' } +} + +export const location: InputField = { + label: 'Location', + description: `Individual's address.`, + type: 'object', + properties: { + address1: { + label: 'Address 1', + type: 'string', + allowNull: true + }, + address2: { + label: 'Address 2', + type: 'string', + allowNull: true + }, + city: { + label: 'City', + type: 'string', + allowNull: true + }, + region: { + label: 'Region', + type: 'string', + allowNull: true + }, + zip: { + label: 'ZIP', + type: 'string', + allowNull: true + }, + latitude: { + label: 'Latitude', + type: 'string', + allowNull: true + }, + longitude: { + label: 'Longitide', + type: 'string', + allowNull: true + }, + country: { + label: 'Country', + type: 'string', + allowNull: true + } + }, + default: { + city: { '@path': '$.context.traits.address.city' }, + region: { '@path': '$.context.traits.address.state' }, + zip: { '@path': '$.context.traits.address.postal_code' }, + address1: { '@path': '$.context.traits.address.street' }, + country: { '@path': '$.context.traits.address.country' } + } +} + +export const properties: InputField = { + description: 'An object containing key/value pairs for any custom properties assigned to this profile.', + label: 'Properties', + type: 'object', + default: { '@path': '$.context.properties' } +} diff --git a/yarn.lock b/yarn.lock index 2ec137e4f5..6bc5529398 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15019,7 +15019,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -15077,7 +15086,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -15105,6 +15114,13 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -16589,7 +16605,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -16616,6 +16632,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"