From 18fef69e4d62e39d18863566ef5968c07773f20e Mon Sep 17 00:00:00 2001 From: Austin Hsueh Date: Wed, 26 Mar 2025 09:39:56 -0700 Subject: [PATCH 01/78] add phone number support to reddit-conversions-api --- .../__snapshots__/snapshot.test.ts.snap | 2 ++ .../__tests__/index.test.ts | 24 +++++++++++++------ .../__snapshots__/snapshot.test.ts.snap | 1 + .../customEvent/generated-types.ts | 4 ++++ .../reddit-conversions-api/fields.ts | 12 ++++++++++ .../__snapshots__/snapshot.test.ts.snap | 1 + .../standardEvent/generated-types.ts | 4 ++++ .../reddit-conversions-api/types.ts | 1 + .../reddit-conversions-api/utils.ts | 3 ++- 9 files changed, 44 insertions(+), 8 deletions(-) diff --git a/packages/destination-actions/src/destinations/reddit-conversions-api/__tests__/__snapshots__/snapshot.test.ts.snap b/packages/destination-actions/src/destinations/reddit-conversions-api/__tests__/__snapshots__/snapshot.test.ts.snap index fa60a344cb..491ddb7c4a 100644 --- a/packages/destination-actions/src/destinations/reddit-conversions-api/__tests__/__snapshots__/snapshot.test.ts.snap +++ b/packages/destination-actions/src/destinations/reddit-conversions-api/__tests__/__snapshots__/snapshot.test.ts.snap @@ -35,6 +35,7 @@ Object { "email": "8614950525b98787b31febe07634987a801175f098b60c83bd3903bff1ec3eed", "external_id": "4fbe858b31ee34144cc3d009fd4636ef20fb342e095570d49a9898bb8d77598e", "ip_address": "4fbe858b31ee34144cc3d009fd4636ef20fb342e095570d49a9898bb8d77598e", + "phone_number": "+5365418331", "screen_dimensions": Object { "height": -2840640572358656, "width": -2840640572358656, @@ -103,6 +104,7 @@ Object { "email": "6ad9c50c3cf54b266add9d94147959c9b024ebf821a18f6e860a01ad33a7b5cf", "external_id": "f136d03523cc17ecdfe2a4df68a819e5e98f6981316b7e50e1a8b34b018c70ae", "ip_address": "f136d03523cc17ecdfe2a4df68a819e5e98f6981316b7e50e1a8b34b018c70ae", + "phone_number": "+5112818613", "screen_dimensions": Object { "height": 2091000289820672, "width": 2091000289820672, diff --git a/packages/destination-actions/src/destinations/reddit-conversions-api/__tests__/index.test.ts b/packages/destination-actions/src/destinations/reddit-conversions-api/__tests__/index.test.ts index 8f0519ee75..46402c6bd6 100644 --- a/packages/destination-actions/src/destinations/reddit-conversions-api/__tests__/index.test.ts +++ b/packages/destination-actions/src/destinations/reddit-conversions-api/__tests__/index.test.ts @@ -31,7 +31,8 @@ describe('Reddit Conversions Api', () => { { product_id: 'product_id_1', category: 'category_1', name: 'name_1' }, { product_id: 'product_id_2', category: 'category_2', name: 'name_2' } ], - email: 'test@test.com' + email: 'test@test.com', + phone: '1-650-555 - 1212 x450' }, context: { userAgent: 'test-user-agent', @@ -86,7 +87,8 @@ describe('Reddit Conversions Api', () => { external_id: '3482ae91c8ec52c06e19d618d400b3985814bf705e00947a302ec849a6575c4c', ip_address: '5feaf188de296cd3b17f7c66fd3a2aec9b694815f2b1180631f7b52f57029777', user_agent: 'test-user-agent', - uuid: 'uuid_1' + uuid: 'uuid_1', + phone_number: '1-650-555 - 1212 x450' } } ], @@ -113,7 +115,8 @@ describe('Reddit Conversions Api', () => { { product_id: 'product_id_1', category: 'category_1', name: 'name_1' }, { product_id: 'product_id_2', category: 'category_2', name: 'name_2' } ], - email: '388c735eec8225c4ad7a507944dd0a975296baea383198aa87177f29af2c6f69' + email: '388c735eec8225c4ad7a507944dd0a975296baea383198aa87177f29af2c6f69', + phone: 'e323ec626319ca94ee8bff2e4c87cf613be6ea19919ed1364124e16807ab3176' }, context: { userAgent: 'test-user-agent', @@ -169,7 +172,8 @@ describe('Reddit Conversions Api', () => { external_id: '3482ae91c8ec52c06e19d618d400b3985814bf705e00947a302ec849a6575c4c', ip_address: '5feaf188de296cd3b17f7c66fd3a2aec9b694815f2b1180631f7b52f57029777', user_agent: 'test-user-agent', - uuid: 'uuid_1' + uuid: 'uuid_1', + phone_number: 'e323ec626319ca94ee8bff2e4c87cf613be6ea19919ed1364124e16807ab3176' } } ], @@ -198,7 +202,8 @@ describe('Reddit Conversions Api', () => { { product_id: 'product_id_1', category: 'category_1', name: 'name_1' }, { product_id: 'product_id_2', category: 'category_2', name: 'name_2' } ], - email: 'test@test.com' + email: 'test@test.com', + phone: '1-650-555 - 1212 x450' }, context: { userAgent: 'test-user-agent', @@ -252,7 +257,8 @@ describe('Reddit Conversions Api', () => { external_id: '3482ae91c8ec52c06e19d618d400b3985814bf705e00947a302ec849a6575c4c', ip_address: '5feaf188de296cd3b17f7c66fd3a2aec9b694815f2b1180631f7b52f57029777', user_agent: 'test-user-agent', - uuid: 'uuid_1' + uuid: 'uuid_1', + phone_number: '1-650-555 - 1212 x450' } } ], @@ -285,6 +291,9 @@ describe('Reddit Conversions Api', () => { ip: '111.111.111.111', device: { advertisingId: 'advertising_id_1' + }, + traits: { + phone: '+1 (650)555-1212' } } }) @@ -331,7 +340,8 @@ describe('Reddit Conversions Api', () => { external_id: '3482ae91c8ec52c06e19d618d400b3985814bf705e00947a302ec849a6575c4c', ip_address: '5feaf188de296cd3b17f7c66fd3a2aec9b694815f2b1180631f7b52f57029777', user_agent: 'test-user-agent', - uuid: 'uuid_1' + uuid: 'uuid_1', + phone_number: '+1 (650)555-1212' } } ], diff --git a/packages/destination-actions/src/destinations/reddit-conversions-api/customEvent/__tests__/__snapshots__/snapshot.test.ts.snap b/packages/destination-actions/src/destinations/reddit-conversions-api/customEvent/__tests__/__snapshots__/snapshot.test.ts.snap index 5101a4ecca..67f6904f63 100644 --- a/packages/destination-actions/src/destinations/reddit-conversions-api/customEvent/__tests__/__snapshots__/snapshot.test.ts.snap +++ b/packages/destination-actions/src/destinations/reddit-conversions-api/customEvent/__tests__/__snapshots__/snapshot.test.ts.snap @@ -35,6 +35,7 @@ Object { "email": "4a032ff520e2ddbb4f3467c73e41b1f94bda5faf9c9080ac7f0d0e83156d9c99", "external_id": "7b1d43d3d43059efd3e755045cf79aa9a554bc456f4c10d1915ccb5d79345b4a", "ip_address": "7b1d43d3d43059efd3e755045cf79aa9a554bc456f4c10d1915ccb5d79345b4a", + "phone_number": "+9658205035", "screen_dimensions": Object { "height": 8361377294974976, "width": 8361377294974976, diff --git a/packages/destination-actions/src/destinations/reddit-conversions-api/customEvent/generated-types.ts b/packages/destination-actions/src/destinations/reddit-conversions-api/customEvent/generated-types.ts index d3a2c7809e..fa53e2da0d 100644 --- a/packages/destination-actions/src/destinations/reddit-conversions-api/customEvent/generated-types.ts +++ b/packages/destination-actions/src/destinations/reddit-conversions-api/customEvent/generated-types.ts @@ -62,6 +62,10 @@ export interface Payload { * The value from the first-party Pixel '_rdt_uuid' cookie on your domain. Note that it is in the '{timestamp}.{uuid}' format. You may use the full value or just the UUID portion. */ uuid?: string + /** + * The phone number of the user in E.164 standard format. + */ + phone_number?: string } /** * A structure of data processing options to specify the processing type for the event. diff --git a/packages/destination-actions/src/destinations/reddit-conversions-api/fields.ts b/packages/destination-actions/src/destinations/reddit-conversions-api/fields.ts index dbcff4969a..04be26b33b 100644 --- a/packages/destination-actions/src/destinations/reddit-conversions-api/fields.ts +++ b/packages/destination-actions/src/destinations/reddit-conversions-api/fields.ts @@ -182,6 +182,11 @@ export const user: InputField = { description: "The value from the first-party Pixel '_rdt_uuid' cookie on your domain. Note that it is in the '{timestamp}.{uuid}' format. You may use the full value or just the UUID portion.", type: 'string' + }, + phone_number: { + label: 'Phone Number', + description: 'The phone number of the user in E.164 standard format.', + type: 'string' } }, default: { @@ -209,6 +214,13 @@ export const user: InputField = { then: { '@path': '$.integrations.Reddit Conversions Api.uuid' }, else: { '@path': '$.properties.uuid' } } + }, + phone_number: { + '@if': { + exists: { '@path': '$.properties.phone' }, + then: { '@path': '$.properties.phone' }, + else: { '@path': '$.context.traits.phone' } + } } } } diff --git a/packages/destination-actions/src/destinations/reddit-conversions-api/standardEvent/__tests__/__snapshots__/snapshot.test.ts.snap b/packages/destination-actions/src/destinations/reddit-conversions-api/standardEvent/__tests__/__snapshots__/snapshot.test.ts.snap index 0f4f42cba5..e10be3b739 100644 --- a/packages/destination-actions/src/destinations/reddit-conversions-api/standardEvent/__tests__/__snapshots__/snapshot.test.ts.snap +++ b/packages/destination-actions/src/destinations/reddit-conversions-api/standardEvent/__tests__/__snapshots__/snapshot.test.ts.snap @@ -34,6 +34,7 @@ Object { "email": "b6887231da112cb08515e502cb0eb06a342f2efb3372c2841368397664844f4e", "external_id": "7da4d4522d67f6ca1c1b0654c2d91c916a22667a5d34fa75c7cdcbe0c1b452ce", "ip_address": "7da4d4522d67f6ca1c1b0654c2d91c916a22667a5d34fa75c7cdcbe0c1b452ce", + "phone_number": "+6504479800", "screen_dimensions": Object { "height": 8501184792887296, "width": 8501184792887296, diff --git a/packages/destination-actions/src/destinations/reddit-conversions-api/standardEvent/generated-types.ts b/packages/destination-actions/src/destinations/reddit-conversions-api/standardEvent/generated-types.ts index 375e8ffe80..8f8b1b86d4 100644 --- a/packages/destination-actions/src/destinations/reddit-conversions-api/standardEvent/generated-types.ts +++ b/packages/destination-actions/src/destinations/reddit-conversions-api/standardEvent/generated-types.ts @@ -62,6 +62,10 @@ export interface Payload { * The value from the first-party Pixel '_rdt_uuid' cookie on your domain. Note that it is in the '{timestamp}.{uuid}' format. You may use the full value or just the UUID portion. */ uuid?: string + /** + * The phone number of the user in E.164 standard format. + */ + phone_number?: string } /** * A structure of data processing options to specify the processing type for the event. diff --git a/packages/destination-actions/src/destinations/reddit-conversions-api/types.ts b/packages/destination-actions/src/destinations/reddit-conversions-api/types.ts index f7588d00f2..e9a6ca9f5f 100644 --- a/packages/destination-actions/src/destinations/reddit-conversions-api/types.ts +++ b/packages/destination-actions/src/destinations/reddit-conversions-api/types.ts @@ -42,6 +42,7 @@ export interface User { height?: number width?: number } + phone_number?: string } export interface StandardEventPayloadItem { diff --git a/packages/destination-actions/src/destinations/reddit-conversions-api/utils.ts b/packages/destination-actions/src/destinations/reddit-conversions-api/utils.ts index 9d764e7de5..aa5d3c7690 100644 --- a/packages/destination-actions/src/destinations/reddit-conversions-api/utils.ts +++ b/packages/destination-actions/src/destinations/reddit-conversions-api/utils.ts @@ -195,7 +195,8 @@ function getUser( user_agent: clean(user.user_agent), uuid: clean(user.uuid), data_processing_options: getDataProcessingOptions(dataProcessingOptions), - screen_dimensions: getScreen(screenDimensions?.height, screenDimensions?.width) + screen_dimensions: getScreen(screenDimensions?.height, screenDimensions?.width), + phone_number: user.phone_number } } From f9be9315920698641617a3ecdc969c41b26c100f Mon Sep 17 00:00:00 2001 From: akhsueh Date: Thu, 8 May 2025 16:29:02 -0700 Subject: [PATCH 02/78] Update order of mapping for phone numbers Co-authored-by: Joe Ayoub <45374896+joe-ayoub-segment@users.noreply.github.com> --- .../src/destinations/reddit-conversions-api/fields.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/destination-actions/src/destinations/reddit-conversions-api/fields.ts b/packages/destination-actions/src/destinations/reddit-conversions-api/fields.ts index 04be26b33b..7082bc773f 100644 --- a/packages/destination-actions/src/destinations/reddit-conversions-api/fields.ts +++ b/packages/destination-actions/src/destinations/reddit-conversions-api/fields.ts @@ -217,9 +217,9 @@ export const user: InputField = { }, phone_number: { '@if': { - exists: { '@path': '$.properties.phone' }, - then: { '@path': '$.properties.phone' }, - else: { '@path': '$.context.traits.phone' } + exists: { '@path': '$.context.traits.phone' }, + then: { '@path': '$.context.traits.phone' }, + else: { '@path': '$.properties.phone' } } } } From 6124d5749cfc9ec0d8d594fbf823dec52e120e71 Mon Sep 17 00:00:00 2001 From: rvadera12 <89420099+rvadera12@users.noreply.github.com> Date: Tue, 25 Mar 2025 03:07:05 -0700 Subject: [PATCH 03/78] [Twilio-Messaging-Omnichannel] Register and set up Basic Authentication (#2827) * Initial commit on branch * Delete packages/destination-actions/src/destinations/twilio-messaging-omnichannel/__tests__/index.test.ts * Delete packages/destination-actions/src/destinations/twilio-messaging-omnichannel/__tests__/snapshot.test.ts * Delete packages/destination-actions/src/destinations/twilio-messaging-omnichannel/sendMessage/__tests__/snapshot.test.ts * Delete packages/destination-actions/src/destinations/twilio-messaging-omnichannel/sendMessage/__tests__/index.test.ts --- .../generated-types.ts | 12 + .../twilio-messaging-omnichannel/index.ts | 47 ++++ .../sendMessage/fields.ts | 264 ++++++++++++++++++ .../sendMessage/generated-types.ts | 128 +++++++++ .../sendMessage/index.ts | 20 ++ 5 files changed, 471 insertions(+) create mode 100644 packages/destination-actions/src/destinations/twilio-messaging-omnichannel/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/twilio-messaging-omnichannel/index.ts create mode 100644 packages/destination-actions/src/destinations/twilio-messaging-omnichannel/sendMessage/fields.ts create mode 100644 packages/destination-actions/src/destinations/twilio-messaging-omnichannel/sendMessage/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/twilio-messaging-omnichannel/sendMessage/index.ts diff --git a/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/generated-types.ts b/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/generated-types.ts new file mode 100644 index 0000000000..e182f60254 --- /dev/null +++ b/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/generated-types.ts @@ -0,0 +1,12 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Settings { + /** + * Twilio Account SID. This can be found in the [Twilio Console](https://www.twilio.com/docs/usage/api#authenticate-with-http:~:text=token%20in%20the-,Twilio%20Console,-after%20signing%20up). + */ + username: string + /** + * AUTH Token. This can be found in the [Twilio Console](https://www.twilio.com/docs/usage/api#authenticate-with-http:~:text=token%20in%20the-,Twilio%20Console,-after%20signing%20up). + */ + password: string +} diff --git a/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/index.ts b/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/index.ts new file mode 100644 index 0000000000..e547c95a63 --- /dev/null +++ b/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/index.ts @@ -0,0 +1,47 @@ +import type { DestinationDefinition } from '@segment/actions-core' +import type { Settings } from './generated-types' + +import sendMessage from './sendMessage' + +const destination: DestinationDefinition = { + name: 'Twilio Messaging', + slug: 'actions-twilio-messaging-omnichannel', + mode: 'cloud', + + authentication: { + scheme: 'basic', + fields: { + username: { + label: 'Twilio Account SID.', + description: + 'Twilio Account SID. This can be found in the [Twilio Console](https://www.twilio.com/docs/usage/api#authenticate-with-http:~:text=token%20in%20the-,Twilio%20Console,-after%20signing%20up).', + type: 'string', + required: true + }, + password: { + label: 'AUTH Token', + description: + 'AUTH Token. This can be found in the [Twilio Console](https://www.twilio.com/docs/usage/api#authenticate-with-http:~:text=token%20in%20the-,Twilio%20Console,-after%20signing%20up).', + type: 'password', + required: true + } + }, + testAuthentication: (request) => { + return request(`https://api.twilio.com/2010-04-01`) + } + }, + + extendRequest({ settings }) { + return { + 'Content-Type': 'application/json', + username: settings.username, + password: settings.password + } + }, + + actions: { + sendMessage + } +} + +export default destination diff --git a/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/sendMessage/fields.ts b/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/sendMessage/fields.ts new file mode 100644 index 0000000000..aa4a49be37 --- /dev/null +++ b/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/sendMessage/fields.ts @@ -0,0 +1,264 @@ +import { InputField } from '@segment/actions-core' +import { DependsOnConditions } from '@segment/actions-core/destination-kittypes' + + +const DEPENDS_ON_MESSAGE_ADDRESS_SENDER: DependsOnConditions = { + match: 'all', + conditions: [ + { + fieldKey: 'from', + operator: 'is', + value: 'MessageAddressSender' + } + ] +} + +const DEPENDS_ON_AGENT_ID_SENDER: DependsOnConditions = { + match: 'all', + conditions: [ + { + fieldKey: 'from', + operator: 'is', + value: 'AgentIdSender' + } + ] +} + +const DEPENDS_ON_AGENT_POOL_ID_SENDER: DependsOnConditions = { + match: 'all', + conditions: [ + { + fieldKey: 'from', + operator: 'is', + value: 'AgentPoolIdSender' + } + ] +} + +const DEPENDS_ON_MESSAGE_CONTENT_TEXT: DependsOnConditions = { + match: 'all', + conditions: [ + { + fieldKey: 'content_type', + operator: 'is', + value: 'MessageContentText' + } + ] +} + +const DEPENDS_ON_MESSAGE_CONTENT_TEMPLATE: DependsOnConditions = { + match: 'all', + conditions: [ + { + fieldKey: 'content_type', + operator: 'is', + value: 'MessageContentTemplate' + } + ] +} + + +export const fields: Record = { + fromObject: { + type: 'object', + label: 'From', + description: 'Defines the fields applicable based on the selected sender type.', + required: true, + multiple: true, + properties: { + from: { + type: 'string', + label: 'From', + description: 'The type of sender.', + choices: [ + { label: 'Message Address Sender', value: 'MessageAddressSender' }, + { label: 'Agent ID Sender', value: 'AgentIdSender' }, + { label: 'Agent Pool ID Sender', value: 'AgentPoolIdSender' } + ] + }, + address: { + type: 'string', + label: 'Address', + description: 'The identifier within a channel address space for an actor.', + depends_on: DEPENDS_ON_MESSAGE_ADDRESS_SENDER + }, + channel: { + type: 'string', + label: 'Channel', + description: 'The channels available for the Message entity.', + choices: [ + { label: 'Phone', value: 'phone' }, + { label: 'RCS', value: 'rcs' }, + { label: 'WhatsApp', value: 'whatsapp' }, + { label: 'Facebook Messenger', value: 'fbm' } + ], + depends_on: DEPENDS_ON_MESSAGE_ADDRESS_SENDER + }, + agent_id: { + type: 'string', + label: 'Agent ID', + description: 'A reference to an Agent.', + depends_on: DEPENDS_ON_AGENT_ID_SENDER + }, + agent_pool_id: { + type: 'string', + label: 'Agent Pool ID', + description: 'A reference to an Agent Pool.', + depends_on: DEPENDS_ON_AGENT_POOL_ID_SENDER + } + + } + }, + to: { + type: 'object', + label: 'To', + description: 'An array of recipient objects to send the message(s) to.', + required: true, + multiple: true, + properties: { + address: { + type: 'string', + label: 'Address', + description: 'The identifier within a channel address space for an actor (e.g. phone number).', + required: true + }, + channel: { + type: 'string', + label: 'Channel', + description: 'The channels available for the Message entity.', + choices: [ + { label: 'Phone', value: 'phone' }, + { label: 'WhatsApp', value: 'whatsapp' }, + { label: 'Facebook Messenger', value: 'fbm' } + ], + required: true + }, + variables: { + type: 'object', + label: 'Variables', + defaultObjectUI: 'keyvalue:only', + description: 'To personalize content for each recipient, supply variables here with values to substitute into any Liquid templated content string or pre-stored Content template.' + } + } + }, + content: { + type: 'object', + label: 'Content', + description: 'Defines the fields applicable based on the selected content type.', + required: true, + properties: { + content_type: { + type: 'string', + label: 'Content Type', + description: 'The type of message content.', + choices: [ + { label: 'Text Content', value: 'MessageContentText' }, + { label: 'Template Content', value: 'MessageContentTemplate' } + ] + }, + text: { + type: 'string', + label: 'Text', + description: 'A simple string or templated content.', + required: true, + depends_on: DEPENDS_ON_MESSAGE_CONTENT_TEXT + }, + title: { + type: 'string', + label: 'Title', + description: 'Optional title prepended to the message.', + depends_on: DEPENDS_ON_MESSAGE_CONTENT_TEXT + }, + default_variables: { + type: 'object', + label: 'Default Variables', + defaultObjectUI: 'keyvalue:only', + description: 'Default values for use within the templated content field text and title.', + depends_on: DEPENDS_ON_MESSAGE_CONTENT_TEXT + }, + content_id: { + type: 'string', + label: 'Content ID', + description: 'A reference to a Content template.', + required: true, + depends_on: DEPENDS_ON_MESSAGE_CONTENT_TEMPLATE + } + } + }, + + channels: { + type: 'string', + label: 'Channels', + description: 'A list of eligible channels to constrain Messages to; leave undefined to send across all available channels.' + }, + + schedule: { + type: 'object', + label: 'Schedule', + description: 'A schedule defines when a communication will be sent to a recipient.', + properties: { + send_at: { + type: 'string', + label: 'Send At', + description: 'List of expressions defining when the message may be sent.', + }, + quiet_hours: { + type: 'string', + label: 'Quiet Hours', + description: 'List of expressions defining when the message may NOT be sent.', + }, + ignore_compliances: { + type: 'string', + label: 'Ignore Compliances', + description: 'List of compliance rules to ignore.', + choices: [ + { label: 'All', value: 'all' }, + { label: 'France Marketing', value: 'france_marketing' }, + { label: 'EU All', value: 'eu_all' }] + }, + frequency_cap: { + type: 'object', + label: 'Frequency Cap', + description: `Optimize the send time to prevent exceeding the count of communications sent to the recipient(s) for any time window of length period.`, + properties: { + count: { + type: 'string', + label: 'Count', + description: 'The number of communications.', + }, + period: { + type: 'string', + label: 'Period', + description: 'The duration for calculating the frequency count.', + default: 'P1D' + } + } + }, + ttl: { + type: 'string', + label: 'TTL', + description: 'Time-to-live for the communication.', + default: 'P1D' + } + } + }, + use_domain: { + type: 'object', + label: 'Use Domain', + description: 'The Domain to use for wrapping links for click-tracked links and shortened links.', + properties: { + domain_name: { + type: 'string', + label: 'Domain Name', + description: 'A fully qualified domain name (FQDN) that you have registered with your DNS provider.' + } + } + }, + tags: { + type: 'object', + label: 'Tags', + description: 'Custom metadata in the form of key-value pairs. Maximum size of a tag key is 128 characters. Maximum size of a tag value is 256 characters. There can be a maximum of 10 key-value pairs.', + defaultObjectUI: 'keyvalue:only' + } + +} diff --git a/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/sendMessage/generated-types.ts b/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/sendMessage/generated-types.ts new file mode 100644 index 0000000000..2612af191f --- /dev/null +++ b/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/sendMessage/generated-types.ts @@ -0,0 +1,128 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * Defines the fields applicable based on the selected sender type. + */ + fromObject: { + /** + * The type of sender. + */ + from?: string + /** + * The identifier within a channel address space for an actor. + */ + address?: string + /** + * The channels available for the Message entity. + */ + channel?: string + /** + * A reference to an Agent. + */ + agent_id?: string + /** + * A reference to an Agent Pool. + */ + agent_pool_id?: string + }[] + /** + * An array of recipient objects to send the message(s) to. + */ + to: { + /** + * The identifier within a channel address space for an actor (e.g. phone number). + */ + address: string + /** + * The channels available for the Message entity. + */ + channel: string + /** + * To personalize content for each recipient, supply variables here with values to substitute into any Liquid templated content string or pre-stored Content template. + */ + variables?: { + [k: string]: unknown + } + }[] + /** + * Defines the fields applicable based on the selected content type. + */ + content: { + /** + * The type of message content. + */ + content_type?: string + /** + * A simple string or templated content. + */ + text: string + /** + * Optional title prepended to the message. + */ + title?: string + /** + * Default values for use within the templated content field text and title. + */ + default_variables?: { + [k: string]: unknown + } + /** + * A reference to a Content template. + */ + content_id: string + } + /** + * A list of eligible channels to constrain Messages to; leave undefined to send across all available channels. + */ + channels?: string + /** + * A schedule defines when a communication will be sent to a recipient. + */ + schedule?: { + /** + * List of expressions defining when the message may be sent. + */ + send_at?: string + /** + * List of expressions defining when the message may NOT be sent. + */ + quiet_hours?: string + /** + * List of compliance rules to ignore. + */ + ignore_compliances?: string + /** + * Optimize the send time to prevent exceeding the count of communications sent to the recipient(s) for any time window of length period. + */ + frequency_cap?: { + /** + * The number of communications. + */ + count?: string + /** + * The duration for calculating the frequency count. + */ + period?: string + } + /** + * Time-to-live for the communication. + */ + ttl?: string + } + /** + * The Domain to use for wrapping links for click-tracked links and shortened links. + */ + use_domain?: { + /** + * A fully qualified domain name (FQDN) that you have registered with your DNS provider. + */ + domain_name?: string + } + /** + * Custom metadata in the form of key-value pairs. Maximum size of a tag key is 128 characters. Maximum size of a tag value is 256 characters. There can be a maximum of 10 key-value pairs. + */ + tags?: { + [k: string]: unknown + } +} diff --git a/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/sendMessage/index.ts b/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/sendMessage/index.ts new file mode 100644 index 0000000000..e1bf23ef6d --- /dev/null +++ b/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/sendMessage/index.ts @@ -0,0 +1,20 @@ +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' +import { fields } from './fields' + +const action: ActionDefinition = { + title: 'Send Message', + description: 'This operation creates and sends out messages to the specified recipients.', + fields, + perform: () => { + return "hello" + // Make your partner api request here! + // return request('https://example.com', { + // method: 'post', + // json: data.payload + // }) + } +} + +export default action From 314ceb116658b2933d0cb2d7074b37f92895cf6e Mon Sep 17 00:00:00 2001 From: Nick Aguilar Date: Tue, 25 Mar 2025 03:07:47 -0700 Subject: [PATCH 04/78] [Salesforce] Advanced stats for CSV generation (#2825) * WIP * Adds stats around CSV generation. Counts the number of columns and rows. Counts the number of actual values and number of nulls for the whole CSV. Unit tests included * Log out the CSV stats after the job is created to ensure the jobID is a defined tag --- .../salesforce/__tests__/sf-utils.test.ts | 59 +++++++++++++++++++ .../destinations/salesforce/sf-operations.ts | 31 ++++++++-- .../src/destinations/salesforce/sf-utils.ts | 28 ++++++++- 3 files changed, 109 insertions(+), 9 deletions(-) diff --git a/packages/destination-actions/src/destinations/salesforce/__tests__/sf-utils.test.ts b/packages/destination-actions/src/destinations/salesforce/__tests__/sf-utils.test.ts index 54ef0141f9..d3606e449f 100644 --- a/packages/destination-actions/src/destinations/salesforce/__tests__/sf-utils.test.ts +++ b/packages/destination-actions/src/destinations/salesforce/__tests__/sf-utils.test.ts @@ -298,6 +298,65 @@ describe('Salesforce Utils', () => { }) }) + describe('Logging capabilities', () => { + it('should function correctly when logging is enabled', async () => { + const createPayloads: GenericPayload[] = [ + { + operation: 'create', + enable_batching: true, + name: 'SpongeBob Squarepants', + phone: '1234567890', + description: 'Krusty Krab' + }, + { + operation: 'create', + enable_batching: true, + name: 'Squidward Tentacles', + phone: '1234567891', + description: 'Krusty Krab' + } + ] + + const csvStats = { shouldLog: true, numberOfColumns: 0, numberOfValuesInCSV: 0, numberOfNullsInCSV: 0 } + + const csv = buildCSVData(createPayloads, '', 'insert', csvStats) + const expected = `Name,Phone,Description\n"SpongeBob Squarepants","1234567890","Krusty Krab"\n"Squidward Tentacles","1234567891","Krusty Krab"\n` + + expect(csv).toEqual(expected) + }) + + it('should correctly count the number of values and number of nulls in the CSV', async () => { + const incompleteUpdatePayloads: GenericPayload[] = [ + { + operation: 'update', + enable_batching: true, + bulkUpdateRecordId: '00', + name: 'SpongeBob Squarepants', + phone: '1234567890' + }, + { + operation: 'update', + enable_batching: true, + bulkUpdateRecordId: '01', + name: 'Squidward Tentacles', + description: 'Krusty Krab' + } + ] + + const csvStats = { shouldLog: true, numberOfColumns: 0, numberOfValuesInCSV: 0, numberOfNullsInCSV: 0 } + + const csv = buildCSVData(incompleteUpdatePayloads, 'Id', 'Update', csvStats) + const expected = `Name,Description,Phone,Id\n"SpongeBob Squarepants",#N/A,"1234567890","00"\n"Squidward Tentacles","Krusty Krab",#N/A,"01"\n` + + expect(csv).toEqual(expected) + + expect(csvStats.numberOfColumns).toEqual(4) + + expect(csvStats.numberOfValuesInCSV).toEqual(4) + expect(csvStats.numberOfNullsInCSV).toEqual(2) + }) + }) + describe('Instance URL', () => { const badInstanceUrls = [ 'https://www.google.com', diff --git a/packages/destination-actions/src/destinations/salesforce/sf-operations.ts b/packages/destination-actions/src/destinations/salesforce/sf-operations.ts index d8439534fa..2a4b4afcc8 100644 --- a/packages/destination-actions/src/destinations/salesforce/sf-operations.ts +++ b/packages/destination-actions/src/destinations/salesforce/sf-operations.ts @@ -371,8 +371,27 @@ export default class Salesforce { logging?: { shouldLog: boolean; logger?: Logger; stats?: StatsContext } ): Promise> { // construct the CSV data to catch errors before creating a bulk job - const csv = buildCSVData(payloads, idField, operation) + const csvStats = { + shouldLog: logging?.shouldLog === true, + numberOfColumns: 0, + numberOfValuesInCSV: 0, + numberOfNullsInCSV: 0 + } + + const csv = buildCSVData(payloads, idField, operation, csvStats) const jobId = await this.createBulkJob(sobject, idField, operation, logging) + + if (logging?.shouldLog === true) { + const statsClient = logging?.stats?.statsClient + const tags = logging?.stats?.tags + tags?.push('jobId:' + jobId) + + statsClient?.incr('bulkCSV.payloadSize', payloads.length, tags) + statsClient?.incr('bulkCSV.numberOfColumns', csvStats.numberOfColumns, tags) + statsClient?.incr('bulkCSV.numberOfValuesInCSV', csvStats.numberOfValuesInCSV, tags) + statsClient?.incr('bulkCSV.numberOfNullsInCSV', csvStats.numberOfNullsInCSV ?? 0, tags) + } + try { await this.uploadBulkCSV(jobId, csv, logging) } catch (err) { @@ -390,7 +409,7 @@ export default class Salesforce { const tags = logging?.stats?.tags tags?.push('jobId:' + jobId) statsClient?.incr('bulkJobError.caughUploadError', 1, tags) - logging?.logger?.crit(`Failed to close bulk job: ${jobId}. Message: ${message}. Code: ${code}`) + logging?.logger?.error(`Failed to close bulk job: ${jobId}. Message: ${message}. Code: ${code}`) }) throw err } @@ -406,7 +425,7 @@ export default class Salesforce { tags?.push('jobId:' + jobId) statsClient?.incr('bulkJobError', 1, tags) - logging?.logger?.crit(`Failed to close bulk job: ${jobId}. Message: ${message}. Code: ${code}`) + logging?.logger?.error(`Failed to close bulk job: ${jobId}. Message: ${message}. Code: ${code}`) throw err } @@ -441,7 +460,7 @@ export default class Salesforce { } if (logging?.shouldLog) { - logging.logger?.crit(`Created bulk job: ${res.data.id} with data: ${JSON.stringify(jsonData)}`) + logging.logger?.error(`Created bulk job: ${res.data.id} with data: ${JSON.stringify(jsonData)}`) const statsClient = logging.stats?.statsClient const tags = logging.stats?.tags @@ -459,7 +478,7 @@ export default class Salesforce { logging?: { shouldLog: boolean; logger?: Logger; stats?: StatsContext } ) => { if (logging?.shouldLog) { - logging.logger?.crit(`Uploading CSV to job: ${jobId}\nCSV: ${csv}`) + logging.logger?.error(`Uploading CSV to job: ${jobId}\nCSV: ${csv}`) const statsClient = logging.stats?.statsClient const tags = logging.stats?.tags @@ -482,7 +501,7 @@ export default class Salesforce { logging?: { shouldLog: boolean; logger?: Logger; stats?: StatsContext } ) => { if (logging?.shouldLog) { - logging.logger?.crit(`Closing job: ${jobId}`) + logging.logger?.error(`Closing job: ${jobId}`) const statsClient = logging.stats?.statsClient const tags = logging.stats?.tags diff --git a/packages/destination-actions/src/destinations/salesforce/sf-utils.ts b/packages/destination-actions/src/destinations/salesforce/sf-utils.ts index 40a0d1315a..8f7d3d04b4 100644 --- a/packages/destination-actions/src/destinations/salesforce/sf-utils.ts +++ b/packages/destination-actions/src/destinations/salesforce/sf-utils.ts @@ -108,9 +108,12 @@ const buildCSVFromHeaderMap = ( payloads: GenericPayload[], headerMap: Map, n: number, - operation: string | undefined + operation: string | undefined, + csvStats?: { shouldLog: boolean; numberOfColumns: number; numberOfValuesInCSV: number; numberOfNullsInCSV?: number } ): string => { let rows = '' + let totalNoValueFound = 0 + let totalValuesInCSV = 0 for (let i = 0; i < n; i++) { let row = '' @@ -121,11 +124,13 @@ const buildCSVFromHeaderMap = ( if (valueTuple != null && valueTuple[0] != null) { row += `"${escapeDoubleQuotes(valueTuple[0])}",` noValueFound = false + totalValuesInCSV++ } } if (noValueFound) { row += `${NO_VALUE},` + totalNoValueFound++ } } @@ -146,6 +151,12 @@ const buildCSVFromHeaderMap = ( rows += `${row}"${uniqueIdValue}"\n` } } + + if (csvStats?.shouldLog === true) { + csvStats.numberOfNullsInCSV = totalNoValueFound + csvStats.numberOfValuesInCSV = totalValuesInCSV + } + return rows } @@ -177,17 +188,28 @@ const getUniqueIdValue = (payload: GenericPayload, operation: string | undefined export const buildCSVData = ( payloads: GenericPayload[], uniqueIdName: string, - operation: string | undefined + operation: string | undefined, + csvStats?: { shouldLog: boolean; numberOfColumns: number; numberOfValuesInCSV: number; numberOfNullsInCSV?: number } ): string => { const headerMap = buildHeaderMap(payloads) let csv = buildHeaders(headerMap) + if (csvStats?.shouldLog === true) { + let numberOfColumns = headerMap.size + + if (operation !== 'insert' && operation !== 'create') { + numberOfColumns++ + } + + csvStats.numberOfColumns = numberOfColumns + } + if (operation === 'insert') { // Remove the trailing comma, since there is no unique ID to append csv = csv.substring(0, csv.length - 1) } - csv += `${uniqueIdName}\n` + buildCSVFromHeaderMap(payloads, headerMap, payloads.length, operation) + csv += `${uniqueIdName}\n` + buildCSVFromHeaderMap(payloads, headerMap, payloads.length, operation, csvStats) return csv } From 0a11ea53c302b9c943e64c14a82e2b74293ce407 Mon Sep 17 00:00:00 2001 From: Varadarajan V <109586712+varadarajan-tw@users.noreply.github.com> Date: Tue, 25 Mar 2025 15:37:58 +0530 Subject: [PATCH 05/78] [STRATCONN-5603] - Format timestamp fields without miliiseconds (#2816) * [STRATCONN-5603] - Format timestamp fields without miliiseconds * Revert type changes * Revert type changes * [GEC] Simplify convertTimestamp Regex * Revert unintended change --- .../__tests__/functions.test.ts | 16 +++++++++++++++- .../google-enhanced-conversions/functions.ts | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/functions.test.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/functions.test.ts index b710ae34ed..f5c9f10982 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/functions.test.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/functions.test.ts @@ -1,7 +1,7 @@ import { createTestIntegration, DynamicFieldResponse } from '@segment/actions-core' import { Features } from '@segment/actions-core/mapping-kit' import nock from 'nock' -import { CANARY_API_VERSION, formatToE164, commonEmailValidation } from '../functions' +import { CANARY_API_VERSION, formatToE164, commonEmailValidation, convertTimestamp } from '../functions' import destination from '../index' const testDestination = createTestIntegration(destination) @@ -178,3 +178,17 @@ describe('phone number formatting', () => { expect(formatToE164('+49 30 1234567', '49')).toEqual('+49301234567') }) }) + +describe('convertTimestamp', () => { + it('should convert timestamp with milliseconds', () => { + const timestamp = '2025-03-11T19:03:56.616960388Z' + const result = convertTimestamp(timestamp) + expect(result).toEqual('2025-03-11 19:03:56+00:00') + }) + + it('should convert timestamp without milliseconds', () => { + const timestamp = '2025-03-11T17:57:29Z' + const result = convertTimestamp(timestamp) + expect(result).toEqual('2025-03-11 17:57:29+00:00') + }) +}) diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/functions.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/functions.ts index 43fbfdafbb..2cd0713e71 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/functions.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/functions.ts @@ -205,7 +205,7 @@ export function convertTimestamp(timestamp: string | undefined): string | undefi if (!timestamp) { return undefined } - return timestamp.replace(/T/, ' ').replace(/\..+/, '+00:00') + return timestamp.replace(/T/, ' ').replace(/(\.\d+)?Z/, '+00:00') } export function getApiVersion(features?: Features, statsContext?: StatsContext): string { From 802991ec467fd15ed9d6e6669f2a285ce32f7977 Mon Sep 17 00:00:00 2001 From: Arijit Ray <35370469+itsarijitray@users.noreply.github.com> Date: Tue, 25 Mar 2025 15:38:07 +0530 Subject: [PATCH 06/78] [STRATCONN-5601] add subscription object support for unsubscribeProfile action in Klaviyo (#2814) * add subscription object support for unsubscribeProfile action in Klaviyo * update snapshots * add tests * add tests * fix comments --- .../__snapshots__/snapshot.test.ts.snap | 15 ++ .../src/destinations/klaviyo/functions.ts | 25 ++- .../src/destinations/klaviyo/types.ts | 15 ++ .../__tests__/index.test.ts | 176 ++++++++++++++++-- 4 files changed, 214 insertions(+), 17 deletions(-) diff --git a/packages/destination-actions/src/destinations/klaviyo/__tests__/__snapshots__/snapshot.test.ts.snap b/packages/destination-actions/src/destinations/klaviyo/__tests__/__snapshots__/snapshot.test.ts.snap index 5623f0c0aa..eb93df5fe2 100644 --- a/packages/destination-actions/src/destinations/klaviyo/__tests__/__snapshots__/snapshot.test.ts.snap +++ b/packages/destination-actions/src/destinations/klaviyo/__tests__/__snapshots__/snapshot.test.ts.snap @@ -159,6 +159,21 @@ Object { "attributes": Object { "email": "vej@ir.cz", "phone_number": "+3658066891", + "subscriptions": Object { + "email": Object { + "marketing": Object { + "consent": "UNSUBSCRIBED", + }, + }, + "sms": Object { + "marketing": Object { + "consent": "UNSUBSCRIBED", + }, + "transactional": Object { + "consent": "UNSUBSCRIBED", + }, + }, + }, }, "type": "profile", }, diff --git a/packages/destination-actions/src/destinations/klaviyo/functions.ts b/packages/destination-actions/src/destinations/klaviyo/functions.ts index bab444e3c9..a3f6260a0f 100644 --- a/packages/destination-actions/src/destinations/klaviyo/functions.ts +++ b/packages/destination-actions/src/destinations/klaviyo/functions.ts @@ -329,19 +329,34 @@ export function formatUnsubscribeRequestBody( } export function formatUnsubscribeProfile(email: string | undefined, phone_number: string | undefined) { - const profileToSubscribe: UnsubscribeProfile = { + const profileToUnSubscribe: UnsubscribeProfile = { type: 'profile', - attributes: {} + attributes: { + subscriptions: {} + } } if (email) { - profileToSubscribe.attributes.email = email + profileToUnSubscribe.attributes.email = email + profileToUnSubscribe.attributes.subscriptions.email = { + marketing: { + consent: 'UNSUBSCRIBED' + } + } } if (phone_number) { - profileToSubscribe.attributes.phone_number = phone_number + profileToUnSubscribe.attributes.phone_number = phone_number + profileToUnSubscribe.attributes.subscriptions.sms = { + marketing: { + consent: 'UNSUBSCRIBED' + }, + transactional: { + consent: 'UNSUBSCRIBED' + } + } } - return profileToSubscribe + return profileToUnSubscribe } export async function getList(request: RequestClient, settings: Settings, listId: string) { diff --git a/packages/destination-actions/src/destinations/klaviyo/types.ts b/packages/destination-actions/src/destinations/klaviyo/types.ts index 39f2ded829..37833dc471 100644 --- a/packages/destination-actions/src/destinations/klaviyo/types.ts +++ b/packages/destination-actions/src/destinations/klaviyo/types.ts @@ -186,6 +186,21 @@ export interface UnsubscribeProfile { attributes: { email?: string phone_number?: string + subscriptions: { + email?: { + marketing: { + consent: 'UNSUBSCRIBED' + } + } + sms?: { + marketing?: { + consent: 'UNSUBSCRIBED' + } + transactional?: { + consent: 'UNSUBSCRIBED' + } + } + } } } diff --git a/packages/destination-actions/src/destinations/klaviyo/unsubscribeProfile/__tests__/index.test.ts b/packages/destination-actions/src/destinations/klaviyo/unsubscribeProfile/__tests__/index.test.ts index 3583f6376d..44a39b5b76 100644 --- a/packages/destination-actions/src/destinations/klaviyo/unsubscribeProfile/__tests__/index.test.ts +++ b/packages/destination-actions/src/destinations/klaviyo/unsubscribeProfile/__tests__/index.test.ts @@ -75,7 +75,17 @@ describe('Unsubscribe Profile', () => { { type: 'profile', attributes: { - phone_number: '+918448309222' + phone_number: '+918448309222', + subscriptions: { + sms: { + marketing: { + consent: 'UNSUBSCRIBED' + }, + transactional: { + consent: 'UNSUBSCRIBED' + } + } + } } } ] @@ -107,7 +117,22 @@ describe('Unsubscribe Profile', () => { type: 'profile', attributes: { email: payload.email, - phone_number: payload.phone_number + phone_number: payload.phone_number, + subscriptions: { + sms: { + marketing: { + consent: 'UNSUBSCRIBED' + }, + transactional: { + consent: 'UNSUBSCRIBED' + } + }, + email: { + marketing: { + consent: 'UNSUBSCRIBED' + } + } + } } } ] @@ -122,7 +147,22 @@ describe('Unsubscribe Profile', () => { context: { traits: { email: payload.email, - phone_number: payload.phone_number + phone_number: payload.phone_number, + subscriptions: { + sms: { + marketing: { + consent: 'UNSUBSCRIBED' + }, + transactional: { + consent: 'UNSUBSCRIBED' + } + }, + email: { + marketing: { + consent: 'UNSUBSCRIBED' + } + } + } } } }) @@ -160,7 +200,22 @@ describe('Unsubscribe Profile', () => { type: 'profile', attributes: { email: payload.email, - phone_number: payload.phone_number + phone_number: payload.phone_number, + subscriptions: { + sms: { + marketing: { + consent: 'UNSUBSCRIBED' + }, + transactional: { + consent: 'UNSUBSCRIBED' + } + }, + email: { + marketing: { + consent: 'UNSUBSCRIBED' + } + } + } } } ] @@ -220,7 +275,14 @@ describe('Unsubscribe Profile', () => { { type: 'profile', attributes: { - email: payload.email + email: payload.email, + subscriptions: { + email: { + marketing: { + consent: 'UNSUBSCRIBED' + } + } + } } } ] @@ -272,7 +334,17 @@ describe('Unsubscribe Profile', () => { { type: 'profile', attributes: { - phone_number: payload.phone_number + phone_number: payload.phone_number, + subscriptions: { + sms: { + marketing: { + consent: 'UNSUBSCRIBED' + }, + transactional: { + consent: 'UNSUBSCRIBED' + } + } + } } } ] @@ -463,6 +535,13 @@ describe('Unsubscribe Profile', () => { { type: 'profile', attributes: { + subscriptions: { + email: { + marketing: { + consent: 'UNSUBSCRIBED' + } + } + }, email: 'user1@example.com' } } @@ -521,12 +600,29 @@ describe('Unsubscribe Profile', () => { { type: 'profile', attributes: { + subscriptions: { + email: { + marketing: { + consent: 'UNSUBSCRIBED' + } + } + }, email: 'user1@example.com' } }, { type: 'profile', attributes: { + subscriptions: { + sms: { + marketing: { + consent: 'UNSUBSCRIBED' + }, + transactional: { + consent: 'UNSUBSCRIBED' + } + } + }, phone_number: '+918448309222' } } @@ -603,20 +699,52 @@ describe('Unsubscribe Profile', () => { { type: 'profile', attributes: { - email: 'test@email.com' + email: 'test@email.com', + subscriptions: { + email: { + marketing: { + consent: 'UNSUBSCRIBED' + } + } + } } }, { type: 'profile', attributes: { - phone_number: '+17067675129' + phone_number: '+17067675129', + subscriptions: { + sms: { + marketing: { + consent: 'UNSUBSCRIBED' + }, + transactional: { + consent: 'UNSUBSCRIBED' + } + } + } } }, { type: 'profile', attributes: { email: 'test2@email.com', - phone_number: '+17067665437' + phone_number: '+17067665437', + subscriptions: { + sms: { + marketing: { + consent: 'UNSUBSCRIBED' + }, + transactional: { + consent: 'UNSUBSCRIBED' + } + }, + email: { + marketing: { + consent: 'UNSUBSCRIBED' + } + } + } } } ] @@ -693,7 +821,14 @@ describe('Unsubscribe Profile', () => { { type: 'profile', attributes: { - email: 'test@email.com' + email: 'test@email.com', + subscriptions: { + email: { + marketing: { + consent: 'UNSUBSCRIBED' + } + } + } } } ] @@ -718,7 +853,17 @@ describe('Unsubscribe Profile', () => { { type: 'profile', attributes: { - phone_number: '+17067675129' + phone_number: '+17067675129', + subscriptions: { + sms: { + marketing: { + consent: 'UNSUBSCRIBED' + }, + transactional: { + consent: 'UNSUBSCRIBED' + } + } + } } } ] @@ -744,7 +889,14 @@ describe('Unsubscribe Profile', () => { { type: 'profile', attributes: { - email: 'test2@email.com' + email: 'test2@email.com', + subscriptions: { + email: { + marketing: { + consent: 'UNSUBSCRIBED' + } + } + } } } ] From 7cde35fe53279a61dabbb5838da9c379eab91ae3 Mon Sep 17 00:00:00 2001 From: Innovative-GauravKochar <117165746+Innovative-GauravKochar@users.noreply.github.com> Date: Tue, 25 Mar 2025 15:38:39 +0530 Subject: [PATCH 07/78] Made Event properties hidden (#2813) Co-authored-by: Gaurav Kochar --- .../src/destinations/braze-cohorts/syncAudiences/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/destination-actions/src/destinations/braze-cohorts/syncAudiences/index.ts b/packages/destination-actions/src/destinations/braze-cohorts/syncAudiences/index.ts index 84ff91303b..48321c3345 100644 --- a/packages/destination-actions/src/destinations/braze-cohorts/syncAudiences/index.ts +++ b/packages/destination-actions/src/destinations/braze-cohorts/syncAudiences/index.ts @@ -83,6 +83,7 @@ const action: ActionDefinition = { 'Displays properties of the event to add/remove users to a cohort and the traits of the specific user', type: 'object', required: true, + unsafe_hidden: true, default: { '@if': { exists: { '@path': '$.properties' }, From 7406e56517d46307435abe14b2f086395ac4d7fb Mon Sep 17 00:00:00 2001 From: Marc Dillar Date: Tue, 25 Mar 2025 11:11:40 +0100 Subject: [PATCH 08/78] [Criteo] Audience Destination: set enable_batching to true by default (#2771) * [Criteo] Audience Destination: set enable_batching to true by default * Criteo Audience Destination: make 'Enable Batching' optional * Criteo Audience Destination: updated generated types --------- Co-authored-by: m.dillar --- .../src/destinations/criteo-audiences/generated-types.ts | 4 ++++ .../src/destinations/criteo-audiences/index.ts | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/packages/destination-actions/src/destinations/criteo-audiences/generated-types.ts b/packages/destination-actions/src/destinations/criteo-audiences/generated-types.ts index 272ddcfe9a..45cd35d944 100644 --- a/packages/destination-actions/src/destinations/criteo-audiences/generated-types.ts +++ b/packages/destination-actions/src/destinations/criteo-audiences/generated-types.ts @@ -13,4 +13,8 @@ export interface Settings { * Your Criteo Advertiser ID */ advertiser_id: string + /** + * Important: This setting should remain enabled! + */ + enable_batching?: boolean } diff --git a/packages/destination-actions/src/destinations/criteo-audiences/index.ts b/packages/destination-actions/src/destinations/criteo-audiences/index.ts index 206e70682b..289023f49e 100644 --- a/packages/destination-actions/src/destinations/criteo-audiences/index.ts +++ b/packages/destination-actions/src/destinations/criteo-audiences/index.ts @@ -30,6 +30,13 @@ const destination: DestinationDefinition = { description: 'Your Criteo Advertiser ID', type: 'string', required: true + }, + enable_batching: { + type: 'boolean', + label: 'Enable Batching', + description: 'Important: This setting should remain enabled!', + required: false, + default: true } }, testAuthentication: async (request, { settings }) => { From e5cdd168d083d366b437a8c36eca1791709f81cc Mon Sep 17 00:00:00 2001 From: Jessica Date: Tue, 25 Mar 2025 21:12:03 +1100 Subject: [PATCH 09/78] [algolia-insights] update presets for conversion events (#2770) * deprecate Product Added * update conversion presets * fix --- .../conversionEvents/index.ts | 284 +++++++++--------- .../destinations/algolia-insights/index.ts | 8 +- .../productAddedEvents/index.ts | 15 +- 3 files changed, 154 insertions(+), 153 deletions(-) diff --git a/packages/destination-actions/src/destinations/algolia-insights/conversionEvents/index.ts b/packages/destination-actions/src/destinations/algolia-insights/conversionEvents/index.ts index 01f649138d..fe11e449d6 100644 --- a/packages/destination-actions/src/destinations/algolia-insights/conversionEvents/index.ts +++ b/packages/destination-actions/src/destinations/algolia-insights/conversionEvents/index.ts @@ -1,4 +1,4 @@ -import type { ActionDefinition, Preset } from '@segment/actions-core' +import type { ActionDefinition, BaseActionDefinition, Preset } from '@segment/actions-core' import { defaultValues } from '@segment/actions-core' import { AlgoliaBehaviourURL, @@ -11,147 +11,149 @@ import type { Payload } from './generated-types' const notUndef = (thing: unknown) => typeof thing !== 'undefined' -export const conversionEvents: ActionDefinition = { - title: 'Conversion Events', - description: - 'In ecommerce, conversions are purchase events often but not always involving multiple products. Outside of a conversion can be any positive signal associated with an index record. Query ID is optional and indicates that the view events is the result of a search query.', - fields: { - eventSubtype: { - label: 'Event Subtype', - description: 'Sub-type of the event, "purchase" or "addToCart".', - type: 'string', - required: false, - choices: [ - { value: 'purchase', label: 'Purchase' }, - { value: 'addToCart', label: 'Add To Cart' } - ], - default: 'purchase' +const getEventFields = (subtype: AlgoliaEventSubtype = 'purchase'): BaseActionDefinition['fields'] => ({ + eventSubtype: { + label: 'Event Subtype', + description: 'Sub-type of the event, "purchase" or "addToCart".', + type: 'string', + required: false, + choices: [ + { value: 'purchase', label: 'Purchase' }, + { value: 'addToCart', label: 'Add To Cart' } + ], + default: subtype + }, + products: { + label: 'Product Details', + description: + 'Populates the ObjectIDs field in the Algolia Insights API. An array of objects representing the purchased items. Each object must contain a product_id field.', + type: 'object', + multiple: true, + defaultObjectUI: 'keyvalue', + properties: { + product_id: { label: 'product_id', type: 'string', required: true }, + price: { label: 'price', type: 'number', required: false }, + quantity: { label: 'quantity', type: 'number', required: false }, + discount: { label: 'discount', type: 'number', required: false }, + queryID: { label: 'queryID', type: 'string', required: false } }, - products: { - label: 'Product Details', - description: - 'Populates the ObjectIDs field in the Algolia Insights API. An array of objects representing the purchased items. Each object must contain a product_id field.', - type: 'object', - multiple: true, - defaultObjectUI: 'keyvalue', - properties: { - product_id: { label: 'product_id', type: 'string', required: true }, - price: { label: 'price', type: 'number', required: false }, - quantity: { label: 'quantity', type: 'number', required: false }, - discount: { label: 'discount', type: 'number', required: false }, - queryID: { label: 'queryID', type: 'string', required: false } - }, - required: true, - default: { - '@arrayPath': [ - '$.properties.products', - { - product_id: { - '@path': '$.product_id' - }, - price: { - '@path': '$.price' - }, - quantity: { - '@path': '$.quantity' - }, - discount: { - '@path': '$.discount' - }, - queryID: { - '@path': '$.queryID' - } + required: true, + default: { + '@arrayPath': [ + '$.properties.products', + { + product_id: { + '@path': '$.product_id' + }, + price: { + '@path': '$.price' + }, + quantity: { + '@path': '$.quantity' + }, + discount: { + '@path': '$.discount' + }, + queryID: { + '@path': '$.queryID' } - ] - } - }, - index: { - label: 'Index', - description: 'Name of the targeted search index.', - type: 'string', - required: true, - default: { - '@path': '$.properties.search_index' - } - }, - queryID: { - label: 'Query ID', - description: 'Query ID of the list on which the item was purchased.', - type: 'string', - required: false, - default: { - '@if': { - exists: { '@path': '$.properties.query_id' }, - then: { '@path': '$.properties.query_id' }, - else: { '@path': '$.integrations.Algolia Insights (Actions).query_id' } - } - } - }, - userToken: { - type: 'string', - required: true, - description: 'The ID associated with the user.', - label: 'User Token', - default: { - '@if': { - exists: { '@path': '$.userId' }, - then: { '@path': '$.userId' }, - else: { '@path': '$.anonymousId' } } + ] + } + }, + index: { + label: 'Index', + description: 'Name of the targeted search index.', + type: 'string', + required: true, + default: { + '@path': '$.properties.search_index' + } + }, + queryID: { + label: 'Query ID', + description: 'Query ID of the list on which the item was purchased.', + type: 'string', + required: false, + default: { + '@if': { + exists: { '@path': '$.properties.query_id' }, + then: { '@path': '$.properties.query_id' }, + else: { '@path': '$.integrations.Algolia Insights (Actions).query_id' } } - }, - timestamp: { - type: 'string', - required: false, - description: 'The timestamp of the event.', - label: 'Timestamp', - default: { '@path': '$.timestamp' } - }, - value: { - type: 'number', - required: false, - description: 'The value of the cart that is being converted.', - label: 'Value', - default: { '@path': '$.properties.value' } - }, - currency: { - type: 'string', - required: false, - description: - 'Currency of the objects associated with the event in 3-letter ISO 4217 format. Required when `value` or `price` is set.', - label: 'Currency', - default: { '@path': '$.properties.currency' } - }, - extraProperties: { - label: 'Extra Properties', - required: false, - description: - 'Additional fields for this event. This field may be useful for Algolia Insights fields which are not mapped in Segment.', - type: 'object', - default: { - '@path': '$.properties' + } + }, + userToken: { + type: 'string', + required: true, + description: 'The ID associated with the user.', + label: 'User Token', + default: { + '@if': { + exists: { '@path': '$.userId' }, + then: { '@path': '$.userId' }, + else: { '@path': '$.anonymousId' } } - }, - eventName: { - label: 'Event Name', - description: "The name of the event to send to Algolia. Defaults to 'Conversion Event'", - type: 'string', - required: false, - default: 'Conversion Event' - }, - eventType: { - label: 'Event Type', - description: "The type of event to send to Algolia. Defaults to 'conversion'", - type: 'string', - required: false, - default: 'conversion', - choices: [ - { label: 'View', value: 'view' }, - { label: 'Conversion', value: 'conversion' }, - { label: 'Click', value: 'click' } - ] } }, + timestamp: { + type: 'string', + required: false, + description: 'The timestamp of the event.', + label: 'Timestamp', + default: { '@path': '$.timestamp' } + }, + value: { + type: 'number', + required: false, + description: 'The value of the cart that is being converted.', + label: 'Value', + default: { '@path': '$.properties.value' } + }, + currency: { + type: 'string', + required: false, + description: + 'Currency of the objects associated with the event in 3-letter ISO 4217 format. Required when `value` or `price` is set.', + label: 'Currency', + default: { '@path': '$.properties.currency' } + }, + extraProperties: { + label: 'Extra Properties', + required: false, + description: + 'Additional fields for this event. This field may be useful for Algolia Insights fields which are not mapped in Segment.', + type: 'object', + default: { + '@path': '$.properties' + } + }, + eventName: { + label: 'Event Name', + description: "The name of the event to send to Algolia. Defaults to 'Conversion Event'", + type: 'string', + required: false, + default: 'Conversion Event' + }, + eventType: { + label: 'Event Type', + description: "The type of event to send to Algolia. Defaults to 'conversion'", + type: 'string', + required: false, + default: 'conversion', + choices: [ + { label: 'View', value: 'view' }, + { label: 'Conversion', value: 'conversion' }, + { label: 'Click', value: 'click' } + ] + } +}) + +export const conversionEvents: ActionDefinition = { + title: 'Conversion Events', + description: + 'In ecommerce, conversions are purchase or add-to-cart events often but not always involving multiple products. Outside of ecommerce, a conversion can be any positive signal associated with an index record. Query ID is optional and indicates that the event is the result of a search query.', + fields: getEventFields(), defaultSubscription: 'type = "track" and event = "Order Completed"', perform: (request, data) => { const objectData = data.payload.products.some(({ queryID, price, discount, quantity }) => { @@ -188,10 +190,18 @@ export const conversionEvents: ActionDefinition = { } /** used in the quick setup */ -export const conversionPresets: Preset = { - name: 'Send conversion events to Algolia', +export const purchasePreset: Preset = { + name: 'Send purchase events to Algolia', subscribe: conversionEvents.defaultSubscription as string, partnerAction: 'conversionEvents', mapping: defaultValues(conversionEvents.fields), type: 'automatic' } + +export const addToCartPreset: Preset = { + name: 'Send add-to-cart events to Algolia', + subscribe: 'type = "track" and event = "Product Added"', + partnerAction: 'conversionEvents', + mapping: defaultValues(getEventFields('addToCart')), + type: 'automatic' +} diff --git a/packages/destination-actions/src/destinations/algolia-insights/index.ts b/packages/destination-actions/src/destinations/algolia-insights/index.ts index 80d19c0595..0cf6afc222 100644 --- a/packages/destination-actions/src/destinations/algolia-insights/index.ts +++ b/packages/destination-actions/src/destinations/algolia-insights/index.ts @@ -3,12 +3,12 @@ import type { Settings } from './generated-types' import { productClickedEvents, productClickPresets } from './productClickedEvents' -import { conversionEvents, conversionPresets } from './conversionEvents' +import { conversionEvents, purchasePreset, addToCartPreset } from './conversionEvents' import { productViewedEvents, productViewedPresets } from './productViewedEvents' import { AlgoliaApiPermissions, algoliaApiPermissionsUrl } from './algolia-insight-api' -import { productAddedEvents, productAddedPresets } from './productAddedEvents' +import { productAddedEvents } from './productAddedEvents' import { productListFilteredEvents, productListFilteredPresets } from './productListFilteredEvents' @@ -64,9 +64,9 @@ const destination: DestinationDefinition = { type: 'automatic' }, productClickPresets, - conversionPresets, + purchasePreset, + addToCartPreset, productViewedPresets, - productAddedPresets, productListFilteredPresets ], actions: { diff --git a/packages/destination-actions/src/destinations/algolia-insights/productAddedEvents/index.ts b/packages/destination-actions/src/destinations/algolia-insights/productAddedEvents/index.ts index 1808889278..ee35bf355f 100644 --- a/packages/destination-actions/src/destinations/algolia-insights/productAddedEvents/index.ts +++ b/packages/destination-actions/src/destinations/algolia-insights/productAddedEvents/index.ts @@ -1,13 +1,12 @@ -import type { ActionDefinition, Preset } from '@segment/actions-core' -import { defaultValues } from '@segment/actions-core' +import type { ActionDefinition } from '@segment/actions-core' import type { Settings } from '../generated-types' import type { Payload } from './generated-types' import { AlgoliaBehaviourURL, AlgoliaConversionEvent, AlgoliaEventType } from '../algolia-insight-api' export const productAddedEvents: ActionDefinition = { - title: 'Product Added Events', + title: '[Deprecated] Product Added Events', description: - 'Product added events for ecommerce use cases for a customer adding an item to their cart. Query ID is optional and indicates that the event was the result of a search query.', + 'Product added events for ecommerce use cases for a customer adding an item to their cart. Query ID is optional and indicates that the event was the result of a search query. **Important** This Action is deprecated. Use the **Conversion Events** Action instead.', fields: { product: { label: 'Product ID', @@ -111,11 +110,3 @@ export const productAddedEvents: ActionDefinition = { }) } } - -export const productAddedPresets: Preset = { - name: 'Send product added events to Algolia', - subscribe: productAddedEvents.defaultSubscription as string, - partnerAction: 'productAddedEvents', - mapping: defaultValues(productAddedEvents.fields), - type: 'automatic' -} From fbee85f32edb1094558a02fa40ccf7332f0e5d80 Mon Sep 17 00:00:00 2001 From: Varadarajan V <109586712+varadarajan-tw@users.noreply.github.com> Date: Tue, 25 Mar 2025 16:07:19 +0530 Subject: [PATCH 10/78] Register twilio messaging omnichannel destination (#2829) --- packages/destination-actions/src/destinations/index.ts | 1 + .../src/destinations/twilio-messaging-omnichannel/index.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/destination-actions/src/destinations/index.ts b/packages/destination-actions/src/destinations/index.ts index fe611f0a4a..d3517529b3 100644 --- a/packages/destination-actions/src/destinations/index.ts +++ b/packages/destination-actions/src/destinations/index.ts @@ -194,6 +194,7 @@ register('673b62169b3342fbe0fc28da', './drip') register('674f2453916dadbd36d899dc', './attentive') register('674f23ece330374dc1ecc874', './twilio-messaging') register('67be4b2aef865ee6e0484fe5', './amazon-eventbridge') +register('67e285767bbb94fc090bf3c7', './twilio-messaging-omnichannel') function register(id: MetadataId, destinationPath: string) { // eslint-disable-next-line @typescript-eslint/no-var-requires diff --git a/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/index.ts b/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/index.ts index e547c95a63..0a2613a858 100644 --- a/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/index.ts +++ b/packages/destination-actions/src/destinations/twilio-messaging-omnichannel/index.ts @@ -4,7 +4,7 @@ import type { Settings } from './generated-types' import sendMessage from './sendMessage' const destination: DestinationDefinition = { - name: 'Twilio Messaging', + name: 'Twilio Messaging Omnichannel', slug: 'actions-twilio-messaging-omnichannel', mode: 'cloud', From 335e3daca1192c210a591340d8ca11023feae1b3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 16:16:48 +0530 Subject: [PATCH 11/78] Publish (#2830) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- packages/destination-actions/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/destination-actions/package.json b/packages/destination-actions/package.json index a8f83bde6f..25818404ee 100644 --- a/packages/destination-actions/package.json +++ b/packages/destination-actions/package.json @@ -1,7 +1,7 @@ { "name": "@segment/action-destinations", "description": "Destination Actions engine and definitions.", - "version": "3.378.0", + "version": "3.379.0", "repository": { "type": "git", "url": "https://github.com/segmentio/action-destinations", From 32f3226a82037882324ac5a7f30f87ec82a96b71 Mon Sep 17 00:00:00 2001 From: Nick Aguilar Date: Tue, 25 Mar 2025 15:07:40 -0700 Subject: [PATCH 12/78] Support min/max string length validation (#2831) * Passes on the InputField min and max properties to JSONSchema for validation * Adds unit tests and updated comment descriptions for string field min/max --- .../src/__tests__/schema-validation.test.ts | 21 +++++++++++++++++++ .../destination-kit/fields-to-jsonschema.ts | 8 +++++++ packages/core/src/destination-kit/types.ts | 10 +++++++-- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/core/src/__tests__/schema-validation.test.ts b/packages/core/src/__tests__/schema-validation.test.ts index 4ae9553e46..47d32d28af 100644 --- a/packages/core/src/__tests__/schema-validation.test.ts +++ b/packages/core/src/__tests__/schema-validation.test.ts @@ -1566,6 +1566,27 @@ describe('validateSchema', () => { expect(validateSchema(greater_than_max_payload, min_max_schema, { throwIfInvalid: false })).toBeFalsy() }) + it('should validate min/max for string type fields', async () => { + const smallString = 'a' + const longString = 'a'.repeat(100) + const stringEqualToMin = 'a'.repeat(5) + const stringEqualToMax = 'a'.repeat(10) + + const min_max_schema = fieldsToJsonSchema({ + limitedString: { + type: 'string', + label: 'Test Field', + minimum: 5, + maximum: 10 + } + }) + + expect(validateSchema({ limitedString: stringEqualToMin }, min_max_schema)).toBeTruthy() + expect(validateSchema({ limitedString: stringEqualToMax }, min_max_schema)).toBeTruthy() + expect(validateSchema({ limitedString: smallString }, min_max_schema, { throwIfInvalid: false })).toBeFalsy() + expect(validateSchema({ limitedString: longString }, min_max_schema, { throwIfInvalid: false })).toBeFalsy() + }) + it('should allow exempted properties', () => { const payload = { a: 'a', diff --git a/packages/core/src/destination-kit/fields-to-jsonschema.ts b/packages/core/src/destination-kit/fields-to-jsonschema.ts index a08bb2b19d..e3dee030dd 100644 --- a/packages/core/src/destination-kit/fields-to-jsonschema.ts +++ b/packages/core/src/destination-kit/fields-to-jsonschema.ts @@ -310,6 +310,14 @@ export function fieldsToJsonSchema( schema.format = 'password' } else if (field.type === 'text') { schema.format = 'text' + } else if (field.type === 'string') { + const { minimum = null, maximum = null } = field as InputField + if (minimum) { + schema.minLength = (field as InputField)?.minimum + } + if (maximum) { + schema.maxLength = (field as InputField)?.maximum + } } else if (field.type === 'number') { const { minimum = null, maximum = null } = field as InputField if (minimum) { diff --git a/packages/core/src/destination-kit/types.ts b/packages/core/src/destination-kit/types.ts index 3c44e55a6e..ff6e81fddf 100644 --- a/packages/core/src/destination-kit/types.ts +++ b/packages/core/src/destination-kit/types.ts @@ -257,9 +257,15 @@ export interface InputField extends InputFieldJSONSchema { */ disabledInputMethods?: FieldInputMethods[] - /** Minimum value for a field of type 'number' */ + /** + * Minimum value for a field of type 'number' + * When applied to a string field the minimum length of the string + * */ minimum?: number - /** Maximum value for a field of type 'number' */ + /** + * Maximum value for a field of type 'number' + * When applied to a string field the maximum length of the string + */ maximum?: number } From 4a959ec9051958297202775528938e8c039927b7 Mon Sep 17 00:00:00 2001 From: Nick Aguilar Date: Wed, 26 Mar 2025 10:30:01 -0700 Subject: [PATCH 13/78] [Salesforce Marketing Cloud] V2 Data Extension mapping save hook (#2826) * Scaffolds new v2 actions * Hook Definition and fields from the original branch * dataExtensionV2 action filled out with the action definition from original branch * contactdataExtensionV2 action filled out with the action definition from original branch * Generates types * Fixes build. Introduces an upsertRowsV2 function for the new actions * Declares keys and values fields as dynamic only in the V2 actions to ensure no new behavior for the existing actions * Adds a little (V2) suffix to the titles for the new actions * Improves error message when users encounter auth issues for data extension id dynamic field * When a user doesn't have auth permission to lookup a data extension, use the ID they provide without verifying it. Provide improved error messaging * Throw an error if no data extension is connected to the mapping --- .../contactDataExtensionV2/generated-types.ts | 192 +++++++ .../contactDataExtensionV2/index.ts | 78 +++ .../dataExtensionV2/generated-types.ts | 188 ++++++ .../dataExtensionV2/index.ts | 62 ++ .../salesforce-marketing-cloud/index.ts | 9 +- .../sfmc-operations.ts | 543 +++++++++++++++++- .../sfmc-properties.ts | 165 +++++- 7 files changed, 1232 insertions(+), 5 deletions(-) create mode 100644 packages/destination-actions/src/destinations/salesforce-marketing-cloud/contactDataExtensionV2/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/salesforce-marketing-cloud/contactDataExtensionV2/index.ts create mode 100644 packages/destination-actions/src/destinations/salesforce-marketing-cloud/dataExtensionV2/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/salesforce-marketing-cloud/dataExtensionV2/index.ts diff --git a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/contactDataExtensionV2/generated-types.ts b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/contactDataExtensionV2/generated-types.ts new file mode 100644 index 0000000000..f6cde85008 --- /dev/null +++ b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/contactDataExtensionV2/generated-types.ts @@ -0,0 +1,192 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * The primary key(s) that uniquely identify a row in the data extension. On the left-hand side, input the SFMC key name. On the right-hand side, map the Segment field that contains the corresponding value. When multiple primary keys are provided, SFMC will update an existing row if all primary keys match, otherwise a new row will be created + */ + keys: { + /** + * The unique identifier that you assign to a contact. Contact Key must be a Primary Key in the data extension that contains contact information. + */ + contactKey: string + [k: string]: unknown + } + /** + * The fields in the data extension that contain data about a contact, such as Email, Last Name, etc. Fields must be created in the data extension before sending data for it. On the left-hand side, input the SFMC field name exactly how it appears in the data extension. On the right-hand side, map the Segment field that contains the corresponding value. + */ + values: { + [k: string]: unknown + } + /** + * If true, data is batched before sending to the SFMC Data Extension. + */ + enable_batching?: boolean + /** + * Maximum number of events to include in each batch. Actual batch sizes may be lower. + */ + batch_size?: number +} +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface RetlOnMappingSaveInputs { + /** + * Whether to create a new data extension or select an existing one for data delivery. + */ + operation: string + /** + * The identifier for the data extension. + */ + dataExtensionId?: string + /** + * The identifier for the folder that contains the data extension. + */ + categoryId?: string + /** + * The name of the data extension. + */ + name?: string + /** + * The description of the data extension. + */ + description?: string + /** + * Indicates whether the custom object can be used to send messages. If the value of this property is true, then the custom object is sendable + */ + isSendable?: boolean + /** + * The field on this data extension which is sendable. This must be a field that is present on this data extension. + */ + sendableCustomObjectField?: string + /** + * The relationship with "Subscribers" for the Sendable Custom Object Field. + */ + sendableSubscriberField?: string + /** + * A list of fields to create in the data extension. + */ + columns?: { + /** + * The name of the field. + */ + name: string + /** + * The data type of the field. + */ + type: string + /** + * Whether the field can be null. + */ + isNullable: boolean + /** + * Whether the field is a primary key. + */ + isPrimaryKey: boolean + /** + * The length of the field. Required for non-boolean fields + */ + length?: number + /** + * The scale of the field. Required for Decimal fields + */ + scale?: number + /** + * The description of the field. + */ + description?: string + [k: string]: unknown + }[] +} +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface RetlOnMappingSaveOutputs { + /** + * The identifier for the data extension. + */ + id: string + /** + * The name of the data extension. + */ + name: string +} +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface OnMappingSaveInputs { + /** + * Whether to create a new data extension or select an existing one for data delivery. + */ + operation: string + /** + * The identifier for the data extension. + */ + dataExtensionId?: string + /** + * The identifier for the folder that contains the data extension. + */ + categoryId?: string + /** + * The name of the data extension. + */ + name?: string + /** + * The description of the data extension. + */ + description?: string + /** + * Indicates whether the custom object can be used to send messages. If the value of this property is true, then the custom object is sendable + */ + isSendable?: boolean + /** + * The field on this data extension which is sendable. This must be a field that is present on this data extension. + */ + sendableCustomObjectField?: string + /** + * The relationship with "Subscribers" for the Sendable Custom Object Field. + */ + sendableSubscriberField?: string + /** + * A list of fields to create in the data extension. + */ + columns?: { + /** + * The name of the field. + */ + name: string + /** + * The data type of the field. + */ + type: string + /** + * Whether the field can be null. + */ + isNullable: boolean + /** + * Whether the field is a primary key. + */ + isPrimaryKey: boolean + /** + * The length of the field. Required for non-boolean fields + */ + length?: number + /** + * The scale of the field. Required for Decimal fields + */ + scale?: number + /** + * The description of the field. + */ + description?: string + [k: string]: unknown + }[] +} +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface OnMappingSaveOutputs { + /** + * The identifier for the data extension. + */ + id: string + /** + * The name of the data extension. + */ + name: string +} diff --git a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/contactDataExtensionV2/index.ts b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/contactDataExtensionV2/index.ts new file mode 100644 index 0000000000..66c25f13c3 --- /dev/null +++ b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/contactDataExtensionV2/index.ts @@ -0,0 +1,78 @@ +import { IntegrationError, ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' +import { keys, enable_batching, batch_size, values_contactFields, dataExtensionHook } from '../sfmc-properties' +import { executeUpsertWithMultiStatus, getDataExtensionFields, upsertRowsV2 } from '../sfmc-operations' + +const action: ActionDefinition = { + title: 'Send Contact to Data Extension (V2)', + defaultSubscription: 'type = "identify"', + description: 'Upsert contact data as rows into an existing data extension in Salesforce Marketing Cloud.', + fields: { + keys: { + ...keys, + dynamic: true, + properties: { + contactKey: { + label: 'Contact Key', + description: + 'The unique identifier that you assign to a contact. Contact Key must be a Primary Key in the data extension that contains contact information.', + type: 'string', + required: true + } + }, + default: { + contactKey: { '@path': '$.userId' } + } + }, + values: { ...values_contactFields, dynamic: true }, + enable_batching: enable_batching, + batch_size: batch_size + }, + dynamicFields: { + keys: { + __keys__: async (request, { settings, payload }) => { + const dataExtensionId = + (payload as any).onMappingSave?.outputs.id || (payload as any).retlOnMappingSave?.outputs?.id || '' + return await getDataExtensionFields(request, settings.subdomain, settings, dataExtensionId, true) + } + }, + values: { + __keys__: async (request, { settings, payload }) => { + const dataExtensionId = + (payload as any).onMappingSave?.outputs?.id || (payload as any).retlOnMappingSave?.outputs?.id || '' + return await getDataExtensionFields(request, settings.subdomain, settings, dataExtensionId, false) + } + } + }, + hooks: { + retlOnMappingSave: { + ...dataExtensionHook + }, + onMappingSave: { + ...dataExtensionHook + } + }, + perform: async (request, { settings, payload, hookOutputs }) => { + const dataExtensionId: string = + hookOutputs?.onMappingSave?.outputs?.id || hookOutputs?.retlOnMappingSave?.outputs?.id + + if (!dataExtensionId) { + throw new IntegrationError('No Data Extension Connected', 'INVALID_CONFIGURATION', 400) + } + + return upsertRowsV2(request, settings.subdomain, [payload], dataExtensionId) + }, + performBatch: async (request, { settings, payload, hookOutputs }) => { + const dataExtensionId: string = + hookOutputs?.onMappingSave?.outputs?.id || hookOutputs?.retlOnMappingSave?.outputs?.id + + if (!dataExtensionId) { + throw new IntegrationError('No Data Extension Connected', 'INVALID_CONFIGURATION', 400) + } + + return executeUpsertWithMultiStatus(request, settings.subdomain, payload, dataExtensionId) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/dataExtensionV2/generated-types.ts b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/dataExtensionV2/generated-types.ts new file mode 100644 index 0000000000..8873c1ea3c --- /dev/null +++ b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/dataExtensionV2/generated-types.ts @@ -0,0 +1,188 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * The primary key(s) that uniquely identify a row in the data extension. On the left-hand side, input the SFMC key name. On the right-hand side, map the Segment field that contains the corresponding value. When multiple primary keys are provided, SFMC will update an existing row if all primary keys match, otherwise a new row will be created + */ + keys: { + [k: string]: unknown + } + /** + * The fields in the data extension that contain data about an event, such as Product Name, Revenue, Event Time, etc. Fields must be created in the data extension before sending data for it. On the left-hand side, input the SFMC field name exactly how it appears in the data extension. On the right-hand side, map the Segment field that contains the corresponding value. + */ + values: { + [k: string]: unknown + } + /** + * If true, data is batched before sending to the SFMC Data Extension. + */ + enable_batching?: boolean + /** + * Maximum number of events to include in each batch. Actual batch sizes may be lower. + */ + batch_size?: number +} +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface RetlOnMappingSaveInputs { + /** + * Whether to create a new data extension or select an existing one for data delivery. + */ + operation: string + /** + * The identifier for the data extension. + */ + dataExtensionId?: string + /** + * The identifier for the folder that contains the data extension. + */ + categoryId?: string + /** + * The name of the data extension. + */ + name?: string + /** + * The description of the data extension. + */ + description?: string + /** + * Indicates whether the custom object can be used to send messages. If the value of this property is true, then the custom object is sendable + */ + isSendable?: boolean + /** + * The field on this data extension which is sendable. This must be a field that is present on this data extension. + */ + sendableCustomObjectField?: string + /** + * The relationship with "Subscribers" for the Sendable Custom Object Field. + */ + sendableSubscriberField?: string + /** + * A list of fields to create in the data extension. + */ + columns?: { + /** + * The name of the field. + */ + name: string + /** + * The data type of the field. + */ + type: string + /** + * Whether the field can be null. + */ + isNullable: boolean + /** + * Whether the field is a primary key. + */ + isPrimaryKey: boolean + /** + * The length of the field. Required for non-boolean fields + */ + length?: number + /** + * The scale of the field. Required for Decimal fields + */ + scale?: number + /** + * The description of the field. + */ + description?: string + [k: string]: unknown + }[] +} +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface RetlOnMappingSaveOutputs { + /** + * The identifier for the data extension. + */ + id: string + /** + * The name of the data extension. + */ + name: string +} +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface OnMappingSaveInputs { + /** + * Whether to create a new data extension or select an existing one for data delivery. + */ + operation: string + /** + * The identifier for the data extension. + */ + dataExtensionId?: string + /** + * The identifier for the folder that contains the data extension. + */ + categoryId?: string + /** + * The name of the data extension. + */ + name?: string + /** + * The description of the data extension. + */ + description?: string + /** + * Indicates whether the custom object can be used to send messages. If the value of this property is true, then the custom object is sendable + */ + isSendable?: boolean + /** + * The field on this data extension which is sendable. This must be a field that is present on this data extension. + */ + sendableCustomObjectField?: string + /** + * The relationship with "Subscribers" for the Sendable Custom Object Field. + */ + sendableSubscriberField?: string + /** + * A list of fields to create in the data extension. + */ + columns?: { + /** + * The name of the field. + */ + name: string + /** + * The data type of the field. + */ + type: string + /** + * Whether the field can be null. + */ + isNullable: boolean + /** + * Whether the field is a primary key. + */ + isPrimaryKey: boolean + /** + * The length of the field. Required for non-boolean fields + */ + length?: number + /** + * The scale of the field. Required for Decimal fields + */ + scale?: number + /** + * The description of the field. + */ + description?: string + [k: string]: unknown + }[] +} +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface OnMappingSaveOutputs { + /** + * The identifier for the data extension. + */ + id: string + /** + * The name of the data extension. + */ + name: string +} diff --git a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/dataExtensionV2/index.ts b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/dataExtensionV2/index.ts new file mode 100644 index 0000000000..dda80f873e --- /dev/null +++ b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/dataExtensionV2/index.ts @@ -0,0 +1,62 @@ +import { ActionDefinition, IntegrationError } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' +import { keys, enable_batching, batch_size, values_dataExtensionFields, dataExtensionHook } from '../sfmc-properties' +import { executeUpsertWithMultiStatus, getDataExtensionFields, upsertRowsV2 } from '../sfmc-operations' + +const action: ActionDefinition = { + title: 'Send Event to Data Extension (V2)', + description: 'Upsert events as rows into an existing data extension in Salesforce Marketing Cloud.', + fields: { + keys: { ...keys, required: true, dynamic: true }, + values: { ...values_dataExtensionFields, dynamic: true }, + enable_batching: enable_batching, + batch_size: batch_size + }, + dynamicFields: { + keys: { + __keys__: async (request, { settings, payload }) => { + const dataExtensionId = + (payload as any).onMappingSave?.outputs?.id || (payload as any).retlOnMappingSave?.outputs?.id || '' + return await getDataExtensionFields(request, settings.subdomain, settings, dataExtensionId, true) + } + }, + values: { + __keys__: async (request, { settings, payload }) => { + const dataExtensionId = + (payload as any).onMappingSave?.outputs?.id || (payload as any).retlOnMappingSave?.outputs?.id || '' + return await getDataExtensionFields(request, settings.subdomain, settings, dataExtensionId, false) + } + } + }, + hooks: { + retlOnMappingSave: { + ...dataExtensionHook + }, + onMappingSave: { + ...dataExtensionHook + } + }, + perform: async (request, { settings, payload, hookOutputs }) => { + const dataExtensionId: string = + hookOutputs?.onMappingSave?.outputs?.id || hookOutputs?.retlOnMappingSave?.outputs?.id + + if (!dataExtensionId) { + throw new IntegrationError('No Data Extension Connected', 'INVALID_CONFIGURATION', 400) + } + + return upsertRowsV2(request, settings.subdomain, [payload], dataExtensionId) + }, + performBatch: async (request, { settings, payload, hookOutputs }) => { + const dataExtensionId: string = + hookOutputs?.onMappingSave?.outputs?.id || hookOutputs?.retlOnMappingSave?.outputs?.id + + if (!dataExtensionId) { + throw new IntegrationError('No Data Extension Connected', 'INVALID_CONFIGURATION', 400) + } + + return executeUpsertWithMultiStatus(request, settings.subdomain, payload, dataExtensionId) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/index.ts b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/index.ts index ccb5db7a45..0cb46148ac 100644 --- a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/index.ts +++ b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/index.ts @@ -5,6 +5,11 @@ import dataExtension from './dataExtension' import contactDataExtension from './contactDataExtension' import apiEvent from './apiEvent' +// These actions include an actions hook which handles configuring the connected +// data extension. They are independent from the original actions to support a slow rollout. +import dataExtensionV2 from './dataExtensionV2' +import contactDataExtensionV2 from './contactDataExtensionV2' + interface RefreshTokenResponse { access_token: string } @@ -71,7 +76,9 @@ const destination: DestinationDefinition = { contact, dataExtension, contactDataExtension, - apiEvent + apiEvent, + dataExtensionV2, + contactDataExtensionV2 } } diff --git a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-operations.ts b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-operations.ts index 194c906724..3442632fbb 100644 --- a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-operations.ts +++ b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-operations.ts @@ -3,11 +3,18 @@ import { MultiStatusResponse, JSONLikeObject, ModifiedResponse, - IntegrationError + IntegrationError, + ActionHookResponse, + DynamicFieldResponse, + DynamicFieldError, + DynamicFieldItem } from '@segment/actions-core' import { Payload as payload_dataExtension } from './dataExtension/generated-types' import { Payload as payload_contactDataExtension } from './contactDataExtension/generated-types' import { ErrorResponse } from './types' +import { OnMappingSaveInputs } from './dataExtensionV2/generated-types' +import { Settings } from './generated-types' +import { xml2js } from 'xml-js' function generateRows(payloads: payload_dataExtension[] | payload_contactDataExtension[]): Record[] { const rows: Record[] = [] @@ -47,16 +54,43 @@ export function upsertRows( } } +export function upsertRowsV2( + request: RequestClient, + subdomain: String, + payloads: payload_dataExtension[] | payload_contactDataExtension[], + dataExtensionId: string +) { + if (!dataExtensionId) { + throw new IntegrationError( + `In order to send an event to a data extension Data Extension ID must be defined.`, + 'Misconfigured required field', + 400 + ) + } + + const rows = generateRows(payloads) + return request(`https://${subdomain}.rest.marketingcloudapis.com/hub/v1/dataevents/${dataExtensionId}/rowset`, { + method: 'POST', + json: rows + }) +} + export async function executeUpsertWithMultiStatus( request: RequestClient, subdomain: String, - payloads: payload_dataExtension[] | payload_contactDataExtension[] + payloads: payload_dataExtension[] | payload_contactDataExtension[], + dataExtensionId?: string ): Promise { const multiStatusResponse = new MultiStatusResponse() let response: ModifiedResponse | undefined const rows = generateRows(payloads) try { - response = await upsertRows(request, subdomain, payloads) + if (dataExtensionId) { + response = await upsertRowsV2(request, subdomain, payloads, dataExtensionId) + } else { + response = await upsertRows(request, subdomain, payloads) + } + if (response) { const responseData = response.data as JSONLikeObject[] payloads.forEach((_, index) => { @@ -114,3 +148,506 @@ export async function executeUpsertWithMultiStatus( } return multiStatusResponse } + +interface DataExtensionCreationResponse { + data: { + id?: string + name?: string + key?: string + message?: string + errorcode?: number + } +} + +interface DataExtensionSelectionResponse { + data: { + id?: string + name?: string + key?: string + } +} + +interface DataExtensionSearchResponse { + count: number + items: { + id: string + name: string + key: string + }[] +} + +interface DataExtensionFieldsResponse { + id: string + fields: { + name: string + type: string + isPrimaryKey: boolean + }[] +} + +interface RefreshTokenResponse { + access_token: string + soap_instance_url: string +} + +interface SoapResponseResult { + ID: { + _text: string + } + Name: { + _text: string + } + ContentType: { + _text: string + } +} + +const getAccessToken = async ( + request: RequestClient, + settings: Settings +): Promise<{ accessToken: string; soapInstanceUrl: string }> => { + const baseUrl = `https://${settings.subdomain}.auth.marketingcloudapis.com/v2/token` + const res = await request(`${baseUrl}`, { + method: 'POST', + body: new URLSearchParams({ + account_id: settings.account_id, + client_id: settings.client_id, + client_secret: settings.client_secret, + grant_type: 'client_credentials' + }) + }) + + return { accessToken: res.data.access_token, soapInstanceUrl: res.data.soap_instance_url } +} + +const dataExtensionRequest = async ( + request: RequestClient, + hookInputs: OnMappingSaveInputs, + auth: { subdomain: string; accessToken: string } +): Promise<{ id?: string; key?: string; error?: string }> => { + if (!hookInputs) { + return { id: '', key: '', error: 'No inputs provided' } + } + + if (!hookInputs.columns) { + return { id: '', key: '', error: 'No columns provided' } + } + + const fields = hookInputs.columns.map((column, i) => { + return { + name: column.name, + type: column.type, + isNullable: column.isNullable, + isPrimaryKey: column.isPrimaryKey, + length: column.length, + scale: column.scale, + description: column.description || '', + // these are required but we don't give the user an option + ordinal: i, + isTemplateField: false, + isHidden: false, + isReadOnly: false, + isInheritable: false, + isOverridable: false, + mustOverride: false + } + }) + + try { + const response = await request( + `https://${auth.subdomain}.rest.marketingcloudapis.com/data/v1/customobjects`, + { + method: 'POST', + json: { + name: hookInputs.name, + description: hookInputs.description, + categoryId: hookInputs.categoryId, + isSendable: hookInputs.isSendable, + sendableCustomObjectField: hookInputs.sendableCustomObjectField, + sendableSubscriberField: hookInputs.sendableSubscriberField, + fields + }, + headers: { + authorization: `Bearer ${auth.accessToken}` + } + } + ) + + if (response.status !== 201) { + return { id: '', key: '', error: `Failed to create Data Extension` } + } + + return { + id: (response as DataExtensionCreationResponse).data.id, + key: (response as DataExtensionCreationResponse).data.key + } + } catch (error) { + return { id: '', key: '', error: error.response.data.message } + } +} + +async function createDataExtension( + request: RequestClient, + subdomain: string, + hookInputs: OnMappingSaveInputs, + settings: Settings +): Promise> { + if (!hookInputs) { + return { + error: { message: 'No inputs provided', code: 'ERROR' } + } + } + + const { accessToken } = await getAccessToken(request, settings) + + const { id, key, error } = await dataExtensionRequest(request, hookInputs, { subdomain, accessToken }) + + if (error || !id || !key) { + return { + error: { message: error || 'Unknown Error', code: 'ERROR' } + } + } + + return { + successMessage: `Data Extension ${hookInputs.name} created successfully with External Key ${key}`, + savedData: { + id, + name: hookInputs.name! + } + } +} + +const selectDataExtensionRequest = async ( + request: RequestClient, + hookInputs: OnMappingSaveInputs, + auth: { subdomain: string; accessToken: string } +): Promise<{ id?: string; name?: string; error?: string }> => { + if (!hookInputs) { + return { id: '', name: '', error: 'No inputs provided' } + } + + if (!hookInputs.dataExtensionId) { + return { id: '', name: '', error: 'No Data Extension Id provided' } + } + + try { + const response = await request( + `https://${auth.subdomain}.rest.marketingcloudapis.com/data/v1/customobjects/${hookInputs.dataExtensionId}`, + { + method: 'GET', + headers: { + authorization: `Bearer ${auth.accessToken}` + } + } + ) + + if (response.status !== 200) { + return { id: '', name: '', error: `Failed to select Data Extension` } + } + + return { + id: (response as DataExtensionSelectionResponse).data.id, + name: (response as DataExtensionSelectionResponse).data.name + } + } catch (err) { + const errorCode: string = + typeof err.response.data.errorcode === 'number' + ? err.response.data.errorcode.toString() + : err.response.data.errorcode + const errorMessage = err.response.data.message + + if (errorCode === '20002') { + return { + id: hookInputs.dataExtensionId, + name: 'Unknown (Insufficient Authentication Error)', + error: `${errorMessage} To resolve this authentication issue refer to the required permissions under 'Getting Started' in the documentation at https://segment.com/docs/connections/destinations/catalog/actions-salesforce-marketing-cloud/` + } + } + + return { id: '', name: '', error: err.response.data.message } + } +} + +async function selectDataExtension( + request: RequestClient, + subdomain: string, + hookInputs: OnMappingSaveInputs, + settings: Settings +): Promise> { + if (!hookInputs) { + return { + error: { message: 'No inputs provided', code: 'ERROR' } + } + } + + const { accessToken } = await getAccessToken(request, settings) + + const { id, name, error } = await selectDataExtensionRequest(request, hookInputs, { subdomain, accessToken }) + if (error && id) { + return { + error: { message: error, code: 'Authentication Error' }, + savedData: { id, name: name! } + } + } + + if (error || !id) { + return { + error: { message: error || 'Unknown Error', code: 'ERROR' } + } + } + + return { + successMessage: `Data Extension ${name} selected successfully with External ID ${id}`, + savedData: { + id, + name: name! + } + } +} + +export const selectOrCreateDataExtension = async ( + request: RequestClient, + subdomain: string, + hookInputs: OnMappingSaveInputs, + settings: Settings +): Promise> => { + if (hookInputs.operation === 'create') { + return await createDataExtension(request, subdomain, hookInputs, settings) + } else if (hookInputs.operation === 'select') { + return await selectDataExtension(request, subdomain, hookInputs, settings) + } + + return { + error: { message: 'Invalid operation', code: 'ERROR' } + } +} + +const getDataExtensionsRequest = async ( + request: RequestClient, + searchQuery: string, + auth: { subdomain: string; accessToken: string } +): Promise<{ results?: DynamicFieldItem[]; error?: DynamicFieldError }> => { + try { + const response = await request( + `https://${auth.subdomain}.rest.marketingcloudapis.com/data/v1/customobjects`, + { + method: 'get', + searchParams: { + $search: searchQuery + }, + headers: { + authorization: `Bearer ${auth.accessToken}` + } + } + ) + + if (response.status !== 200) { + return { error: { message: 'Failed to fetch Data Extensions', code: 'BAD_REQUEST' } } + } + + const choices = response.data.items + + return { + results: choices.map((choice) => { + return { + value: choice.id, + label: choice.name + } + }) + } + } catch (err) { + const errorCode: string = + typeof err.response.data.errorcode === 'number' + ? err.response.data.errorcode.toString() + : err.response.data.errorcode + const errorMessage = err.response.data.message + + if (errorCode === '20002') { + return { + error: { + message: `${errorMessage}. Please input a data extension ID manually to configure your mapping. To resolve this authentication issue refer to the required permissions under 'Getting Started' in the documentation at https://segment.com/docs/connections/destinations/catalog/actions-salesforce-marketing-cloud/`, + code: errorCode + } + } + } + + return { error: { message: err.response.data.message, code: errorCode || 'BAD_REQUEST' } } + } +} + +export const getDataExtensions = async ( + request: RequestClient, + subdomain: string, + settings: Settings, + query?: string +): Promise => { + let searchQuery = '_' + if (query && query !== '') { + searchQuery = query + } + + const { accessToken } = await getAccessToken(request, settings) + + const { results, error } = await getDataExtensionsRequest(request, searchQuery, { subdomain, accessToken }) + + if (error) { + return { + choices: [], + error: error + } + } + + if (!results || (Array.isArray(results) && results.length === 0)) { + return { + choices: [], + error: { message: 'No Data Extensions found', code: 'NOT_FOUND' } + } + } + + return { + choices: results + } +} + +const getDataExtensionFieldsRequest = async ( + request: RequestClient, + dataExtensionId: string, + auth: { subdomain: string; accessToken: string }, + primaryKey: boolean +): Promise<{ results?: DynamicFieldItem[]; error?: DynamicFieldError }> => { + try { + const response = await request( + `https://${auth.subdomain}.rest.marketingcloudapis.com/data/v1/customobjects/${dataExtensionId}/fields`, + { + method: 'GET', + headers: { + authorization: `Bearer ${auth.accessToken}` + } + } + ) + + if (response.status !== 200) { + return { error: { message: 'Failed to fetch Data Extension fields', code: 'BAD_REQUEST' } } + } + + const filteredItems = response.data.fields.filter((field) => field.isPrimaryKey === primaryKey) + + const choices = filteredItems.map((field) => { + return { value: field.name, label: field.name } + }) + + return { results: choices } + } catch (err) { + return { error: { message: err.response.data.message, code: 'BAD_REQUEST' } } + } +} + +export const getDataExtensionFields = async ( + request: RequestClient, + subdomain: string, + settings: Settings, + dataExtensionID: string, + primaryKey: boolean +): Promise => { + if (!dataExtensionID) { + return { choices: [], error: { message: 'No Data Extension ID provided', code: 'BAD_REQUEST' } } + } + const { accessToken } = await getAccessToken(request, settings) + + const { results, error } = await getDataExtensionFieldsRequest( + request, + dataExtensionID, + { subdomain, accessToken }, + primaryKey + ) + + if (error) { + return { + choices: [], + error: error + } + } + + if (!results || (Array.isArray(results) && results.length === 0)) { + return { + choices: [], + error: { message: 'No Data Extension fields found', code: 'NOT_FOUND' } + } + } + + return { + choices: results + } +} + +const getCategoriesRequest = async ( + request: RequestClient, + auth: { soapInstanceUrl: string; accessToken: string } +): Promise<{ results?: DynamicFieldItem[]; error?: DynamicFieldError }> => { + try { + const response = await request(`${auth.soapInstanceUrl}/Service.asmx`, { + method: 'POST', + headers: { + 'Content-Type': 'text/xml', + SOAPAction: 'Retrieve' + }, + body: ` + + + ${auth.accessToken} + + + + + DataFolder + ID + Name + ContentType + + + + + ` + }) + + const convert: any = xml2js(response.content, { compact: true }) + const items = (convert['soap:Envelope']['soap:Body']['RetrieveResponseMsg']['Results'] as SoapResponseResult[]).map( + (item) => { + const type = item.ContentType._text + + return { + label: item.Name._text, + value: item.ID._text, + description: `ContentType: ${type}` + } + } + ) + + return { + results: items + } + } catch (err) { + return { error: { message: err.response.data.message, code: 'BAD_REQUEST' } } + } +} + +export const getCategories = async (request: RequestClient, settings: Settings): Promise => { + const { accessToken, soapInstanceUrl } = await getAccessToken(request, settings) + + const { results, error } = await getCategoriesRequest(request, { soapInstanceUrl, accessToken }) + + if (error) { + return { + choices: [], + error: error + } + } + + return { + choices: results || [] + } +} diff --git a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-properties.ts b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-properties.ts index 5dc3de26eb..f3562e16ce 100644 --- a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-properties.ts +++ b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-properties.ts @@ -1,4 +1,6 @@ -import { InputField } from '@segment/actions-core/destination-kit/types' +import { ActionHookDefinition } from '@segment/actions-core/destination-kit' +import { InputField, FieldTypeName, DependsOnConditions } from '@segment/actions-core/destination-kit/types' +import { getCategories, getDataExtensions, selectOrCreateDataExtension } from './sfmc-operations' export const contactKey: InputField = { label: 'Contact Key', @@ -94,3 +96,164 @@ export const batch_size: InputField = { * */ default: 10 } + +// Scripting for the create/select existing data extension flow +const CREATE_OPERATION: DependsOnConditions = { + match: 'all', + conditions: [{ fieldKey: 'operation', operator: 'is', value: 'create' }] +} + +const SELECT_OPERATION: DependsOnConditions = { + match: 'all', + conditions: [{ fieldKey: 'operation', operator: 'is', value: 'select' }] +} + +const IS_SENDABLE: DependsOnConditions = { + match: 'all', + conditions: [{ fieldKey: 'isSendable', operator: 'is', value: true }] +} + +export const dataExtensionHook: ActionHookDefinition = { + label: 'Create or Select Data Extension', + description: 'Connect to an existing data extension or create a new one in Salesforce Marketing Cloud.', + inputFields: { + operation: { + label: 'Operation', + description: 'Whether to create a new data extension or select an existing one for data delivery.', + type: 'string', + choices: [ + { label: 'Create a new Data Extension', value: 'create' }, + { label: 'Select an existing Data Extension', value: 'select' } + ], + required: true + }, + dataExtensionId: { + label: 'Data Extension ID', + description: 'The identifier for the data extension.', + type: 'string', + depends_on: SELECT_OPERATION, + dynamic: async (request, { dynamicFieldContext, settings }) => { + const query = dynamicFieldContext?.query + return await getDataExtensions(request, settings.subdomain, settings, query) + } + }, + categoryId: { + label: 'Category ID (Folder ID)', + description: 'The identifier for the folder that contains the data extension.', + type: 'string', + required: CREATE_OPERATION, + depends_on: CREATE_OPERATION, + dynamic: async (request, { settings }) => { + return await getCategories(request, settings) + } + }, + name: { + label: 'Data Extension Name', + description: 'The name of the data extension.', + type: 'string', + required: CREATE_OPERATION, + depends_on: CREATE_OPERATION + }, + description: { + label: 'Data Extension Description', + description: 'The description of the data extension.', + type: 'string', + depends_on: CREATE_OPERATION + }, + isSendable: { + label: 'Is Sendable', + type: 'boolean', + depends_on: CREATE_OPERATION, + description: + 'Indicates whether the custom object can be used to send messages. If the value of this property is true, then the custom object is sendable' + }, + sendableCustomObjectField: { + label: 'Sendable Custom Object Field', + description: + 'The field on this data extension which is sendable. This must be a field that is present on this data extension.', + type: 'string', + depends_on: IS_SENDABLE, + required: IS_SENDABLE + }, + sendableSubscriberField: { + label: 'Sendable Subscriber Field', + description: 'The relationship with "Subscribers" for the Sendable Custom Object Field.', + type: 'string', + depends_on: IS_SENDABLE, + required: IS_SENDABLE, + choices: [ + { label: 'Subscriber Key', value: '_SubscriberKey' }, + { label: 'Subscriber ID', value: '_SubscriberID' } + ] + }, + columns: { + label: 'Data Extension Fields', + description: 'A list of fields to create in the data extension.', + type: 'object' as FieldTypeName, + multiple: true, + defaultObjectUI: 'arrayeditor', + additionalProperties: true, + required: CREATE_OPERATION, + depends_on: CREATE_OPERATION, + properties: { + name: { + label: 'Field Name', + description: 'The name of the field.', + type: 'string', + required: true + }, + type: { + label: 'Field Type', + description: 'The data type of the field.', + type: 'string', + required: true, + choices: ['Text', 'Number', 'Date', 'Boolean', 'EmailAddress', 'Phone', 'Decimal', 'Locale'] + }, + isNullable: { + label: 'Is Nullable', + description: 'Whether the field can be null.', + type: 'boolean', + required: true + }, + isPrimaryKey: { + label: 'Is Primary Key', + description: 'Whether the field is a primary key.', + type: 'boolean', + required: true + }, + length: { + label: 'Field Length', + description: 'The length of the field. Required for non-boolean fields', + type: 'integer' + }, + scale: { + label: 'Decimal Scale', + description: 'The scale of the field. Required for Decimal fields', + type: 'integer' + }, + description: { + label: 'Field Description', + description: 'The description of the field.', + type: 'string' + } + } + } + }, + performHook: async (request, { settings, hookInputs }) => { + return await selectOrCreateDataExtension(request, settings.subdomain, hookInputs, settings) + }, + outputTypes: { + id: { + label: 'Data Extension ID', + description: 'The identifier for the data extension.', + type: 'string', + required: true + }, + name: { + label: 'Data Extension Name', + description: 'The name of the data extension.', + type: 'string', + required: true + } + } +} From faefa28aaa533ad8b61b868e4a1bfc3a501d98c1 Mon Sep 17 00:00:00 2001 From: mayur-pitale <109548891+mayur-pitale@users.noreply.github.com> Date: Wed, 26 Mar 2025 10:30:30 -0700 Subject: [PATCH 14/78] Eventbridge enhancements (#2828) * Passes on the InputField min and max properties to JSONSchema for validation * [required-field-check] Run yarn install after switching to main branch (#2801) * [required-field-check] Run install after switching to main branch * Add more comments * Eventbridge cosmetic changes (#2807) * Description changes * Description changes * additional enhancements * eventbridge enhancements * eventbridge enhancements * changed Error to IntegrationError --------- Co-authored-by: Nick Aguilar Co-authored-by: Varadarajan V <109586712+varadarajan-tw@users.noreply.github.com> --- .../amazon-eventbridge/functions.ts | 12 + .../send/__tests__/index.test.ts | 246 ++++++++++++++---- .../send/generated-types.ts | 2 +- .../amazon-eventbridge/send/index.ts | 3 +- 4 files changed, 207 insertions(+), 56 deletions(-) diff --git a/packages/destination-actions/src/destinations/amazon-eventbridge/functions.ts b/packages/destination-actions/src/destinations/amazon-eventbridge/functions.ts index c6288d3036..52596e792e 100644 --- a/packages/destination-actions/src/destinations/amazon-eventbridge/functions.ts +++ b/packages/destination-actions/src/destinations/amazon-eventbridge/functions.ts @@ -34,8 +34,20 @@ async function process_data(events: Payload[], settings: Settings) { Resources: event.resources ? [event.resources] : [] })) } + const command = new PutPartnerEventsCommand(eb_payload) const response = await client.send(command) + // Check for errors in the response + if (response?.FailedEntryCount && response.FailedEntryCount > 0) { + const errors = response.Entries?.filter((entry) => entry.ErrorCode || entry.ErrorMessage) + const errorMessage = errors?.map((err) => `Error: ${err.ErrorCode}, Message: ${err.ErrorMessage}`).join('; ') + throw new IntegrationError( + `EventBridge failed with ${response.FailedEntryCount} errors: ${errorMessage}`, + 'EVENTBRIDGE_ERROR', + 400 + ) + } + return response } diff --git a/packages/destination-actions/src/destinations/amazon-eventbridge/send/__tests__/index.test.ts b/packages/destination-actions/src/destinations/amazon-eventbridge/send/__tests__/index.test.ts index 91d193ce1f..93451b2d52 100644 --- a/packages/destination-actions/src/destinations/amazon-eventbridge/send/__tests__/index.test.ts +++ b/packages/destination-actions/src/destinations/amazon-eventbridge/send/__tests__/index.test.ts @@ -1,19 +1,34 @@ import { send } from '../../functions' // Adjust path as needed -import { EventBridgeClient } from '@aws-sdk/client-eventbridge' -import { Payload } from '../../send/generated-types' // Import the Payload type -import { Settings } from '../../generated-types' // Import the Settings type -import { CreatePartnerEventSourceCommand } from '@aws-sdk/client-eventbridge' +import { + PutPartnerEventsCommand, + ListPartnerEventSourcesCommand, + CreatePartnerEventSourceCommand +} from '@aws-sdk/client-eventbridge' +import { Payload } from '../../send/generated-types' +import { Settings } from '../../generated-types' -jest.mock('@aws-sdk/client-eventbridge') // Mock AWS SDK +// Mock AWS SDK +jest.mock('@aws-sdk/client-eventbridge', () => { + const mockSend = jest.fn() + return { + EventBridgeClient: jest.fn(() => ({ + send: mockSend + })), + PutPartnerEventsCommand: jest.fn(), + ListPartnerEventSourcesCommand: jest.fn(), + CreatePartnerEventSourceCommand: jest.fn(), + mockSend + } +}) -const mockSend = jest.fn() -EventBridgeClient.prototype.send = mockSend +const { mockSend } = jest.requireMock('@aws-sdk/client-eventbridge') describe('AWS EventBridge Integration', () => { - const settings = { + const settings: Settings = { awsRegion: 'us-west-2', awsAccountId: '123456789012', - createPartnerEventSource: true + createPartnerEventSource: true, + partnerEventSourceName: 'your-partner-event-source-name' } afterEach(() => { @@ -32,52 +47,103 @@ describe('AWS EventBridge Integration', () => { detailType: 'UserSignup', data: { user: '123', event: 'signed_up' }, resources: 'test-resource', - enable_batching: true // Add the enable_batching property + enable_batching: true } ] - const updatedSettings: Settings = { - ...settings, - partnerEventSourceName: 'your-partner-event-source-name' // Add the partnerEventSourceName property - } + // Mocking the listPartnerEventSources response to simulate source existence check + mockSend.mockResolvedValueOnce({ + PartnerEventSources: [{ Name: `${settings.partnerEventSourceName}/test-source` }] + }) - await send(payloads, updatedSettings) - expect(mockSend).toHaveBeenCalled() + // Mocking a successful PutPartnerEventsCommand response + mockSend.mockResolvedValueOnce({ + FailedEntryCount: 0, + Entries: [{ EventId: '12345' }] + }) + + await send(payloads, settings) + + expect(mockSend).toHaveBeenCalledWith(expect.any(ListPartnerEventSourcesCommand)) + expect(mockSend).toHaveBeenCalledWith(expect.any(PutPartnerEventsCommand)) + expect(mockSend).toHaveBeenCalledTimes(2) }) - test('process_data should send event to EventBridge', async () => { + test('send should throw an error if FailedEntryCount > 0', async () => { const payloads: Payload[] = [ - // Ensure payloads is of type Payload[] { sourceId: 'test-source', detailType: 'UserSignup', data: { user: '123', event: 'signed_up' }, resources: 'test-resource', - enable_batching: true // Add the enable_batching property + enable_batching: true } ] - // Proper mock setup for List and Put commands - mockSend - .mockResolvedValueOnce({ PartnerEventSources: [] }) // Simulate "List" finding no source - .mockResolvedValueOnce({}) // Simulate successful event send + mockSend.mockResolvedValueOnce({ + PartnerEventSources: [{ Name: `${settings.partnerEventSourceName}/test-source` }] + }) + + mockSend.mockResolvedValueOnce({ + FailedEntryCount: 1, + Entries: [{ ErrorCode: 'EventBridgeError', ErrorMessage: 'Invalid event' }] + }) + + await expect(send(payloads, settings)).rejects.toThrow('Invalid event') + + expect(mockSend).toHaveBeenCalledWith(expect.any(ListPartnerEventSourcesCommand)) + expect(mockSend).toHaveBeenCalledWith(expect.any(PutPartnerEventsCommand)) + expect(mockSend).toHaveBeenCalledTimes(2) + }) + + test('ensurePartnerSourceExists should create source if it does not exist', async () => { + const payloads: Payload[] = [ + { + sourceId: 'test-source', + detailType: 'UserSignup', + data: { user: '123', event: 'signed_up' }, + resources: 'test-resource', + enable_batching: true + } + ] const updatedSettings: Settings = { ...settings, - partnerEventSourceName: 'aws.partner/segment.com.test', // Add the partnerEventSourceName property - createPartnerEventSource: true // Ensure this is enabled + partnerEventSourceName: 'aws.partner/segment.com.test', + createPartnerEventSource: true } + // Simulate "List" finding no source and success on creation + mockSend + .mockResolvedValueOnce({ PartnerEventSources: [] }) // No source exists + .mockResolvedValueOnce({}) // CreatePartnerEventSourceCommand success + .mockResolvedValueOnce({ FailedEntryCount: 0 }) // Event sent successfully + await send(payloads, updatedSettings) - expect(mockSend).toHaveBeenCalledWith(expect.any(Object)) - expect(mockSend).toHaveBeenCalledTimes(3) // One for List, one for Put + // Ensure the List command was called + expect(mockSend).toHaveBeenCalledWith(expect.any(ListPartnerEventSourcesCommand)) + + // Ensure the Create command was called + expect(mockSend).toHaveBeenCalledWith(expect.any(CreatePartnerEventSourceCommand)) + + // Ensure the event is sent + expect(mockSend).toHaveBeenCalledWith(expect.any(PutPartnerEventsCommand)) + + // Ensure all three commands are called + expect(mockSend).toHaveBeenCalledTimes(3) }) test('ensurePartnerSourceExists should not create source if it already exists', async () => { - mockSend.mockResolvedValueOnce({ - PartnerEventSources: [{ Name: 'aws.partner/segment.com.test/test-source' }] - }) + // Mock ListPartnerEventSourcesCommand to simulate existing source + mockSend + .mockResolvedValueOnce({ + PartnerEventSources: [{ Name: 'aws.partner/segment.com.test/test-source' }] + }) // ListPartnerEventSourcesCommand + .mockResolvedValueOnce({ + FailedEntryCount: 0, // Mock success for PutPartnerEventsCommand + Entries: [{ EventId: '12345' }] + }) const payloads: Payload[] = [ { @@ -85,25 +151,39 @@ describe('AWS EventBridge Integration', () => { detailType: 'UserSignup', data: { user: '123', event: 'signed_up' }, resources: 'test-resource', - enable_batching: true // Add the enable_batching property + enable_batching: true } ] const updatedSettings: Settings = { ...settings, - partnerEventSourceName: 'your-partner-event-source-name', // Add the partnerEventSourceName property - createPartnerEventSource: false // Ensure this is disabled + partnerEventSourceName: 'your-partner-event-source-name', + createPartnerEventSource: false } await send(payloads, updatedSettings) - expect(mockSend).toHaveBeenCalledTimes(2) // Only ListPartnerEventSources + + // Ensure it only calls ListPartnerEventSources and PutPartnerEventsCommand + expect(mockSend).toHaveBeenCalledTimes(2) + + // Ensure it does NOT call CreatePartnerEventSourceCommand + expect(mockSend).not.toHaveBeenCalledWith( + expect.objectContaining({ input: expect.objectContaining({ Account: '123456789012' }) }) + ) }) test('ensurePartnerSourceExists should create source if missing', async () => { - const mockSend = jest.spyOn(EventBridgeClient.prototype, 'send') - mockSend - .mockResolvedValueOnce({ PartnerEventSources: [] } as never) // First call: No sources exist - .mockResolvedValueOnce({} as never) // Second call: CreatePartnerEventSource + // Simulate "ListPartnerEventSources" - No source found + mockSend.mockResolvedValueOnce({ PartnerEventSources: [] }) + + // Simulate "CreatePartnerEventSource" - Source created + mockSend.mockResolvedValueOnce({}) + + // Simulate "PutEventsCommand" - Event sent successfully + mockSend.mockResolvedValueOnce({ + FailedEntryCount: 0, + Entries: [{ EventId: '12345' }] + }) const payloads: Payload[] = [ { @@ -111,19 +191,17 @@ describe('AWS EventBridge Integration', () => { detailType: 'UserSignup', data: { user: '123', event: 'signed_up' }, resources: 'test-resource', - enable_batching: true // Add the enable_batching property + enable_batching: true } ] - const updatedSettings: Settings = { - ...settings, - partnerEventSourceName: 'your-partner-event-source-name', // Add the partnerEventSourceName property - createPartnerEventSource: true // Ensure this is enabled - } - - await send(payloads, updatedSettings) + await send(payloads, settings) + // Ensure all three calls are made: List, Create, and Put expect(mockSend).toHaveBeenCalledTimes(3) + + // Check if CreatePartnerEventSourceCommand is called + expect(mockSend).toHaveBeenCalledWith(expect.any(CreatePartnerEventSourceCommand)) }) test('should throw error if partner source is missing and createPartnerEventSource is false', async () => { @@ -155,20 +233,80 @@ describe('AWS EventBridge Integration', () => { detailType: 'UserSignup', data: { user: '123', event: 'signed_up' }, resources: 'test-resource', - enable_batching: true // Add the enable_batching property + enable_batching: true } ] - mockSend.mockResolvedValueOnce({}) + // Ensure correct mock responses for all EventBridge calls + mockSend + .mockResolvedValueOnce({ PartnerEventSources: [] }) // ListPartnerEventSourcesCommand + .mockResolvedValueOnce({}) // CreatePartnerEventSourceCommand + .mockResolvedValueOnce({ FailedEntryCount: 0, Entries: [{ EventId: '12345' }] }) // PutEventsCommand - const updatedSettings: Settings = { - ...settings, - partnerEventSourceName: 'your-partner-event-source-name', // Add the partnerEventSourceName property - createPartnerEventSource: true // Ensure this is enabled - } + await send(payloads, settings) - await send(payloads, updatedSettings) + expect(mockSend).toHaveBeenCalledTimes(3) + // Ensure CreatePartnerEventSourceCommand is called expect(mockSend).toHaveBeenCalledWith(expect.any(CreatePartnerEventSourceCommand)) + + // Ensure PutEventsCommand is called with expected arguments + expect(mockSend).toHaveBeenCalledWith(expect.any(PutPartnerEventsCommand)) + }) + + test('process_data should throw error if event send fails', async () => { + const payloads: Payload[] = [ + { + sourceId: 'test-source', + detailType: 'UserSignup', + data: { user: '123', event: 'signed_up' }, + resources: 'test-resource', + enable_batching: true + } + ] + + const settings = { + awsRegion: 'us-west-2', + awsAccountId: '123456789012', + partnerEventSourceName: 'test-source' + // Other settings here + } + + // Mock a failed response + mockSend + .mockResolvedValueOnce({ + PartnerEventSources: [{ Name: 'aws.partner/segment.com.test/test-source' }] + }) // ListPartnerEventSourcesCommand + .mockResolvedValueOnce({ + FailedEntryCount: 1, + Entries: [{ ErrorCode: 'Error', ErrorMessage: 'Failed' }] + }) + + // Call the function and assert that it throws an error + await expect(send(payloads, settings)).rejects.toThrow( + 'EventBridge failed with 1 errors: Error: Error, Message: Failed' + ) + }) + + test('process_data should send event to EventBridge', async () => { + const payloads: Payload[] = [ + { + sourceId: 'test-source', + detailType: 'UserSignup', + data: { user: '123', event: 'signed_up' }, + resources: 'test-resource', + enable_batching: true + } + ] + + // Proper mock setup for List and Put commands + mockSend + .mockResolvedValueOnce({ PartnerEventSources: [] }) // Simulate "List" finding no source + .mockResolvedValueOnce({}) // Simulate successful event send + + await send(payloads, settings) + + expect(mockSend).toHaveBeenCalledWith(expect.any(Object)) + expect(mockSend).toHaveBeenCalledTimes(3) // One for List, one for Put }) }) diff --git a/packages/destination-actions/src/destinations/amazon-eventbridge/send/generated-types.ts b/packages/destination-actions/src/destinations/amazon-eventbridge/send/generated-types.ts index 4573b82309..d2fc4a6168 100644 --- a/packages/destination-actions/src/destinations/amazon-eventbridge/send/generated-types.ts +++ b/packages/destination-actions/src/destinations/amazon-eventbridge/send/generated-types.ts @@ -9,7 +9,7 @@ export interface Payload { } /** * Detail Type of the event. Used to determine what fields to expect in the event Detail. - * Values longer than 128 characters are trimmed + * Value cannot be longer than 128 characters. */ detailType: string /** diff --git a/packages/destination-actions/src/destinations/amazon-eventbridge/send/index.ts b/packages/destination-actions/src/destinations/amazon-eventbridge/send/index.ts index 93972065f6..66900f0b68 100644 --- a/packages/destination-actions/src/destinations/amazon-eventbridge/send/index.ts +++ b/packages/destination-actions/src/destinations/amazon-eventbridge/send/index.ts @@ -17,8 +17,9 @@ const action: ActionDefinition = { detailType: { label: 'Detail Type', description: `Detail Type of the event. Used to determine what fields to expect in the event Detail. - Values longer than 128 characters are trimmed`, + Value cannot be longer than 128 characters.`, type: 'string', + maximum: 128, default: { '@path': '$.type' }, required: true }, From d0ccde3b407f212803aa40c6a605e63389487f69 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 26 Mar 2025 10:45:42 -0700 Subject: [PATCH 15/78] Publish (#2835) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- packages/actions-shared/package.json | 4 +- .../browser-destination-runtime/package.json | 4 +- .../destinations/1flow/package.json | 4 +- .../destinations/adobe-target/package.json | 4 +- .../destinations/algolia-plugins/package.json | 4 +- .../amplitude-plugins/package.json | 4 +- .../braze-cloud-plugins/package.json | 6 +- .../destinations/braze/package.json | 6 +- .../destinations/bucket/package.json | 6 +- .../destinations/cdpresolution/package.json | 4 +- .../destinations/commandbar/package.json | 6 +- .../contentstack-web/package.json | 4 +- .../destinations/devrev/package.json | 4 +- .../destinations/evolv-ai/package.json | 4 +- .../destinations/friendbuy/package.json | 8 +- .../destinations/fullstory/package.json | 6 +- .../google-analytics-4-web/package.json | 6 +- .../google-campaign-manager/package.json | 4 +- .../destinations/heap/package.json | 6 +- .../destinations/hubble-web/package.json | 6 +- .../destinations/hubspot-web/package.json | 6 +- .../destinations/intercom/package.json | 8 +- .../destinations/iterate/package.json | 6 +- .../destinations/jimo/package.json | 4 +- .../destinations/koala/package.json | 6 +- .../destinations/logrocket/package.json | 6 +- .../nextdoor-plugins/package.json | 4 +- .../pendo-web-actions/package.json | 4 +- .../destinations/playerzero-web/package.json | 6 +- .../destinations/reddit-plugins/package.json | 4 +- .../destinations/replaybird/package.json | 4 +- .../destinations/ripe/package.json | 6 +- .../destinations/rupt/package.json | 6 +- .../destinations/screeb/package.json | 6 +- .../segment-utilities-web/package.json | 4 +- .../destinations/snap-plugins/package.json | 4 +- .../destinations/sprig-web/package.json | 6 +- .../destinations/stackadapt/package.json | 6 +- .../destinations/survicate/package.json | 4 +- .../destinations/tiktok-pixel/package.json | 6 +- .../destinations/upollo/package.json | 6 +- .../destinations/userpilot/package.json | 6 +- .../destinations/vwo/package.json | 6 +- .../destinations/wisepops/package.json | 6 +- packages/core/package.json | 2 +- packages/destination-actions/package.json | 6 +- packages/destinations-manifest/package.json | 80 +++++++++---------- 47 files changed, 159 insertions(+), 159 deletions(-) diff --git a/packages/actions-shared/package.json b/packages/actions-shared/package.json index 22ba162ff9..ca3f1e7d26 100644 --- a/packages/actions-shared/package.json +++ b/packages/actions-shared/package.json @@ -1,7 +1,7 @@ { "name": "@segment/actions-shared", "description": "Shared destination action methods and definitions.", - "version": "1.130.0", + "version": "1.131.0", "repository": { "type": "git", "url": "https://github.com/segmentio/action-destinations", @@ -37,7 +37,7 @@ }, "dependencies": { "@amplitude/ua-parser-js": "^0.7.25", - "@segment/actions-core": "^3.149.0", + "@segment/actions-core": "^3.150.0", "cheerio": "^1.0.0-rc.10", "dayjs": "^1.10.7", "escape-goat": "^3", diff --git a/packages/browser-destination-runtime/package.json b/packages/browser-destination-runtime/package.json index 5b03afd4ae..75f6b0759e 100644 --- a/packages/browser-destination-runtime/package.json +++ b/packages/browser-destination-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@segment/browser-destination-runtime", - "version": "1.78.0", + "version": "1.79.0", "license": "MIT", "publishConfig": { "access": "public", @@ -62,7 +62,7 @@ } }, "dependencies": { - "@segment/actions-core": "^3.149.0" + "@segment/actions-core": "^3.150.0" }, "devDependencies": { "@segment/analytics-next": "*" diff --git a/packages/browser-destinations/destinations/1flow/package.json b/packages/browser-destinations/destinations/1flow/package.json index ce246c95a9..8f628ee8cc 100644 --- a/packages/browser-destinations/destinations/1flow/package.json +++ b/packages/browser-destinations/destinations/1flow/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-1flow", - "version": "1.61.0", + "version": "1.62.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/adobe-target/package.json b/packages/browser-destinations/destinations/adobe-target/package.json index 24978319bb..61f984e248 100644 --- a/packages/browser-destinations/destinations/adobe-target/package.json +++ b/packages/browser-destinations/destinations/adobe-target/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-adobe-target", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -16,7 +16,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/algolia-plugins/package.json b/packages/browser-destinations/destinations/algolia-plugins/package.json index 6e98bf0776..87f5b103be 100644 --- a/packages/browser-destinations/destinations/algolia-plugins/package.json +++ b/packages/browser-destinations/destinations/algolia-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-algolia-plugins", - "version": "1.56.0", + "version": "1.57.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/amplitude-plugins/package.json b/packages/browser-destinations/destinations/amplitude-plugins/package.json index 2785f4df70..13249f0f26 100644 --- a/packages/browser-destinations/destinations/amplitude-plugins/package.json +++ b/packages/browser-destinations/destinations/amplitude-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-amplitude-plugins", - "version": "1.79.0", + "version": "1.80.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/braze-cloud-plugins/package.json b/packages/browser-destinations/destinations/braze-cloud-plugins/package.json index c8bf7613a8..f4ab49a3a9 100644 --- a/packages/browser-destinations/destinations/braze-cloud-plugins/package.json +++ b/packages/browser-destinations/destinations/braze-cloud-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-braze-cloud-plugins", - "version": "1.87.0", + "version": "1.88.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/analytics-browser-actions-braze": "^1.87.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/analytics-browser-actions-braze": "^1.88.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/braze/package.json b/packages/browser-destinations/destinations/braze/package.json index 808d5b748e..22d77bfe5f 100644 --- a/packages/browser-destinations/destinations/braze/package.json +++ b/packages/browser-destinations/destinations/braze/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-braze", - "version": "1.87.0", + "version": "1.88.0", "license": "MIT", "publishConfig": { "access": "public", @@ -35,8 +35,8 @@ "dependencies": { "@braze/web-sdk": "npm:@braze/web-sdk@^5", "@braze/web-sdk-v3": "npm:@braze/web-sdk@^3.5.1", - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/bucket/package.json b/packages/browser-destinations/destinations/bucket/package.json index 282d92161d..682bec2bc0 100644 --- a/packages/browser-destinations/destinations/bucket/package.json +++ b/packages/browser-destinations/destinations/bucket/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-bucket", - "version": "1.60.0", + "version": "1.61.0", "license": "MIT", "publishConfig": { "access": "public", @@ -16,8 +16,8 @@ "typings": "./dist/esm", "dependencies": { "@bucketco/tracking-sdk": "^2.0.0", - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/cdpresolution/package.json b/packages/browser-destinations/destinations/cdpresolution/package.json index 5ffd3aa370..39094610a8 100644 --- a/packages/browser-destinations/destinations/cdpresolution/package.json +++ b/packages/browser-destinations/destinations/cdpresolution/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-cdpresolution", - "version": "1.66.0", + "version": "1.67.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/commandbar/package.json b/packages/browser-destinations/destinations/commandbar/package.json index 1f83077086..1181226ec1 100644 --- a/packages/browser-destinations/destinations/commandbar/package.json +++ b/packages/browser-destinations/destinations/commandbar/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-commandbar", - "version": "1.79.0", + "version": "1.80.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/contentstack-web/package.json b/packages/browser-destinations/destinations/contentstack-web/package.json index ebebc1e9d0..c38e01b8a6 100644 --- a/packages/browser-destinations/destinations/contentstack-web/package.json +++ b/packages/browser-destinations/destinations/contentstack-web/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-contentstack-web", - "version": "1.22.0", + "version": "1.23.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/devrev/package.json b/packages/browser-destinations/destinations/devrev/package.json index 422a5f766a..878c4e3e98 100644 --- a/packages/browser-destinations/destinations/devrev/package.json +++ b/packages/browser-destinations/destinations/devrev/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-devrev", - "version": "1.66.0", + "version": "1.67.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/evolv-ai/package.json b/packages/browser-destinations/destinations/evolv-ai/package.json index 6dcd2812d5..cd07cb4b9b 100644 --- a/packages/browser-destinations/destinations/evolv-ai/package.json +++ b/packages/browser-destinations/destinations/evolv-ai/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-evolv-ai", - "version": "1.22.0", + "version": "1.23.0", "license": "MIT", "repository": { "type": "git", @@ -16,7 +16,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/friendbuy/package.json b/packages/browser-destinations/destinations/friendbuy/package.json index 19ccc4e3c1..2eeea3a42a 100644 --- a/packages/browser-destinations/destinations/friendbuy/package.json +++ b/packages/browser-destinations/destinations/friendbuy/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-friendbuy", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,9 +15,9 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/actions-shared": "^1.130.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/actions-shared": "^1.131.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/fullstory/package.json b/packages/browser-destinations/destinations/fullstory/package.json index f9c5497cb7..e73525fd85 100644 --- a/packages/browser-destinations/destinations/fullstory/package.json +++ b/packages/browser-destinations/destinations/fullstory/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-fullstory", - "version": "1.81.0", + "version": "1.82.0", "license": "MIT", "publishConfig": { "access": "public", @@ -16,8 +16,8 @@ "typings": "./dist/esm", "dependencies": { "@fullstory/browser": "^2.0.3", - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/google-analytics-4-web/package.json b/packages/browser-destinations/destinations/google-analytics-4-web/package.json index 8a081cd556..4b0a9e7f63 100644 --- a/packages/browser-destinations/destinations/google-analytics-4-web/package.json +++ b/packages/browser-destinations/destinations/google-analytics-4-web/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-google-analytics-4", - "version": "1.86.0", + "version": "1.87.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/google-campaign-manager/package.json b/packages/browser-destinations/destinations/google-campaign-manager/package.json index 5d356a8873..e6a945c881 100644 --- a/packages/browser-destinations/destinations/google-campaign-manager/package.json +++ b/packages/browser-destinations/destinations/google-campaign-manager/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-google-campaign-manager", - "version": "1.70.0", + "version": "1.71.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/heap/package.json b/packages/browser-destinations/destinations/heap/package.json index e53a1789b4..ff35299f1f 100644 --- a/packages/browser-destinations/destinations/heap/package.json +++ b/packages/browser-destinations/destinations/heap/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-heap", - "version": "1.79.0", + "version": "1.80.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/hubble-web/package.json b/packages/browser-destinations/destinations/hubble-web/package.json index c8da42ccb1..848dd51de3 100644 --- a/packages/browser-destinations/destinations/hubble-web/package.json +++ b/packages/browser-destinations/destinations/hubble-web/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-hubble-web", - "version": "1.65.0", + "version": "1.66.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/hubspot-web/package.json b/packages/browser-destinations/destinations/hubspot-web/package.json index 434747ae62..92aa5a76f1 100644 --- a/packages/browser-destinations/destinations/hubspot-web/package.json +++ b/packages/browser-destinations/destinations/hubspot-web/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-hubspot", - "version": "1.79.0", + "version": "1.80.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/intercom/package.json b/packages/browser-destinations/destinations/intercom/package.json index f59fcb54cf..194fd6e715 100644 --- a/packages/browser-destinations/destinations/intercom/package.json +++ b/packages/browser-destinations/destinations/intercom/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-intercom", - "version": "1.84.0", + "version": "1.85.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,9 +15,9 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/actions-shared": "^1.130.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/actions-shared": "^1.131.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/iterate/package.json b/packages/browser-destinations/destinations/iterate/package.json index 6ac366b0bf..6a2ca4c1f3 100644 --- a/packages/browser-destinations/destinations/iterate/package.json +++ b/packages/browser-destinations/destinations/iterate/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-iterate", - "version": "1.79.0", + "version": "1.80.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/jimo/package.json b/packages/browser-destinations/destinations/jimo/package.json index 5fbb2d4d2f..f763663d60 100644 --- a/packages/browser-destinations/destinations/jimo/package.json +++ b/packages/browser-destinations/destinations/jimo/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-jimo", - "version": "1.69.0", + "version": "1.70.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/koala/package.json b/packages/browser-destinations/destinations/koala/package.json index adea90cafb..f2d7c5923b 100644 --- a/packages/browser-destinations/destinations/koala/package.json +++ b/packages/browser-destinations/destinations/koala/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-koala", - "version": "1.81.0", + "version": "1.82.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/logrocket/package.json b/packages/browser-destinations/destinations/logrocket/package.json index 5cad359694..f72c5c3821 100644 --- a/packages/browser-destinations/destinations/logrocket/package.json +++ b/packages/browser-destinations/destinations/logrocket/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-logrocket", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0", + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0", "logrocket": "^9.0.0" }, "peerDependencies": { diff --git a/packages/browser-destinations/destinations/nextdoor-plugins/package.json b/packages/browser-destinations/destinations/nextdoor-plugins/package.json index 3b3358dbf7..311a1804c8 100644 --- a/packages/browser-destinations/destinations/nextdoor-plugins/package.json +++ b/packages/browser-destinations/destinations/nextdoor-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-nextdoor-plugins", - "version": "1.22.0", + "version": "1.23.0", "license": "MIT", "repository": { "type": "git", @@ -16,7 +16,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/pendo-web-actions/package.json b/packages/browser-destinations/destinations/pendo-web-actions/package.json index 2f3330d959..a031dbae0e 100644 --- a/packages/browser-destinations/destinations/pendo-web-actions/package.json +++ b/packages/browser-destinations/destinations/pendo-web-actions/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-pendo-web-actions", - "version": "1.69.0", + "version": "1.70.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/playerzero-web/package.json b/packages/browser-destinations/destinations/playerzero-web/package.json index 29c8af016b..319596eee5 100644 --- a/packages/browser-destinations/destinations/playerzero-web/package.json +++ b/packages/browser-destinations/destinations/playerzero-web/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-playerzero", - "version": "1.79.0", + "version": "1.80.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/reddit-plugins/package.json b/packages/browser-destinations/destinations/reddit-plugins/package.json index 994dfbc0a0..04053ded05 100644 --- a/packages/browser-destinations/destinations/reddit-plugins/package.json +++ b/packages/browser-destinations/destinations/reddit-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-reddit-plugins", - "version": "1.22.0", + "version": "1.23.0", "license": "MIT", "repository": { "type": "git", @@ -16,7 +16,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/replaybird/package.json b/packages/browser-destinations/destinations/replaybird/package.json index 027aa2532f..3ee79f1b45 100644 --- a/packages/browser-destinations/destinations/replaybird/package.json +++ b/packages/browser-destinations/destinations/replaybird/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-replaybird", - "version": "1.60.0", + "version": "1.61.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/ripe/package.json b/packages/browser-destinations/destinations/ripe/package.json index bf2321778a..e92d7b0fec 100644 --- a/packages/browser-destinations/destinations/ripe/package.json +++ b/packages/browser-destinations/destinations/ripe/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-ripe", - "version": "1.79.0", + "version": "1.80.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/rupt/package.json b/packages/browser-destinations/destinations/rupt/package.json index d2b4f51c41..e626f556d9 100644 --- a/packages/browser-destinations/destinations/rupt/package.json +++ b/packages/browser-destinations/destinations/rupt/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-rupt", - "version": "1.68.0", + "version": "1.69.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/screeb/package.json b/packages/browser-destinations/destinations/screeb/package.json index 03d1a62968..2a83e602e5 100644 --- a/packages/browser-destinations/destinations/screeb/package.json +++ b/packages/browser-destinations/destinations/screeb/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-screeb", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/segment-utilities-web/package.json b/packages/browser-destinations/destinations/segment-utilities-web/package.json index 1836b95e81..514d9e9519 100644 --- a/packages/browser-destinations/destinations/segment-utilities-web/package.json +++ b/packages/browser-destinations/destinations/segment-utilities-web/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-utils", - "version": "1.79.0", + "version": "1.80.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/snap-plugins/package.json b/packages/browser-destinations/destinations/snap-plugins/package.json index 6895e41c91..21cf9bbebc 100644 --- a/packages/browser-destinations/destinations/snap-plugins/package.json +++ b/packages/browser-destinations/destinations/snap-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-snap-plugins", - "version": "1.60.0", + "version": "1.61.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/sprig-web/package.json b/packages/browser-destinations/destinations/sprig-web/package.json index e5d1deed34..8c5110711e 100644 --- a/packages/browser-destinations/destinations/sprig-web/package.json +++ b/packages/browser-destinations/destinations/sprig-web/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-sprig", - "version": "1.79.0", + "version": "1.80.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/stackadapt/package.json b/packages/browser-destinations/destinations/stackadapt/package.json index ae3b7678aa..9dc20efd5e 100644 --- a/packages/browser-destinations/destinations/stackadapt/package.json +++ b/packages/browser-destinations/destinations/stackadapt/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-stackadapt", - "version": "1.81.0", + "version": "1.82.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/survicate/package.json b/packages/browser-destinations/destinations/survicate/package.json index 8d94ed9037..ae5343dff0 100644 --- a/packages/browser-destinations/destinations/survicate/package.json +++ b/packages/browser-destinations/destinations/survicate/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-survicate", - "version": "1.55.0", + "version": "1.56.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/tiktok-pixel/package.json b/packages/browser-destinations/destinations/tiktok-pixel/package.json index 293040d6ea..d2bf75dc0d 100644 --- a/packages/browser-destinations/destinations/tiktok-pixel/package.json +++ b/packages/browser-destinations/destinations/tiktok-pixel/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-tiktok-pixel", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/upollo/package.json b/packages/browser-destinations/destinations/upollo/package.json index e912e78ede..3218995584 100644 --- a/packages/browser-destinations/destinations/upollo/package.json +++ b/packages/browser-destinations/destinations/upollo/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-upollo", - "version": "1.79.0", + "version": "1.80.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/userpilot/package.json b/packages/browser-destinations/destinations/userpilot/package.json index ccf199adc3..fe840c6438 100644 --- a/packages/browser-destinations/destinations/userpilot/package.json +++ b/packages/browser-destinations/destinations/userpilot/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-userpilot", - "version": "1.79.0", + "version": "1.80.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/vwo/package.json b/packages/browser-destinations/destinations/vwo/package.json index 8e09aaf673..50fb08c609 100644 --- a/packages/browser-destinations/destinations/vwo/package.json +++ b/packages/browser-destinations/destinations/vwo/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-vwo", - "version": "1.82.0", + "version": "1.83.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/wisepops/package.json b/packages/browser-destinations/destinations/wisepops/package.json index 537b233306..9f79edd038 100644 --- a/packages/browser-destinations/destinations/wisepops/package.json +++ b/packages/browser-destinations/destinations/wisepops/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-wiseops", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.149.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/actions-core": "^3.150.0", + "@segment/browser-destination-runtime": "^1.79.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/core/package.json b/packages/core/package.json index e7ade4a247..4a710f1928 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@segment/actions-core", "description": "Core runtime for Destinations Actions.", - "version": "3.149.0", + "version": "3.150.0", "repository": { "type": "git", "url": "https://github.com/segmentio/fab-5-engine", diff --git a/packages/destination-actions/package.json b/packages/destination-actions/package.json index 25818404ee..3f02ea7b4f 100644 --- a/packages/destination-actions/package.json +++ b/packages/destination-actions/package.json @@ -1,7 +1,7 @@ { "name": "@segment/action-destinations", "description": "Destination Actions engine and definitions.", - "version": "3.379.0", + "version": "3.380.0", "repository": { "type": "git", "url": "https://github.com/segmentio/action-destinations", @@ -45,8 +45,8 @@ "@aws-sdk/client-s3": "^3.600.0", "@bufbuild/protobuf": "^2.2.3", "@segment/a1-notation": "^2.1.4", - "@segment/actions-core": "^3.149.0", - "@segment/actions-shared": "^1.130.0", + "@segment/actions-core": "^3.150.0", + "@segment/actions-shared": "^1.131.0", "@types/node": "^22.13.1", "ajv-formats": "^2.1.1", "aws4": "^1.12.0", diff --git a/packages/destinations-manifest/package.json b/packages/destinations-manifest/package.json index af1e0abe6c..ffabb76e60 100644 --- a/packages/destinations-manifest/package.json +++ b/packages/destinations-manifest/package.json @@ -1,6 +1,6 @@ { "name": "@segment/destinations-manifest", - "version": "1.109.0", + "version": "1.110.0", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" @@ -12,45 +12,45 @@ "main": "./dist/index.js", "typings": "./dist/index.d.ts", "dependencies": { - "@segment/analytics-browser-actions-1flow": "^1.61.0", - "@segment/analytics-browser-actions-adobe-target": "^1.80.0", - "@segment/analytics-browser-actions-algolia-plugins": "^1.56.0", - "@segment/analytics-browser-actions-amplitude-plugins": "^1.79.0", - "@segment/analytics-browser-actions-braze": "^1.87.0", - "@segment/analytics-browser-actions-braze-cloud-plugins": "^1.87.0", - "@segment/analytics-browser-actions-bucket": "^1.60.0", - "@segment/analytics-browser-actions-cdpresolution": "^1.66.0", - "@segment/analytics-browser-actions-commandbar": "^1.79.0", - "@segment/analytics-browser-actions-contentstack-web": "^1.22.0", - "@segment/analytics-browser-actions-devrev": "^1.66.0", - "@segment/analytics-browser-actions-friendbuy": "^1.80.0", - "@segment/analytics-browser-actions-fullstory": "^1.81.0", - "@segment/analytics-browser-actions-google-analytics-4": "^1.86.0", - "@segment/analytics-browser-actions-google-campaign-manager": "^1.70.0", - "@segment/analytics-browser-actions-heap": "^1.79.0", - "@segment/analytics-browser-actions-hubspot": "^1.79.0", - "@segment/analytics-browser-actions-intercom": "^1.84.0", - "@segment/analytics-browser-actions-iterate": "^1.79.0", - "@segment/analytics-browser-actions-jimo": "^1.69.0", - "@segment/analytics-browser-actions-koala": "^1.81.0", - "@segment/analytics-browser-actions-logrocket": "^1.80.0", - "@segment/analytics-browser-actions-pendo-web-actions": "^1.69.0", - "@segment/analytics-browser-actions-playerzero": "^1.79.0", - "@segment/analytics-browser-actions-replaybird": "^1.60.0", - "@segment/analytics-browser-actions-ripe": "^1.79.0", + "@segment/analytics-browser-actions-1flow": "^1.62.0", + "@segment/analytics-browser-actions-adobe-target": "^1.81.0", + "@segment/analytics-browser-actions-algolia-plugins": "^1.57.0", + "@segment/analytics-browser-actions-amplitude-plugins": "^1.80.0", + "@segment/analytics-browser-actions-braze": "^1.88.0", + "@segment/analytics-browser-actions-braze-cloud-plugins": "^1.88.0", + "@segment/analytics-browser-actions-bucket": "^1.61.0", + "@segment/analytics-browser-actions-cdpresolution": "^1.67.0", + "@segment/analytics-browser-actions-commandbar": "^1.80.0", + "@segment/analytics-browser-actions-contentstack-web": "^1.23.0", + "@segment/analytics-browser-actions-devrev": "^1.67.0", + "@segment/analytics-browser-actions-friendbuy": "^1.81.0", + "@segment/analytics-browser-actions-fullstory": "^1.82.0", + "@segment/analytics-browser-actions-google-analytics-4": "^1.87.0", + "@segment/analytics-browser-actions-google-campaign-manager": "^1.71.0", + "@segment/analytics-browser-actions-heap": "^1.80.0", + "@segment/analytics-browser-actions-hubspot": "^1.80.0", + "@segment/analytics-browser-actions-intercom": "^1.85.0", + "@segment/analytics-browser-actions-iterate": "^1.80.0", + "@segment/analytics-browser-actions-jimo": "^1.70.0", + "@segment/analytics-browser-actions-koala": "^1.82.0", + "@segment/analytics-browser-actions-logrocket": "^1.81.0", + "@segment/analytics-browser-actions-pendo-web-actions": "^1.70.0", + "@segment/analytics-browser-actions-playerzero": "^1.80.0", + "@segment/analytics-browser-actions-replaybird": "^1.61.0", + "@segment/analytics-browser-actions-ripe": "^1.80.0", "@segment/analytics-browser-actions-sabil": "^1.6.0", - "@segment/analytics-browser-actions-screeb": "^1.80.0", - "@segment/analytics-browser-actions-snap-plugins": "^1.60.0", - "@segment/analytics-browser-actions-sprig": "^1.79.0", - "@segment/analytics-browser-actions-stackadapt": "^1.81.0", - "@segment/analytics-browser-actions-survicate": "^1.55.0", - "@segment/analytics-browser-actions-tiktok-pixel": "^1.80.0", - "@segment/analytics-browser-actions-upollo": "^1.79.0", - "@segment/analytics-browser-actions-userpilot": "^1.79.0", - "@segment/analytics-browser-actions-utils": "^1.79.0", - "@segment/analytics-browser-actions-vwo": "^1.82.0", - "@segment/analytics-browser-actions-wiseops": "^1.80.0", - "@segment/analytics-browser-hubble-web": "^1.65.0", - "@segment/browser-destination-runtime": "^1.78.0" + "@segment/analytics-browser-actions-screeb": "^1.81.0", + "@segment/analytics-browser-actions-snap-plugins": "^1.61.0", + "@segment/analytics-browser-actions-sprig": "^1.80.0", + "@segment/analytics-browser-actions-stackadapt": "^1.82.0", + "@segment/analytics-browser-actions-survicate": "^1.56.0", + "@segment/analytics-browser-actions-tiktok-pixel": "^1.81.0", + "@segment/analytics-browser-actions-upollo": "^1.80.0", + "@segment/analytics-browser-actions-userpilot": "^1.80.0", + "@segment/analytics-browser-actions-utils": "^1.80.0", + "@segment/analytics-browser-actions-vwo": "^1.83.0", + "@segment/analytics-browser-actions-wiseops": "^1.81.0", + "@segment/analytics-browser-hubble-web": "^1.66.0", + "@segment/browser-destination-runtime": "^1.79.0" } } From c131ad0b16eacca86c82922e439158631bd1d6bc Mon Sep 17 00:00:00 2001 From: Varadarajan V <109586712+varadarajan-tw@users.noreply.github.com> Date: Thu, 27 Mar 2025 16:29:03 +0530 Subject: [PATCH 16/78] [STRATCONN-5608] - Make ssl_enabled and ssl_reject_unauthorized_ca settings optional (#2832) --- .../src/destinations/kafka/generated-types.ts | 4 ++-- packages/destination-actions/src/destinations/kafka/index.ts | 4 ++-- packages/destination-actions/src/destinations/kafka/utils.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/destination-actions/src/destinations/kafka/generated-types.ts b/packages/destination-actions/src/destinations/kafka/generated-types.ts index bb57b46af5..44b2bf3a15 100644 --- a/packages/destination-actions/src/destinations/kafka/generated-types.ts +++ b/packages/destination-actions/src/destinations/kafka/generated-types.ts @@ -36,7 +36,7 @@ export interface Settings { /** * Indicates if SSL should be enabled. */ - ssl_enabled: boolean + ssl_enabled?: boolean /** * The Certificate Authority for your Kafka instance. Exclude the first and last lines from the file. i.e `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`. */ @@ -52,5 +52,5 @@ export interface Settings { /** * Whether to reject unauthorized CAs or not. This can be useful when testing, but is unadvised in Production. */ - ssl_reject_unauthorized_ca: boolean + ssl_reject_unauthorized_ca?: boolean } diff --git a/packages/destination-actions/src/destinations/kafka/index.ts b/packages/destination-actions/src/destinations/kafka/index.ts index b60c0d9dbe..15747e67c5 100644 --- a/packages/destination-actions/src/destinations/kafka/index.ts +++ b/packages/destination-actions/src/destinations/kafka/index.ts @@ -84,7 +84,7 @@ const destination: DestinationDefinition = { label: 'SSL Enabled', description: 'Indicates if SSL should be enabled.', type: 'boolean', - required: true, + required: false, default: true }, ssl_ca: { @@ -115,7 +115,7 @@ const destination: DestinationDefinition = { description: 'Whether to reject unauthorized CAs or not. This can be useful when testing, but is unadvised in Production.', type: 'boolean', - required: true, + required: false, default: true } }, diff --git a/packages/destination-actions/src/destinations/kafka/utils.ts b/packages/destination-actions/src/destinations/kafka/utils.ts index 77ec861a3c..170f683bc7 100644 --- a/packages/destination-actions/src/destinations/kafka/utils.ts +++ b/packages/destination-actions/src/destinations/kafka/utils.ts @@ -60,7 +60,7 @@ interface TopicMessages { interface SSLConfig { ca: string[] - rejectUnauthorized: boolean + rejectUnauthorized?: boolean key?: string cert?: string } From 5227a1a0a25e15ceb02fbb66874ce78ca6a0950b Mon Sep 17 00:00:00 2001 From: Arijit Ray <35370469+itsarijitray@users.noreply.github.com> Date: Tue, 1 Apr 2025 13:17:59 +0530 Subject: [PATCH 17/78] Revert "Fix Test cases (#2538)" (#2844) This reverts commit dda2c53ee4a2e0498c543595e2ea2ac59b6a23d2. --- .../destinations/hyperengage/__tests__/validateInput.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/destination-actions/src/destinations/hyperengage/__tests__/validateInput.test.ts b/packages/destination-actions/src/destinations/hyperengage/__tests__/validateInput.test.ts index 00417afb92..72dd17d61b 100644 --- a/packages/destination-actions/src/destinations/hyperengage/__tests__/validateInput.test.ts +++ b/packages/destination-actions/src/destinations/hyperengage/__tests__/validateInput.test.ts @@ -81,7 +81,7 @@ describe('validateInput', () => { expect(payload.traits.industry).toEqual(fakeGroupData.industry) expect(payload.traits.website).toEqual(fakeGroupData.website) expect(payload.traits).toHaveProperty('required') - expect(payload.local_tz_offset).toEqual(60) + expect(payload.local_tz_offset).toEqual(120) }) }) From 42a20fe260fc263a69d5ef0480d08fcf44128791 Mon Sep 17 00:00:00 2001 From: guillaumefontan <82604059+guillaumefontan@users.noreply.github.com> Date: Tue, 1 Apr 2025 13:10:59 +0200 Subject: [PATCH 18/78] Update Trubrics action destination (#2784) * Test * Test * update types * final * identify * pr comments * utils https * add thread_id * fix tests --------- Co-authored-by: Jeff Kayne <43336277+jeffkayne@users.noreply.github.com> --- .../trubrics/identify/__tests__/index.test.ts | 24 ++++ .../identify/__tests__/snapshot.test.ts | 75 +++++++++++ .../trubrics/identify/generated-types.ts | 22 ++++ .../destinations/trubrics/identify/index.ts | 48 +++++++ .../destinations/trubrics/identify/utils.ts | 23 ++++ .../src/destinations/trubrics/index.ts | 6 +- .../trubrics/track/__tests__/index.test.ts | 24 ++-- .../trubrics/track/generated-types.ts | 45 +++++-- .../src/destinations/trubrics/track/index.ts | 120 +++++++++++------- .../src/destinations/trubrics/track/utils.ts | 26 ++++ 10 files changed, 344 insertions(+), 69 deletions(-) create mode 100644 packages/destination-actions/src/destinations/trubrics/identify/__tests__/index.test.ts create mode 100644 packages/destination-actions/src/destinations/trubrics/identify/__tests__/snapshot.test.ts create mode 100644 packages/destination-actions/src/destinations/trubrics/identify/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/trubrics/identify/index.ts create mode 100644 packages/destination-actions/src/destinations/trubrics/identify/utils.ts create mode 100644 packages/destination-actions/src/destinations/trubrics/track/utils.ts diff --git a/packages/destination-actions/src/destinations/trubrics/identify/__tests__/index.test.ts b/packages/destination-actions/src/destinations/trubrics/identify/__tests__/index.test.ts new file mode 100644 index 0000000000..093a0eeba5 --- /dev/null +++ b/packages/destination-actions/src/destinations/trubrics/identify/__tests__/index.test.ts @@ -0,0 +1,24 @@ +import nock from 'nock' +import { createTestIntegration } from '@segment/actions-core' +import Destination from '../../index' + +const testDestination = createTestIntegration(Destination) +const settings = { apiKey: 'api-key', url: 'app.trubrics.com/api/ingestion' } +const mapping = { user_id: 'user-id', timestamp: '2021-01-01T00:00:00.000Z', anonymous_id: 'my-id', traits: {} } + +describe('Trubrics.identify', () => { + it('should work', async () => { + nock(`https://${settings.url}`) + .post('/identify_segment_users', [mapping]) + .matchHeader('x-api-key', settings.apiKey) + .reply(200, {}) + + const responses = await testDestination.testAction('identify', { + mapping, + settings + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(200) + }) +}) diff --git a/packages/destination-actions/src/destinations/trubrics/identify/__tests__/snapshot.test.ts b/packages/destination-actions/src/destinations/trubrics/identify/__tests__/snapshot.test.ts new file mode 100644 index 0000000000..7a412500bd --- /dev/null +++ b/packages/destination-actions/src/destinations/trubrics/identify/__tests__/snapshot.test.ts @@ -0,0 +1,75 @@ +import { createTestEvent, createTestIntegration } from '@segment/actions-core' +import { generateTestData } from '../../../../lib/test-data' +import destination from '../../index' +import nock from 'nock' + +const testDestination = createTestIntegration(destination) +const actionSlug = 'identify' +const destinationSlug = 'Trubrics' +const seedName = `${destinationSlug}#${actionSlug}` + +describe(`Testing snapshot for ${destinationSlug}'s ${actionSlug} destination action:`, () => { + it.skip('required fields', async () => { + const action = destination.actions[actionSlug] + const [eventData, settingsData] = generateTestData(seedName, destination, action, true) + + nock(/.*/).persist().get(/.*/).reply(200) + nock(/.*/).persist().post(/.*/).reply(200) + nock(/.*/).persist().put(/.*/).reply(200) + + const event = createTestEvent({ + properties: eventData + }) + + const responses = await testDestination.testAction(actionSlug, { + event: event, + mapping: event.properties, + settings: settingsData, + auth: undefined + }) + + const request = responses[0].request + const rawBody = await request.text() + + try { + const json = JSON.parse(rawBody) + expect(json).toMatchSnapshot() + return + } catch (err) { + expect(rawBody).toMatchSnapshot() + } + + expect(request.headers).toMatchSnapshot() + }) + + it.skip('all fields', async () => { + const action = destination.actions[actionSlug] + const [eventData, settingsData] = generateTestData(seedName, destination, action, false) + + nock(/.*/).persist().get(/.*/).reply(200) + nock(/.*/).persist().post(/.*/).reply(200) + nock(/.*/).persist().put(/.*/).reply(200) + + const event = createTestEvent({ + properties: eventData + }) + + const responses = await testDestination.testAction(actionSlug, { + event: event, + mapping: event.properties, + settings: settingsData, + auth: undefined + }) + + const request = responses[0].request + const rawBody = await request.text() + + try { + const json = JSON.parse(rawBody) + expect(json).toMatchSnapshot() + return + } catch (err) { + expect(rawBody).toMatchSnapshot() + } + }) +}) diff --git a/packages/destination-actions/src/destinations/trubrics/identify/generated-types.ts b/packages/destination-actions/src/destinations/trubrics/identify/generated-types.ts new file mode 100644 index 0000000000..4d3f9e00c7 --- /dev/null +++ b/packages/destination-actions/src/destinations/trubrics/identify/generated-types.ts @@ -0,0 +1,22 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * The ID of the user performing the event. + */ + user_id: string + /** + * An anonymous identifier + */ + anonymous_id?: string + /** + * The traits of the user. + */ + traits?: { + [k: string]: unknown + } + /** + * The timestamp of the event + */ + timestamp?: string +} diff --git a/packages/destination-actions/src/destinations/trubrics/identify/index.ts b/packages/destination-actions/src/destinations/trubrics/identify/index.ts new file mode 100644 index 0000000000..d7a2392532 --- /dev/null +++ b/packages/destination-actions/src/destinations/trubrics/identify/index.ts @@ -0,0 +1,48 @@ +import type { ActionDefinition } from '@segment/actions-core' +import type { Payload } from './generated-types' +import type { Settings } from '../generated-types' +import { sendRequest } from './utils' + +const action: ActionDefinition = { + title: 'Identify', + description: 'Identify a user in Trubrics and associate traits with them.', + fields: { + user_id: { + label: 'User ID', + type: 'string', + description: 'The ID of the user performing the event.', + required: true, + default: { + '@path': '$.userId' + } + }, + anonymous_id: { + type: 'string', + description: 'An anonymous identifier', + required: false, + label: 'Anonymous ID', + default: { '@path': '$.anonymousId' } + }, + traits: { + label: 'Traits', + type: 'object', + description: 'The traits of the user.', + required: false, + default: { + '@path': '$.traits' + } + }, + timestamp: { + type: 'string', + format: 'date-time', + required: false, + description: 'The timestamp of the event', + label: 'Timestamp', + default: { '@path': '$.timestamp' } + } + }, + perform: async (request, { settings, payload }) => await sendRequest(request, settings, [payload]), + performBatch: async (request, { settings, payload }) => await sendRequest(request, settings, payload) +} + +export default action diff --git a/packages/destination-actions/src/destinations/trubrics/identify/utils.ts b/packages/destination-actions/src/destinations/trubrics/identify/utils.ts new file mode 100644 index 0000000000..11d33249a9 --- /dev/null +++ b/packages/destination-actions/src/destinations/trubrics/identify/utils.ts @@ -0,0 +1,23 @@ +import type { Payload } from './generated-types' +import type { Settings } from '../generated-types' + +const prepareJSON = (payload: Payload) => { + return { + user_id: payload.user_id, + timestamp: payload.timestamp, + anonymous_id: payload.anonymous_id, + traits: payload.traits + } +} + +export const sendRequest = async (request: Function, settings: Settings, payload: Payload[]) => { + const json = payload.map(prepareJSON) + + return await request(`https://${settings.url}/identify_segment_users`, { + method: 'post', + headers: { + 'x-api-key': settings.apiKey + }, + json + }) +} diff --git a/packages/destination-actions/src/destinations/trubrics/index.ts b/packages/destination-actions/src/destinations/trubrics/index.ts index 1cdfd0042d..26b8d1a34d 100644 --- a/packages/destination-actions/src/destinations/trubrics/index.ts +++ b/packages/destination-actions/src/destinations/trubrics/index.ts @@ -2,6 +2,7 @@ import type { DestinationDefinition } from '@segment/actions-core' import type { Settings } from './generated-types' import track from './track' +import identify from './identify' const destination: DestinationDefinition = { name: 'Trubrics', @@ -22,12 +23,13 @@ const destination: DestinationDefinition = { description: 'The Trubrics API URL. In most cases the default value should be used.', type: 'string', required: true, - default: 'api.trubrics.com' + default: 'app.trubrics.com/api/ingestion' } } }, actions: { - track + track, + identify } } diff --git a/packages/destination-actions/src/destinations/trubrics/track/__tests__/index.test.ts b/packages/destination-actions/src/destinations/trubrics/track/__tests__/index.test.ts index 53fbaa785d..20a107ff59 100644 --- a/packages/destination-actions/src/destinations/trubrics/track/__tests__/index.test.ts +++ b/packages/destination-actions/src/destinations/trubrics/track/__tests__/index.test.ts @@ -1,23 +1,29 @@ import nock from 'nock' -import { createTestEvent, createTestIntegration } from '@segment/actions-core' +import { createTestIntegration } from '@segment/actions-core' import Destination from '../../index' const testDestination = createTestIntegration(Destination) +const settings = { apiKey: 'api-key', url: 'app.trubrics.com/api/ingestion' } +const mapping = { + anonymous_id: 'my-id', + event: 'test event', + timestamp: '2021-01-01T00:00:00.000Z', + user_id: 'user-id' +} describe('Trubrics.track', () => { - it.skip('should work', async () => { - const settings = { apiKey: 'api-key', url: 'api.trubrics.com' } - const event = createTestEvent({ timestamp: '2021-08-17T15:21:15.449Z', event: 'Test Event' }) - nock(`https://${settings.url}`).post(`/publish_event?project_api_key=${settings.apiKey}`).reply(200, {}) + it('should work', async () => { + nock(`https://${settings.url}`) + .post('/publish_segment_events', [mapping]) + .matchHeader('x-api-key', settings.apiKey) + .reply(200, {}) const responses = await testDestination.testAction('track', { - event, - settings, - useDefaultMappings: true + mapping, + settings }) expect(responses.length).toBe(1) expect(responses[0].status).toBe(200) - expect(responses[0].data).toMatchObject({}) }) }) diff --git a/packages/destination-actions/src/destinations/trubrics/track/generated-types.ts b/packages/destination-actions/src/destinations/trubrics/track/generated-types.ts index b8baf9cd4e..256980a0c5 100644 --- a/packages/destination-actions/src/destinations/trubrics/track/generated-types.ts +++ b/packages/destination-actions/src/destinations/trubrics/track/generated-types.ts @@ -6,15 +6,42 @@ export interface Payload { */ event: string /** - * Properties to send with the event + * The timestamp of the event */ - properties?: { - [k: string]: unknown + timestamp?: string + /** + * The ID associated with the user + */ + user_id?: string + /** + * The properties associated with an LLM event + */ + llm_properties?: { + /** + * The LLM assistant ID (often the model name) + */ + assistant_id?: string + /** + * A user prompt to an LLM + */ + prompt?: string + /** + * An LLM assistant response + */ + generation?: string + /** + * The thread/conversation ID of the LLM conversation. + */ + thread_id?: string + /** + * The latency in seconds between the LLM prompt and generation + */ + latency?: number } /** - * user properties to send with the event + * Properties to send with the event */ - traits?: { + properties?: { [k: string]: unknown } /** @@ -23,14 +50,6 @@ export interface Payload { context?: { [k: string]: unknown } - /** - * The timestamp of the event - */ - timestamp: string - /** - * The ID associated with the user - */ - user_id?: string /** * The Anonymous ID associated with the user */ diff --git a/packages/destination-actions/src/destinations/trubrics/track/index.ts b/packages/destination-actions/src/destinations/trubrics/track/index.ts index 1808b9ac80..fe63928909 100644 --- a/packages/destination-actions/src/destinations/trubrics/track/index.ts +++ b/packages/destination-actions/src/destinations/trubrics/track/index.ts @@ -1,6 +1,7 @@ import type { ActionDefinition } from '@segment/actions-core' import type { Payload } from './generated-types' import type { Settings } from '../generated-types' +import { sendRequest } from './utils' const action: ActionDefinition = { title: 'Track', @@ -14,31 +15,10 @@ const action: ActionDefinition = { label: 'Event Name', default: { '@path': '$.event' } }, - properties: { - type: 'object', - required: false, - description: 'Properties to send with the event', - label: 'Event properties', - default: { '@path': '$.properties' } - }, - traits: { - type: 'object', - required: false, - description: 'user properties to send with the event', - label: 'User properties', - default: { '@path': '$.context.traits' } - }, - context: { - type: 'object', - required: false, - description: 'Context properties to send with the event', - label: 'Context properties', - default: { '@path': '$.context' } - }, timestamp: { type: 'string', format: 'date-time', - required: true, + required: false, description: 'The timestamp of the event', label: 'Timestamp', default: { '@path': '$.timestamp' } @@ -50,6 +30,77 @@ const action: ActionDefinition = { label: 'User ID', default: { '@path': '$.userId' } }, + llm_properties: { + label: 'LLM Event Properties', + description: 'The properties associated with an LLM event', + type: 'object', + additionalProperties: false, + required: false, + defaultObjectUI: 'keyvalue:only', + properties: { + assistant_id: { + type: 'string', + required: false, + description: 'The LLM assistant ID (often the model name)', + label: 'LLM Assistant ID' + }, + prompt: { + type: 'string', + required: false, + description: 'A user prompt to an LLM', + label: 'LLM User Prompt' + }, + generation: { + type: 'string', + required: false, + description: 'An LLM assistant response', + label: 'LLM Assistant Generation' + }, + thread_id: { + type: 'string', + required: false, + description: 'The thread/conversation ID of the LLM conversation.', + label: 'LLM Thread ID' + }, + latency: { + type: 'number', + required: false, + description: 'The latency in seconds between the LLM prompt and generation', + label: 'LLM Latency' + } + }, + default: { + assistant_id: { + '@path': '$.properties.$trubrics_assistant_id' + }, + prompt: { + '@path': '$.properties.$trubrics_prompt' + }, + generation: { + '@path': '$.properties.$trubrics_generation' + }, + thread_id: { + '@path': '$.properties.$trubrics_thread_id' + }, + latency: { + '@path': '$.properties.$trubrics_latency' + } + } + }, + properties: { + type: 'object', + required: false, + description: 'Properties to send with the event', + label: 'Event properties', + default: { '@path': '$.properties' } + }, + context: { + type: 'object', + required: false, + description: 'Context properties to send with the event', + label: 'Context properties', + default: { '@path': '$.context' } + }, anonymous_id: { type: 'string', required: false, @@ -58,29 +109,8 @@ const action: ActionDefinition = { default: { '@path': '$.anonymousId' } } }, - perform: (request, { settings, payload }) => { - const trubrics_properties = ['assistant_id', 'thread_id', 'text'] - - const modifiedProperties = Object.entries(payload.properties || {}).reduce((acc, [key, value]) => { - if (trubrics_properties.includes(key)) { - acc[`$${key}`] = value - } else { - acc[key] = value - } - return acc - }, {} as Record) - - return request(`https://${settings.url}/publish_event?project_api_key=${settings.apiKey}`, { - method: 'post', - json: { - event: payload.event, - properties: { ...modifiedProperties, ...payload.context }, - traits: payload.traits, - timestamp: payload.timestamp, - user_id: payload.user_id || payload.anonymous_id // Trubrics currently requires user_id - } - }) - } + perform: async (request, { settings, payload }) => await sendRequest(request, settings, [payload]), + performBatch: async (request, { settings, payload }) => await sendRequest(request, settings, payload) } export default action diff --git a/packages/destination-actions/src/destinations/trubrics/track/utils.ts b/packages/destination-actions/src/destinations/trubrics/track/utils.ts new file mode 100644 index 0000000000..357c64b71a --- /dev/null +++ b/packages/destination-actions/src/destinations/trubrics/track/utils.ts @@ -0,0 +1,26 @@ +import type { Payload } from './generated-types' +import type { Settings } from '../generated-types' + +const prepareJSON = (payload: Payload) => { + return { + event: payload.event, + timestamp: payload.timestamp, + user_id: payload.user_id, + anonymous_id: payload.anonymous_id, + llm_properties: payload.llm_properties, + context: payload.context, + properties: payload.properties + } +} + +export const sendRequest = async (request: Function, settings: Settings, payload: Payload[]) => { + const json = payload.map(prepareJSON) + + return await request(`https://${settings.url}/publish_segment_events`, { + method: 'post', + headers: { + 'x-api-key': settings.apiKey + }, + json + }) +} From f56e5458bc17d0de4b1a8395ed4aca7f9ebdd89d Mon Sep 17 00:00:00 2001 From: Arijit Ray <35370469+itsarijitray@users.noreply.github.com> Date: Tue, 1 Apr 2025 16:57:28 +0530 Subject: [PATCH 19/78] [STRATCONN-5473] Add destinationInstanceID and subscriptionId to support audience fanout for Liveramp Audiences (#2786) * [STRATCONN-5473] Add destinationInstanceID and subscriptionId to support audience fanout * Revert Tradesk Changes * handle file path edge cases * handle file path edge cases * Add Tests --- .../__tests__/liveramp.test.ts | 193 ++++++++++++++++++ .../audienceEnteredS3/index.ts | 45 ++-- .../audienceEnteredSftp/index.ts | 45 ++-- .../liveramp-audiences/awsClient.ts | 10 +- 4 files changed, 261 insertions(+), 32 deletions(-) create mode 100644 packages/destination-actions/src/destinations/liveramp-audiences/__tests__/liveramp.test.ts diff --git a/packages/destination-actions/src/destinations/liveramp-audiences/__tests__/liveramp.test.ts b/packages/destination-actions/src/destinations/liveramp-audiences/__tests__/liveramp.test.ts new file mode 100644 index 0000000000..f1efdc01d9 --- /dev/null +++ b/packages/destination-actions/src/destinations/liveramp-audiences/__tests__/liveramp.test.ts @@ -0,0 +1,193 @@ +import nock from 'nock' +import { createTestIntegration, PayloadValidationError, SegmentEvent } from '@segment/actions-core' +import Destination from '../index' +import fs from 'fs' +import { LIVERAMP_MIN_RECORD_COUNT } from '../properties' + +const testDestination = createTestIntegration(Destination) + +const mockAwsCredentials = { + accessKeyId: 'accessKeyId', + secretAccessKey: 'secretAccessKey', + sessionToken: 'sessionToken' +} + +const mockedEvents: SegmentEvent[] = Array.from({ length: 50 }, (_, i) => ({ + messageId: `segment-test-message-00000${i + 2}`, + timestamp: '2023-07-26T15:23:39.803Z', + type: 'track', + userId: `userid${i + 2}`, + receivedAt: '2015-12-12T19:11:01.266Z', + properties: {}, + event: 'Audience Entered' +})) + +describe('Liveramp Audiences', () => { + beforeEach(() => { + jest.clearAllMocks() + jest.resetModules() + + jest.spyOn(fs, 'readFileSync').mockReturnValue('token') + + nock('https://kubernetes.default.svc') + .get('/api/v1/namespaces/default/serviceaccounts/pod-service-account') + .reply(200, { + metadata: { + annotations: { + 'eks.amazonaws.com/role-arn': 'arn:aws:iam::123456789012:role/role-name' + } + } + }) + + nock('https://sts.us-west-2.amazonaws.com/') + .get( + `/?` + + `Action=AssumeRoleWithWebIdentity` + + `&DurationSeconds=3600` + + `&RoleSessionName=integrations-monoservice` + + `&RoleArn=arn:aws:iam::123456789012:role/role-name` + + `&WebIdentityToken=token` + + `&Version=2011-06-15` + ) + .reply(200, { + AssumeRoleWithWebIdentityResponse: { + AssumeRoleWithWebIdentityResult: { + Credentials: { + accessKeyId: mockAwsCredentials.accessKeyId, + secretAccessKey: mockAwsCredentials.secretAccessKey, + sessionToken: mockAwsCredentials.sessionToken + } + } + } + }) + + nock('https://integrations-outbound-event-store-test-us-west-2.s3.us-west-2.amazonaws.com') + .put( + /\/actions-liveramp-audiences\/destinationConfigId\/actionConfigId\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\.csv$/ + ) + .reply(200) + + nock('https://integrations-outbound-event-store-test-us-west-2.s3.us-west-2.amazonaws.com') + .put('/actions-liveramp-audiences/destinationConfigId/actionConfigId/meta.json') + .reply(200) + }) + describe('audienceEnteredS3', () => { + it('should send events with valid payload size and events', async () => { + const response = await testDestination.executeBatch('audienceEnteredS3', { + events: mockedEvents, + mapping: { + s3_aws_access_key: 's3_aws_access_key', + s3_aws_secret_key: 's3_aws_secret_key', + s3_aws_bucket_name: 's3_aws_bucket_name', + s3_aws_region: 's3_aws_region', + audience_key: 'audience_key', + delimiter: ',', + filename: 'filename.csv', + enable_batching: true + }, + subscriptionMetadata: { + destinationConfigId: 'destinationConfigId', + actionConfigId: 'actionConfigId' + }, + settings: { + __segment_internal_engage_force_full_sync: true, + __segment_internal_engage_batch_sync: true + } + }) + for (let i = 0; i < mockedEvents.length; i++) { + expect(response.at(i)?.status).toEqual(200) + } + }) + it(`should throw error if payload size is less than ${LIVERAMP_MIN_RECORD_COUNT}`, async () => { + try { + await testDestination.executeBatch('audienceEnteredS3', { + events: mockedEvents.slice(1, 4), + mapping: { + s3_aws_access_key: 's3_aws_access_key', + s3_aws_secret_key: 's3_aws_secret_key', + s3_aws_bucket_name: 's3_aws_bucket_name', + s3_aws_region: 's3_aws_region', + audience_key: 'audience_key', + delimiter: ',', + filename: 'filename.csv', + enable_batching: true + }, + subscriptionMetadata: { + destinationConfigId: 'destinationConfigId', + actionConfigId: 'actionConfigId' + }, + settings: { + __segment_internal_engage_force_full_sync: true, + __segment_internal_engage_batch_sync: true + } + }) + } catch (e) { + expect(e).toBeInstanceOf(PayloadValidationError) + expect(e.message).toEqual( + `received payload count below LiveRamp's ingestion limits. expected: >=${LIVERAMP_MIN_RECORD_COUNT} actual: 3` + ) + expect(e.status).toEqual(400) + } + }) + }) + describe('audienceEnteredSFTP', () => { + it('should send events with valid payload size and events', async () => { + const response = await testDestination.executeBatch('audienceEnteredSFTP', { + events: mockedEvents, + mapping: { + sftp_username: 'sftp_username', + sftp_aws_access_key: 'sftp_aws_access_key', + sftp_folder_path: 'sftp_folder_path', + sftp_password: 'sftp_password', + audience_key: 'audience_key', + delimiter: ',', + filename: 'filename.csv', + enable_batching: true + }, + subscriptionMetadata: { + destinationConfigId: 'destinationConfigId', + actionConfigId: 'actionConfigId' + }, + settings: { + __segment_internal_engage_force_full_sync: true, + __segment_internal_engage_batch_sync: true + }, + features: {} + }) + for (let i = 0; i < mockedEvents.length; i++) { + expect(response.at(i)?.status).toEqual(200) + } + }) + it(`should throw error if payload size is less than ${LIVERAMP_MIN_RECORD_COUNT}`, async () => { + try { + await testDestination.executeBatch('audienceEnteredSFTP', { + events: mockedEvents.slice(1, 4), + mapping: { + sftp_username: 'sftp_username', + sftp_aws_access_key: 'sftp_aws_access_key', + sftp_folder_path: 'sftp_folder_path', + sftp_password: 'sftp_password', + audience_key: 'audience_key', + delimiter: ',', + filename: 'filename.csv', + enable_batching: true + }, + subscriptionMetadata: { + destinationConfigId: 'destinationConfigId', + actionConfigId: 'actionConfigId' + }, + settings: { + __segment_internal_engage_force_full_sync: true, + __segment_internal_engage_batch_sync: true + } + }) + } catch (e) { + expect(e).toBeInstanceOf(PayloadValidationError) + expect(e.message).toEqual( + `received payload count below LiveRamp's ingestion limits. expected: >=${LIVERAMP_MIN_RECORD_COUNT} actual: 3` + ) + expect(e.status).toEqual(400) + } + }) + }) +}) diff --git a/packages/destination-actions/src/destinations/liveramp-audiences/audienceEnteredS3/index.ts b/packages/destination-actions/src/destinations/liveramp-audiences/audienceEnteredS3/index.ts index 534f61f5df..39c9d55741 100644 --- a/packages/destination-actions/src/destinations/liveramp-audiences/audienceEnteredS3/index.ts +++ b/packages/destination-actions/src/destinations/liveramp-audiences/audienceEnteredS3/index.ts @@ -7,6 +7,7 @@ import { LIVERAMP_MIN_RECORD_COUNT, LIVERAMP_LEGACY_FLOW_FLAG_NAME } from '../pr import type { Settings } from '../generated-types' import type { Payload } from './generated-types' import type { RawData, ExecuteInputRaw, ProcessDataInput } from '../operations' +import { SubscriptionMetadata } from '@segment/actions-core/destination-kit' const action: ActionDefinition = { title: 'Audience Entered (S3)', @@ -86,25 +87,37 @@ const action: ActionDefinition = { default: 170000 } }, - perform: async (request, { payload, features, rawData }: ExecuteInputRaw) => { - return processData({ - request, - payloads: [payload], - features, - rawData: rawData ? [rawData] : [] - }) + perform: async ( + request, + { payload, features, rawData, subscriptionMetadata }: ExecuteInputRaw + ) => { + return processData( + { + request, + payloads: [payload], + features, + rawData: rawData ? [rawData] : [] + }, + subscriptionMetadata + ) }, - performBatch: (request, { payload, features, rawData }: ExecuteInputRaw) => { - return processData({ - request, - payloads: payload, - features, - rawData - }) + performBatch: ( + request, + { payload, features, rawData, subscriptionMetadata }: ExecuteInputRaw + ) => { + return processData( + { + request, + payloads: payload, + features, + rawData + }, + subscriptionMetadata + ) } } -async function processData(input: ProcessDataInput) { +async function processData(input: ProcessDataInput, subscriptionMetadata?: SubscriptionMetadata) { if (input.payloads.length < LIVERAMP_MIN_RECORD_COUNT) { throw new PayloadValidationError( `received payload count below LiveRamp's ingestion limits. expected: >=${LIVERAMP_MIN_RECORD_COUNT} actual: ${input.payloads.length}` @@ -128,6 +141,8 @@ async function processData(input: ProcessDataInput) { audienceComputeId: input.rawData?.[0].context?.personas?.computation_id, uploadType: 's3', filename, + destinationInstanceID: subscriptionMetadata?.destinationConfigId, + subscriptionId: subscriptionMetadata?.actionConfigId, fileContents, s3Info: { s3BucketName: input.payloads[0].s3_aws_bucket_name, diff --git a/packages/destination-actions/src/destinations/liveramp-audiences/audienceEnteredSftp/index.ts b/packages/destination-actions/src/destinations/liveramp-audiences/audienceEnteredSftp/index.ts index ca9f09218a..94470ff7df 100644 --- a/packages/destination-actions/src/destinations/liveramp-audiences/audienceEnteredSftp/index.ts +++ b/packages/destination-actions/src/destinations/liveramp-audiences/audienceEnteredSftp/index.ts @@ -7,6 +7,7 @@ import { LIVERAMP_MIN_RECORD_COUNT, LIVERAMP_LEGACY_FLOW_FLAG_NAME } from '../pr import type { Settings } from '../generated-types' import type { Payload } from './generated-types' import type { RawData, ExecuteInputRaw, ProcessDataInput } from '../operations' +import { SubscriptionMetadata } from '@segment/actions-core/destination-kit' const action: ActionDefinition = { title: 'Audience Entered (SFTP)', @@ -84,25 +85,37 @@ const action: ActionDefinition = { default: 50000 } }, - perform: async (request, { payload, features, rawData }: ExecuteInputRaw) => { - return processData({ - request, - payloads: [payload], - features, - rawData: rawData ? [rawData] : [] - }) + perform: async ( + request, + { payload, features, rawData, subscriptionMetadata }: ExecuteInputRaw + ) => { + return processData( + { + request, + payloads: [payload], + features, + rawData: rawData ? [rawData] : [] + }, + subscriptionMetadata + ) }, - performBatch: (request, { payload, features, rawData }: ExecuteInputRaw) => { - return processData({ - request, - payloads: payload, - features, - rawData - }) + performBatch: ( + request, + { payload, features, rawData, subscriptionMetadata }: ExecuteInputRaw + ) => { + return processData( + { + request, + payloads: payload, + features, + rawData + }, + subscriptionMetadata + ) } } -async function processData(input: ProcessDataInput) { +async function processData(input: ProcessDataInput, subscriptionMetadata?: SubscriptionMetadata) { if (input.payloads.length < LIVERAMP_MIN_RECORD_COUNT) { throw new PayloadValidationError( `received payload count below LiveRamp's ingestion limits. expected: >=${LIVERAMP_MIN_RECORD_COUNT} actual: ${input.payloads.length}` @@ -128,6 +141,8 @@ async function processData(input: ProcessDataInput) { uploadType: 'sftp', filename, fileContents, + destinationInstanceID: subscriptionMetadata?.destinationConfigId, + subscriptionId: subscriptionMetadata?.actionConfigId, sftpInfo: { sftpUsername: input.payloads[0].sftp_username, sftpPassword: input.payloads[0].sftp_password, diff --git a/packages/destination-actions/src/destinations/liveramp-audiences/awsClient.ts b/packages/destination-actions/src/destinations/liveramp-audiences/awsClient.ts index 76d3f656a0..2a1fec89a3 100644 --- a/packages/destination-actions/src/destinations/liveramp-audiences/awsClient.ts +++ b/packages/destination-actions/src/destinations/liveramp-audiences/awsClient.ts @@ -8,6 +8,8 @@ import { ACTION_SLUG, LIVERAMP_SFTP_SERVER, LIVERAMP_SFTP_PORT } from './propert interface SendToAWSRequest { audienceComputeId?: string + destinationInstanceID?: string + subscriptionId?: string uploadType: 's3' | 'sftp' filename: string fileContents: Buffer @@ -61,8 +63,12 @@ export const sendEventToAWS = async (request: RequestClient, input: SendToAWSReq // Compute file path and message dedupe id // Each advertiser and segment can eventually have multiple data drops, we use uuid create unique files const uuidValue = uuidv4() - const userdataFilePath = `/${ACTION_SLUG}/${input.audienceComputeId}/${uuidValue}.csv` - const metadataFilePath = `/${ACTION_SLUG}/${input.audienceComputeId}/meta.json` + const aggreagtedFilePath = + `${input.destinationInstanceID ?? ''}${input.subscriptionId ? '/' + input.subscriptionId : ''}${ + input.audienceComputeId ? '/' + input.audienceComputeId : '' + }`.replace(/^\/+|\/+$/g, '') || '' + const userdataFilePath = `/${ACTION_SLUG}/${aggreagtedFilePath}/${uuidValue}.csv` + const metadataFilePath = `/${ACTION_SLUG}/${aggreagtedFilePath}/meta.json` // Create Metadata const metadata: LRMetaPayload = { From 39619d8756d19c293b29bf88ddddcbef3feeb59d Mon Sep 17 00:00:00 2001 From: Ankit Gupta <139338151+AnkitSegment@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:09:47 +0530 Subject: [PATCH 20/78] [SMART-HASHING] [STRATCONN-5636] Updated smart-hashing in delivrai-activate (#2836) * Updated smart-hashing in delivrai-activate * Updated unit test cases --- .../updateSegment/__tests__/index.test.ts | 45 ++++++++++++++++++- .../delivrai-activate/updateSegment/index.ts | 15 ++++--- .../delivrai-activate/utils-rt.ts | 24 ++++------ 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/packages/destination-actions/src/destinations/delivrai-activate/updateSegment/__tests__/index.test.ts b/packages/destination-actions/src/destinations/delivrai-activate/updateSegment/__tests__/index.test.ts index 6fb41f5177..27f8341505 100644 --- a/packages/destination-actions/src/destinations/delivrai-activate/updateSegment/__tests__/index.test.ts +++ b/packages/destination-actions/src/destinations/delivrai-activate/updateSegment/__tests__/index.test.ts @@ -77,7 +77,50 @@ describe('delivrAIAudiences.updateSegment', () => { client_identifier_id: client_identifier_id } }) - + + expect(responses[0].status).toBe(200) + }) + + it('should not hash if pii data is already hashed', async () => { + nock(`https://dev.cdpresolution.com`).post('/backend/segment/audience').reply(200) + + const good_event = createTestEvent({ + type: 'identify', + context: { + device: { + advertisingId: ADVERTISING_ID + }, + personas: { + audience_settings: { + audience_id: AUDIENCE_ID, + audience_key: AUDIENCE_KEY + }, + computation_id: AUDIENCE_ID, + computation_key: AUDIENCE_KEY, + computation_class: 'audience' + } + }, + traits: { + email: 'testing@testing.com', + sneakers_buyers: true + } + }) + + const responses = await testDestination.testAction('updateSegment', { + auth, + event: good_event, + mapping: { + identifier: 'email' + }, + useDefaultMappings: true, + settings: { + client_identifier_id: client_identifier_id + } + }) + + expect(responses[0].options.body).toBe( + '{"audience_key":"aud_12345","data":[{"email":"584c4423c421df49955759498a71495aba49b8780eb9387dff333b6f0982c777","advertising_id_ios":"","advertising_id_android":"foobar","phone":""}],"client_identifier_id":"delivrai"}' + ) expect(responses[0].status).toBe(200) }) }) diff --git a/packages/destination-actions/src/destinations/delivrai-activate/updateSegment/index.ts b/packages/destination-actions/src/destinations/delivrai-activate/updateSegment/index.ts index 275c792603..19fe78c8d9 100644 --- a/packages/destination-actions/src/destinations/delivrai-activate/updateSegment/index.ts +++ b/packages/destination-actions/src/destinations/delivrai-activate/updateSegment/index.ts @@ -1,4 +1,4 @@ -import type { ActionDefinition, RequestClient } from '@segment/actions-core' +import type { ActionDefinition, Features, RequestClient } from '@segment/actions-core' import { PayloadValidationError, InvalidAuthenticationError, APIError } from '@segment/actions-core' import type { Settings } from '../generated-types' import type { Payload } from './generated-types' @@ -94,7 +94,7 @@ const action: ActionDefinition = { } }, - perform: async (request, { payload, settings }) => { + perform: async (request, { payload, settings, features }) => { // Check if client_identifier_id is valid let rt_access_token_response // Generate JWT token @@ -109,9 +109,9 @@ const action: ActionDefinition = { } const rt_access_token = rt_access_token_response?.data?.token // Process the payload with the valid token - return process_payload(request, [payload], rt_access_token, settings.client_identifier_id) + return process_payload(request, [payload], rt_access_token, settings.client_identifier_id, features || {}) }, - performBatch: async (request, { payload, settings }) => { + performBatch: async (request, { payload, settings, features }) => { // Ensure client_identifier_id is provided let rt_access_token_response // Generate JWT token @@ -126,7 +126,7 @@ const action: ActionDefinition = { } const rt_access_token = rt_access_token_response?.data?.token // Process the payload with the valid token - return process_payload(request, payload, rt_access_token, settings.client_identifier_id) + return process_payload(request, payload, rt_access_token, settings.client_identifier_id, features ?? {}) } } @@ -135,9 +135,10 @@ async function process_payload( request: RequestClient, payload: Payload[], token: string | undefined, - client_identifier_id: string + client_identifier_id: string, + features: Features ) { - const body = gen_update_segment_payload(payload, client_identifier_id) + const body = gen_update_segment_payload(payload, client_identifier_id, features) // Send request to Delivr AI only when all events in the batch include selected Ids if (body.data.length > 0) { return await request(`${DELIVRAI_BASE_URL}${DELIVRAI_SEGMENTATION_AUDIENCE}`, { diff --git a/packages/destination-actions/src/destinations/delivrai-activate/utils-rt.ts b/packages/destination-actions/src/destinations/delivrai-activate/utils-rt.ts index eb57f67b92..61eed915b4 100644 --- a/packages/destination-actions/src/destinations/delivrai-activate/utils-rt.ts +++ b/packages/destination-actions/src/destinations/delivrai-activate/utils-rt.ts @@ -1,18 +1,8 @@ -import { createHash } from 'crypto' import { Payload } from './updateSegment/generated-types' import { DelivrAIPayload } from './types' -import { RequestClient } from '@segment/actions-core' +import { Features, RequestClient } from '@segment/actions-core' import { DELIVRAI_BASE_URL, DELIVRAI_GET_TOKEN } from './constants' - -/** - * Creates a SHA256 hash from the input - * @param input The input string - * @returns The SHA256 hash (string), or undefined if the input is undefined. - */ -export function create_hash(input: string | undefined): string | undefined { - if (input === undefined) return - return createHash('sha256').update(input).digest('hex') -} +import { processHashing } from '../../lib/hashing-utils' /** * Generates JWT for Realtime API authentication @@ -60,7 +50,11 @@ export function validate_phone(phone: string) { return '' } } -export function gen_update_segment_payload(payloads: Payload[], client_identifier_id: string): DelivrAIPayload { +export function gen_update_segment_payload( + payloads: Payload[], + client_identifier_id: string, + features: Features +): DelivrAIPayload { const data_groups: { [hashed_email: string]: { exp: string @@ -74,7 +68,7 @@ export function gen_update_segment_payload(payloads: Payload[], client_identifie for (const event of payloads) { let hashed_email: string | undefined = '' if (event.email) { - hashed_email = create_hash(event.email.toLowerCase()) + hashed_email = processHashing(event.email.toLowerCase(), 'sha256', 'hex', features, 'actions-delivrai-audiences') } let idfa: string | undefined = '' let gpsaid: string | undefined = '' @@ -109,7 +103,7 @@ export function gen_update_segment_payload(payloads: Payload[], client_identifie } const ts = Math.floor(new Date().getTime() / 1000) let exp - const seg_id = event.segment_audience_id || ''; + const seg_id = event.segment_audience_id || '' audience_key = seg_id const group_key = `${hashed_email}|${idfa}|${gpsaid}|${hashed_phone}` if (!(group_key in data_groups)) { From c445f69877c1aad002be500c9a96c08b22c14e8b Mon Sep 17 00:00:00 2001 From: harsh-joshi99 <129737395+harsh-joshi99@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:36:33 +0530 Subject: [PATCH 21/78] [Smart Hashing] [Snap Conversions API] Implement Smart hashing for Snap Conversions API (#2837) * Implement Smart hashing for Snap Conversions API * Return undefined from smart hash if hash field are not defined --- .../_tests_/index.test.ts | 226 ++++++++++++++++++ .../reportConversionEvent/snap-capi-v3.ts | 68 +++--- .../reportConversionEvent/utils.ts | 13 +- .../src/lib/hashing-utils.ts | 1 + 4 files changed, 274 insertions(+), 34 deletions(-) diff --git a/packages/destination-actions/src/destinations/snap-conversions-api/_tests_/index.test.ts b/packages/destination-actions/src/destinations/snap-conversions-api/_tests_/index.test.ts index 4cc19cd5e0..82f271f018 100644 --- a/packages/destination-actions/src/destinations/snap-conversions-api/_tests_/index.test.ts +++ b/packages/destination-actions/src/destinations/snap-conversions-api/_tests_/index.test.ts @@ -611,5 +611,231 @@ describe('Snap Conversions API ', () => { expect(user_data.sc_click_id).toEqual((testEvent.integrations?.['Snap Conversions Api'] as any)?.click_id) expect(user_data.sc_cookie1).toEqual((testEvent.integrations?.['Snap Conversions Api'] as any)?.uuid_c1) }) + + it('should detect hashed values', async () => { + const { + data: { + action_source, + app_data, + custom_data, + data_processing_options, + data_processing_options_country, + data_processing_options_state, + event_id, + event_name, + event_source_url, + event_time, + user_data + } + } = await reportConversionEvent({ + event: { + ...testEvent, + properties: { + currency: 'USD', + email: '388c735eec8225c4ad7a507944dd0a975296baea383198aa87177f29af2c6f69', + phone: 'd74dcef7e1dba5aed01b62bc932ab1a7848ed9a590ca029aa958a1bf82b6993d', + birthday: '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b', + gender: 'eef2eae2699d81c58d176a9a58d4bf183df2acb6844b9eebf1cc60ae460ec50d', + last_name: 'fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7', + first_name: '5f39b51ae9a4dacbb8d9538229d726bfb7e1a03633e37d64598c32989a8c1277', + address: { + city: 'dc968997b2904bcdb46f90378701f86fb09b17274579e9d75601cdaaecd0a5fb', + state: 'dc968997b2904bcdb46f90378701f86fb09b17274579e9d75601cdaaecd0a5fb', + country: 'dc968997b2904bcdb46f90378701f86fb09b17274579e9d75601cdaaecd0a5fb', + postalCode: 'dc968997b2904bcdb46f90378701f86fb09b17274579e9d75601cdaaecd0a5fb' + }, + query: 'test-query', + order_id: 'A1234', + revenue: 1000 + }, + context: { + ...testEvent.context, + app: { + namespace: 'com.segment.app', + version: '1.0.0' + }, + device: { + adTrackingEnabled: true + }, + screen: { + width: '1920', + height: '1080', + density: '2.0' + }, + os: { + version: '19.5' + } + } + }, + mapping: { + event_name: 'PURCHASE', + action_source: 'app', + data_processing_options: true, + data_processing_options_country: 1, + data_processing_options_state: 1000 + } + }) + + expect(action_source).toEqual('app') + expect(event_name).toEqual('PURCHASE') + expect(event_id).toEqual(testEvent.messageId) + expect(event_source_url).toEqual(testEvent.context?.page?.url ?? '') + expect(event_time).toEqual(Date.parse(testEvent.timestamp as string)) + + expect(data_processing_options).toEqual(['LDU']) + expect(data_processing_options_country).toEqual(1) + expect(data_processing_options_state).toEqual(1000) + + expect(app_data.application_tracking_enabled).toEqual(1) + expect(app_data.extinfo).toEqual([ + 'i2', + 'com.segment.app', + '', + '1.0.0', + '19.5', + '', + 'en-US', + '', + '', + '1920', + '1080', + '2.0', + '', + '', + '', + 'Europe/Amsterdam' + ]) + + expect(custom_data.currency).toEqual('USD') + expect(custom_data.order_id).toEqual('A1234') + expect(custom_data.search_string).toEqual('test-query') + expect(custom_data.value).toEqual(1000) + + expect(user_data.external_id[0]).toEqual(hash(testEvent.userId ?? '')) + expect(user_data.em[0]).toEqual('388c735eec8225c4ad7a507944dd0a975296baea383198aa87177f29af2c6f69') + expect(user_data.ph[0]).toEqual('d74dcef7e1dba5aed01b62bc932ab1a7848ed9a590ca029aa958a1bf82b6993d') + expect(user_data.db).toEqual('6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b') + expect(user_data.fn).toEqual('5f39b51ae9a4dacbb8d9538229d726bfb7e1a03633e37d64598c32989a8c1277') + expect(user_data.ln).toEqual('fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7') + expect(user_data.ge).toEqual('eef2eae2699d81c58d176a9a58d4bf183df2acb6844b9eebf1cc60ae460ec50d') + expect(user_data.ct).toEqual('dc968997b2904bcdb46f90378701f86fb09b17274579e9d75601cdaaecd0a5fb') + expect(user_data.st).toEqual('dc968997b2904bcdb46f90378701f86fb09b17274579e9d75601cdaaecd0a5fb') + expect(user_data.country).toEqual('dc968997b2904bcdb46f90378701f86fb09b17274579e9d75601cdaaecd0a5fb') + expect(user_data.zp).toEqual('dc968997b2904bcdb46f90378701f86fb09b17274579e9d75601cdaaecd0a5fb') + expect(user_data.client_ip_address).toBe(testEvent.context?.ip) + expect(user_data.client_user_agent).toBe(testEvent.context?.userAgent) + expect(user_data.idfv).toBe(testEvent.context?.device?.id) + expect(user_data.madid).toBe(testEvent.context?.device?.advertisingId) + expect(user_data.sc_click_id).toEqual((testEvent.integrations?.['Snap Conversions Api'] as any)?.click_id) + expect(user_data.sc_cookie1).toEqual((testEvent.integrations?.['Snap Conversions Api'] as any)?.uuid_c1) + }) + + it('should not add empty values in final payload', async () => { + const { + data: { + action_source, + app_data, + custom_data, + data_processing_options, + data_processing_options_country, + data_processing_options_state, + event_id, + event_name, + event_source_url, + event_time, + user_data + } + } = await reportConversionEvent({ + event: { + ...testEvent, + properties: { + currency: 'USD', + email: '388c735eec8225c4ad7a507944dd0a975296baea383198aa87177f29af2c6f69', + phone: 'd74dcef7e1dba5aed01b62bc932ab1a7848ed9a590ca029aa958a1bf82b6993d', + query: 'test-query', + order_id: 'A1234', + revenue: 1000 + }, + context: { + ...testEvent.context, + app: { + namespace: 'com.segment.app', + version: '1.0.0' + }, + device: { + adTrackingEnabled: true + }, + screen: { + width: '1920', + height: '1080', + density: '2.0' + }, + os: { + version: '19.5' + } + } + }, + mapping: { + event_name: 'PURCHASE', + action_source: 'app', + data_processing_options: true, + data_processing_options_country: 1, + data_processing_options_state: 1000 + } + }) + + expect(action_source).toEqual('app') + expect(event_name).toEqual('PURCHASE') + expect(event_id).toEqual(testEvent.messageId) + expect(event_source_url).toEqual(testEvent.context?.page?.url ?? '') + expect(event_time).toEqual(Date.parse(testEvent.timestamp as string)) + + expect(data_processing_options).toEqual(['LDU']) + expect(data_processing_options_country).toEqual(1) + expect(data_processing_options_state).toEqual(1000) + + expect(app_data.application_tracking_enabled).toEqual(1) + expect(app_data.extinfo).toEqual([ + 'i2', + 'com.segment.app', + '', + '1.0.0', + '19.5', + '', + 'en-US', + '', + '', + '1920', + '1080', + '2.0', + '', + '', + '', + 'Europe/Amsterdam' + ]) + + expect(custom_data.currency).toEqual('USD') + expect(custom_data.order_id).toEqual('A1234') + expect(custom_data.search_string).toEqual('test-query') + expect(custom_data.value).toEqual(1000) + + expect(user_data.external_id[0]).toEqual(hash(testEvent.userId ?? '')) + expect(user_data.em[0]).toEqual('388c735eec8225c4ad7a507944dd0a975296baea383198aa87177f29af2c6f69') + expect(user_data.ph[0]).toEqual('d74dcef7e1dba5aed01b62bc932ab1a7848ed9a590ca029aa958a1bf82b6993d') + expect(user_data.fn).toEqual(undefined) + expect(user_data.ln).toEqual(undefined) + expect(user_data.ge).toEqual(undefined) + expect(user_data.db).toEqual(undefined) + expect(user_data.ct).toEqual(undefined) + expect(user_data.st).toEqual(undefined) + expect(user_data.country).toEqual(undefined) + expect(user_data.zp).toEqual(undefined) + expect(user_data.client_ip_address).toBe(testEvent.context?.ip) + expect(user_data.client_user_agent).toBe(testEvent.context?.userAgent) + expect(user_data.idfv).toBe(testEvent.context?.device?.id) + expect(user_data.madid).toBe(testEvent.context?.device?.advertisingId) + expect(user_data.sc_click_id).toEqual((testEvent.integrations?.['Snap Conversions Api'] as any)?.click_id) + expect(user_data.sc_cookie1).toEqual((testEvent.integrations?.['Snap Conversions Api'] as any)?.uuid_c1) + }) }) }) diff --git a/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/snap-capi-v3.ts b/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/snap-capi-v3.ts index 10cabfcf6a..51ba369711 100644 --- a/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/snap-capi-v3.ts +++ b/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/snap-capi-v3.ts @@ -1,19 +1,19 @@ -import { ExecuteInput, ModifiedResponse, RequestClient } from '@segment/actions-core' +import { ExecuteInput, Features, ModifiedResponse, RequestClient } from '@segment/actions-core' import { Payload } from './generated-types' import { Settings } from '../generated-types' import { box, emptyObjectToUndefined, - hash, - hashEmailSafe, isNullOrUndefined, splitListValueToArray, raiseMisconfiguredRequiredFieldErrorIf, raiseMisconfiguredRequiredFieldErrorIfNullOrUndefined, emptyStringToUndefined, parseNumberSafe, - parseDateSafe + parseDateSafe, + smartHash } from './utils' +import { processHashing } from '../../../lib/hashing-utils' const CURRENCY_ISO_4217_CODES = new Set([ 'USD', @@ -426,53 +426,54 @@ const buildAppData = (payload: Payload, settings: Settings) => { } } -const buildUserData = (payload: Payload) => { +const buildUserData = (payload: Payload, features?: Features) => { const { user_data } = payload // Removes all leading and trailing whitespace and converts all characters to lowercase. - const normalizedEmail = (user_data?.email ?? payload.email)?.replace(/\s/g, '').toLowerCase() - const email = hashEmailSafe(normalizedEmail) + const normalizedValue = (value: string) => value?.replace(/\s/g, '').toLowerCase() + const email = smartHash(user_data?.email ?? payload.email, features, normalizedValue) // Removes all non-numberic characters and leading zeros. - const normalizedPhoneNumber = (user_data?.phone ?? payload.phone_number)?.replace(/\D|^0+/g, '') - const phone_number = hash(normalizedPhoneNumber) + const normalizedPhoneNumber = (value: string) => value?.replace(/\D|^0+/g, '') + const phone_number = smartHash(user_data?.phone ?? payload.phone_number, features, normalizedPhoneNumber) // Converts all characters to lowercase const madid = (user_data?.madid ?? payload.mobile_ad_id)?.toLowerCase() - const normalizedGender = user_data?.gender?.replace(/\s/g, '').toLowerCase() - const gender = normalizedGender === 'male' ? 'm' : normalizedGender === 'female' ? 'f' : normalizedGender - const hashedGender = hash(gender) + const normalizedGender = (value: string): string => { + const normalizedValue = value?.replace(/\s/g, '').toLowerCase() + return normalizedValue === 'male' ? 'm' : normalizedValue === 'female' ? 'f' : normalizedValue + } + const hashedGender = smartHash(user_data?.gender, features, normalizedGender) - const normalizedLastName = user_data?.lastName?.replace(/\s/g, '')?.toLowerCase() - const hashedLastName = hash(normalizedLastName) + const hashedLastName = smartHash(user_data?.lastName, features, normalizedValue) - const normalizedFirstName = user_data?.firstName?.replace(/\s/g, '')?.toLowerCase() - const hashedFirstName = hash(normalizedFirstName) + const hashedFirstName = smartHash(user_data?.firstName, features, normalizedValue) const client_ip_address = user_data?.client_ip_address ?? payload.ip_address const client_user_agent = user_data?.client_user_agent ?? payload.user_agent - const normalizedCity = user_data?.city?.replace(/\s/g, '')?.toLowerCase() - const hashedCity = hash(normalizedCity) + const hashedCity = smartHash(user_data?.city, features, normalizedValue) - const normalizedState = user_data?.state?.replace(/\s/g, '').toLowerCase() // checks if the full US state name is used instead of the two letter abbreviation - const state = US_STATE_CODES.get(normalizedState ?? '') ?? normalizedState - const hashedState = hash(state) + const normalizedState = (value: string): string => { + const normalizedValue = value?.replace(/\s/g, '').toLowerCase() + return US_STATE_CODES.get(normalizedValue ?? '') ?? normalizedValue + } + const hashedState = smartHash(user_data?.state, features, normalizedState) - const normalizedZip = user_data?.zip?.replace(/\s/g, '').toLowerCase() - const hashedZip = hash(normalizedZip) + const hashedZip = smartHash(user_data?.zip, features, normalizedValue) - const normalizedCountry = user_data?.country?.replace(/\s/g, '').toLowerCase() - const country = COUNTRY_CODES.get(normalizedCountry ?? '') ?? normalizedCountry - const hashedCountry = hash(country) + const normalizedCountry = (value: string): string => { + const normalizedValue = value?.replace(/\s/g, '').toLowerCase() + return COUNTRY_CODES.get(normalizedValue ?? '') ?? normalizedValue + } + const hashedCountry = smartHash(user_data?.country, features, normalizedCountry) const external_id = user_data?.externalId?.map((id) => { - const normalizedId = id.replace(/\s/g, '').toLowerCase() - return hash(normalizedId) as string + return processHashing(id, 'sha256', 'hex', features, 'actions-snap-conversions', normalizedValue) }) - const db = hash(user_data?.dateOfBirth) + const db = smartHash(user_data?.dateOfBirth, features) const lead_id = user_data?.leadID const subscription_id = user_data?.subscriptionID @@ -605,7 +606,7 @@ const getSupportedActionSource = (action_source: string | undefined): string | u : undefined } -const buildPayloadData = (payload: Payload, settings: Settings) => { +const buildPayloadData = (payload: Payload, settings: Settings, features?: Features) => { // event_conversion_type is a required parameter whose value is enforced as // always OFFLINE, WEB, or MOBILE_APP, so in practice action_source will always have a value. const action_source = @@ -625,7 +626,7 @@ const buildPayloadData = (payload: Payload, settings: Settings) => { const event_time = event_time_date_time ?? event_time_number const app_data = action_source === 'app' ? buildAppData(payload, settings) : undefined - const user_data = buildUserData(payload) + const user_data = buildUserData(payload, features) const custom_data = buildCustomData(payload) const data_processing_options = payload.data_processing_options ?? false ? ['LDU'] : undefined @@ -729,11 +730,12 @@ const validatePayload = (payload: ReturnType) => { export const performSnapCAPIv3 = async ( request: RequestClient, - data: ExecuteInput + data: ExecuteInput, + features?: Features ): Promise> => { const { payload, settings } = data - const payloadData = buildPayloadData(payload, settings) + const payloadData = buildPayloadData(payload, settings, features) validatePayload(payloadData) validateSettingsConfig(settings, payloadData.action_source) diff --git a/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/utils.ts b/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/utils.ts index 3ff37226f6..db6940416e 100644 --- a/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/utils.ts +++ b/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/utils.ts @@ -1,5 +1,6 @@ -import { IntegrationError } from '@segment/actions-core' +import { Features, IntegrationError } from '@segment/actions-core' import { createHash } from 'crypto' +import { processHashing } from '../../../lib/hashing-utils' export const isNullOrUndefined = (v: T | null | undefined): v is null | undefined => v == null @@ -82,3 +83,13 @@ export const parseDateSafe = (v: string | undefined): number | undefined => { const parsed = Date.parse(v ?? '') return Number.isSafeInteger(parsed) ? parsed : undefined } + +export const smartHash = ( + value: string | undefined, + features?: Features, + cleaningFunction?: (value: string) => string +): string | undefined => { + if (value === undefined) return + + return processHashing(value, 'sha256', 'hex', features, 'actions-snap-conversions', cleaningFunction) +} diff --git a/packages/destination-actions/src/lib/hashing-utils.ts b/packages/destination-actions/src/lib/hashing-utils.ts index c53a3d2d16..d090858375 100644 --- a/packages/destination-actions/src/lib/hashing-utils.ts +++ b/packages/destination-actions/src/lib/hashing-utils.ts @@ -27,6 +27,7 @@ export const hashConfigs: { const slugsToBypassFeatureFlag = [ 'actions-facebook-custom-audiences', 'actions-linkedin-audiences', + 'actions-snap-conversions', 'actions-tiktok-offline-conversions', 'tiktok-conversions', 'actions-google-enhanced-conversions', From d03fb1b6322910259fa1bb6c977f83757ec5aadc Mon Sep 17 00:00:00 2001 From: Ankit Gupta <139338151+AnkitSegment@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:44:50 +0530 Subject: [PATCH 22/78] [SMART-HASHING] [STRATCONN-5637] Updated smart-hashing in dynamic-yield (#2839) * Updated smart-hashing in dynamic-yield * Removed email formating * Fixed test cases --- .../dynamic-yield-audiences/helpers.ts | 23 +++++++++++-------- .../dynamic-yield-audiences/index.ts | 2 +- .../syncAudience/index.ts | 10 ++++---- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/packages/destination-actions/src/destinations/dynamic-yield-audiences/helpers.ts b/packages/destination-actions/src/destinations/dynamic-yield-audiences/helpers.ts index 0c85abce95..9eba490db5 100644 --- a/packages/destination-actions/src/destinations/dynamic-yield-audiences/helpers.ts +++ b/packages/destination-actions/src/destinations/dynamic-yield-audiences/helpers.ts @@ -1,11 +1,12 @@ -import { createHash } from 'crypto' +import { Features } from '@segment/actions-core' +import { processHashing } from '../../lib/hashing-utils' -export function hashAndEncode(property: string): string { - return createHash('sha256').update(property).digest('hex') +export function hashAndEncode(property: string, features: Features): string { + return processHashing(property, 'sha256', 'hex', features, 'actions-dynamic-yield-audiences') } -export function hashAndEncodeToInt(property: string): number { - const hash = createHash('sha256').update(property).digest('hex') +export function hashAndEncodeToInt(property: string, features: Features): number { + const hash = processHashing(property, 'sha256', 'hex', features, 'actions-dynamic-yield-audiences') const bigInt = BigInt('0x' + hash) let integerString = bigInt.toString() if (integerString.length > 16) { @@ -38,10 +39,14 @@ export function getCreateAudienceURL(dataCenter: string): string { return `https://cdp-extensions-api.${getDomain(dataCenter)}.dynamicyield.com/cdp/segment/audiences/subscription` } -export function getDataCenter(sectionId: string){ - return sectionId.toLocaleLowerCase().startsWith("9") ? "EU" : sectionId.toLocaleLowerCase().startsWith("8") ? "US" : "DEV" +export function getDataCenter(sectionId: string) { + return sectionId.toLocaleLowerCase().startsWith('9') + ? 'EU' + : sectionId.toLocaleLowerCase().startsWith('8') + ? 'US' + : 'DEV' } -export function getSectionId(sectionId: string){ +export function getSectionId(sectionId: string) { return sectionId.toLocaleLowerCase().replace('dev', '') -} \ No newline at end of file +} diff --git a/packages/destination-actions/src/destinations/dynamic-yield-audiences/index.ts b/packages/destination-actions/src/destinations/dynamic-yield-audiences/index.ts index a157d19c93..5cfe503926 100644 --- a/packages/destination-actions/src/destinations/dynamic-yield-audiences/index.ts +++ b/packages/destination-actions/src/destinations/dynamic-yield-audiences/index.ts @@ -90,7 +90,7 @@ const destination: AudienceDestinationDefinition = { throw new IntegrationError('Missing computation parameters: Id and Key', 'MISSING_REQUIRED_FIELD', 400) } - const audience_id = hashAndEncodeToInt(personas.computation_id) + const audience_id = hashAndEncodeToInt(personas.computation_id, createAudienceInput.features || {}) const json = { type: 'audience_subscription_request', diff --git a/packages/destination-actions/src/destinations/dynamic-yield-audiences/syncAudience/index.ts b/packages/destination-actions/src/destinations/dynamic-yield-audiences/syncAudience/index.ts index a68b57d32c..6839354cc1 100644 --- a/packages/destination-actions/src/destinations/dynamic-yield-audiences/syncAudience/index.ts +++ b/packages/destination-actions/src/destinations/dynamic-yield-audiences/syncAudience/index.ts @@ -72,13 +72,15 @@ const action: ActionDefinition = { default: { '@path': '$.context.personas.computation_class' }, - choices: [{ label: 'audience', value: 'audience' },{ label: 'journey_step', value: 'journey_step' }] + choices: [ + { label: 'audience', value: 'audience' }, + { label: 'journey_step', value: 'journey_step' } + ] }, email: { label: 'Email', description: "User's email address", type: 'string', - format: 'email', required: false, unsafe_hidden: true, default: { @@ -107,7 +109,7 @@ const action: ActionDefinition = { } }, - perform: async (request, { audienceSettings, payload, settings }) => { + perform: async (request, { audienceSettings, payload, settings, features }) => { const { external_audience_id } = payload if (!audienceSettings?.audience_name) { @@ -174,7 +176,7 @@ const action: ActionDefinition = { { type: idTypeToSend, encoding: idTypeToSend === 'email' ? '"sha-256"' : 'raw', - value: idTypeToSend === 'email' ? hashAndEncode(primaryIdentifier) : primaryIdentifier + value: idTypeToSend === 'email' ? hashAndEncode(primaryIdentifier, features || {}) : primaryIdentifier } ], audiences: [ From 29fba410274ca4a8a90f962ed5b36ef8c90b692a Mon Sep 17 00:00:00 2001 From: harsh-joshi99 <129737395+harsh-joshi99@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:57:07 +0530 Subject: [PATCH 23/78] Return undefined from smart hash if hash field are not defined (#2841) --- .../__tests__/index.test.ts | 104 ++++++++++++++++++ .../conversionUpload/__tests__/index.test.ts | 102 +++++++++++++++++ .../google-campaign-manager-360/utils.ts | 30 ++--- .../__tests__/index.test.ts | 75 +++++++++++++ .../reddit-conversions-api/utils.ts | 47 +++----- 5 files changed, 304 insertions(+), 54 deletions(-) diff --git a/packages/destination-actions/src/destinations/google-campaign-manager-360/conversionAdjustmentUpload/__tests__/index.test.ts b/packages/destination-actions/src/destinations/google-campaign-manager-360/conversionAdjustmentUpload/__tests__/index.test.ts index e2d681c388..782e109b10 100644 --- a/packages/destination-actions/src/destinations/google-campaign-manager-360/conversionAdjustmentUpload/__tests__/index.test.ts +++ b/packages/destination-actions/src/destinations/google-campaign-manager-360/conversionAdjustmentUpload/__tests__/index.test.ts @@ -264,6 +264,110 @@ describe('CampaignManager360.conversionAdjustmentUpload', () => { expect(responses.length).toBe(1) expect(responses[0].status).toBe(200) }) + + it('sends an event and handles hashing with partial data correctly', async () => { + const event = createTestEvent({ + timestamp, + event: 'Test Event', + context: { + traits: { + email: 'daffy@warnerbros.com' + } + }, + properties: { + ordinal: '1', + quantity: '2', + value: '100', + gclid: '54321', + limitAdTracking: true, + childDirectedTreatment: true, + nonPersonalizedAd: true, + treatmentForUnderage: true + } + }) + + delete event.userId + + nock(`https://dfareporting.googleapis.com/dfareporting/v4/userprofiles/${profileId}/conversions/batchupdate`) + .post('') + .reply(200, { results: [{}] }) + + const responses = await testDestination.testAction('conversionAdjustmentUpload', { + event, + mapping: { + requiredId: { + gclid: { + '@path': '$.properties.gclid' + } + }, + timestamp: { + '@path': '$.timestamp' + }, + value: { + '@path': '$.properties.value' + }, + quantity: { + '@path': '$.properties.quantity' + }, + ordinal: { + '@path': '$.properties.ordinal' + }, + userDetails: { + email: { + '@path': '$.context.traits.email' + }, + phone: { + '@path': '$.context.traits.phone' + }, + firstName: { + '@path': '$.context.traits.firstName' + }, + lastName: { + '@path': '$.context.traits.lastName' + }, + streetAddress: { + '@path': '$.context.traits.streetAddress' + }, + city: { + '@path': '$.context.traits.city' + }, + state: { + '@path': '$.context.traits.state' + }, + postalCode: { + '@path': '$.context.traits.postalCode' + }, + countryCode: { + '@path': '$.context.traits.countryCode' + } + }, + limitAdTracking: { + '@path': '$.properties.limitAdTracking' + }, + childDirectedTreatment: { + '@path': '$.properties.childDirectedTreatment' + }, + nonPersonalizedAd: { + '@path': '$.properties.nonPersonalizedAd' + }, + treatmentForUnderage: { + '@path': '$.properties.treatmentForUnderage' + } + }, + useDefaultMappings: true, + settings: { + profileId, + defaultFloodlightActivityId: floodlightActivityId, + defaultFloodlightConfigurationId: floodlightConfigurationId + } + }) + + expect(responses[0].options.body).toBe( + `{"conversions":[{"childDirectedTreatment":true,"floodlightActivityId":"23456","floodlightConfigurationId":"34567","gclid":"54321","kind":"dfareporting#conversion","limitAdTracking":true,"nonPersonalizedAd":true,"ordinal":"1","quantity":"2","timestampMicros":"1718042884000","treatmentForUnderage":true,"userIdentifiers":[{"hashedEmail":"8e46bd4eaabb5d6324e327751b599f190dbaacd90066e66c94a046640bed60d0"}],"value":100,"customVariables":[],"encryptedUserIdCandidates":[]}],"kind":"dfareporting#conversionsBatchInsertRequest"}` + ) + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(200) + }) }) describe('Batch', () => { diff --git a/packages/destination-actions/src/destinations/google-campaign-manager-360/conversionUpload/__tests__/index.test.ts b/packages/destination-actions/src/destinations/google-campaign-manager-360/conversionUpload/__tests__/index.test.ts index 9dc298f389..6bf3ef1846 100644 --- a/packages/destination-actions/src/destinations/google-campaign-manager-360/conversionUpload/__tests__/index.test.ts +++ b/packages/destination-actions/src/destinations/google-campaign-manager-360/conversionUpload/__tests__/index.test.ts @@ -263,6 +263,108 @@ describe('Cm360.conversionUpload', () => { expect(responses.length).toBe(1) expect(responses[0].status).toBe(201) }) + + it('sends an event and handles hashing with partial data correctly', async () => { + const event = createTestEvent({ + timestamp, + event: 'Test Event', + context: { + traits: { + email: 'daffy@warnerbros.com' + } + }, + properties: { + ordinal: '1', + quantity: '1', + value: '123', + gclid: '54321', + limitAdTracking: true, + childDirectedTreatment: true, + nonPersonalizedAd: true, + treatmentForUnderage: true + } + }) + + nock(`https://dfareporting.googleapis.com/dfareporting/v4/userprofiles/${profileId}/conversions/batchinsert`) + .post('') + .reply(201, { results: [{}] }) + + const responses = await testDestination.testAction('conversionUpload', { + event, + mapping: { + requiredId: { + gclid: { + '@path': '$.properties.gclid' + } + }, + timestamp: { + '@path': '$.timestamp' + }, + value: { + '@path': '$.properties.value' + }, + quantity: { + '@path': '$.properties.quantity' + }, + ordinal: { + '@path': '$.properties.ordinal' + }, + userDetails: { + email: { + '@path': '$.context.traits.email' + }, + phone: { + '@path': '$.context.traits.phone' + }, + firstName: { + '@path': '$.context.traits.firstName' + }, + lastName: { + '@path': '$.context.traits.lastName' + }, + streetAddress: { + '@path': '$.context.traits.streetAddress' + }, + city: { + '@path': '$.context.traits.city' + }, + state: { + '@path': '$.context.traits.state' + }, + postalCode: { + '@path': '$.context.traits.postalCode' + }, + countryCode: { + '@path': '$.context.traits.countryCode' + } + }, + limitAdTracking: { + '@path': '$.properties.limitAdTracking' + }, + childDirectedTreatment: { + '@path': '$.properties.childDirectedTreatment' + }, + nonPersonalizedAd: { + '@path': '$.properties.nonPersonalizedAd' + }, + treatmentForUnderage: { + '@path': '$.properties.treatmentForUnderage' + } + }, + useDefaultMappings: true, + settings: { + profileId, + defaultFloodlightActivityId: floodlightActivityId, + defaultFloodlightConfigurationId: floodlightConfigurationId + } + }) + + expect(responses[0].options.body).toBe( + `{"conversions":[{"childDirectedTreatment":true,"floodlightActivityId":"23456","floodlightConfigurationId":"34567","gclid":"54321","kind":"dfareporting#conversion","limitAdTracking":true,"nonPersonalizedAd":true,"ordinal":"1","quantity":"1","timestampMicros":"1718042884000","treatmentForUnderage":true,"userIdentifiers":[{"hashedEmail":"8e46bd4eaabb5d6324e327751b599f190dbaacd90066e66c94a046640bed60d0"}],"value":123,"customVariables":[],"encryptedUserIdCandidates":[]}],"kind":"dfareporting#conversionsBatchInsertRequest"}` + ) + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + }) }) describe('Batch', () => { diff --git a/packages/destination-actions/src/destinations/google-campaign-manager-360/utils.ts b/packages/destination-actions/src/destinations/google-campaign-manager-360/utils.ts index ecdfa924c5..3202322b5e 100644 --- a/packages/destination-actions/src/destinations/google-campaign-manager-360/utils.ts +++ b/packages/destination-actions/src/destinations/google-campaign-manager-360/utils.ts @@ -169,27 +169,15 @@ export function getJSON( } if (firstName || lastName || streetAddress || city || state || postalCode || countryCode) { const addressInfo: AddressInfo = { - hashedFirstName: processHashing( - firstName ?? '', - 'sha256', - 'hex', - features, - 'actions-google-campaign-manager-360' - ), - hashedLastName: processHashing( - lastName ?? '', - 'sha256', - 'hex', - features, - 'actions-google-campaign-manager-360' - ), - hashedStreetAddress: processHashing( - streetAddress ?? '', - 'sha256', - 'hex', - features, - 'actions-google-campaign-manager-360' - ), + hashedFirstName: firstName + ? processHashing(firstName, 'sha256', 'hex', features, 'actions-google-campaign-manager-360') + : undefined, + hashedLastName: lastName + ? processHashing(lastName, 'sha256', 'hex', features, 'actions-google-campaign-manager-360') + : undefined, + hashedStreetAddress: streetAddress + ? processHashing(streetAddress, 'sha256', 'hex', features, 'actions-google-campaign-manager-360') + : undefined, city: city ?? undefined, state: state ?? undefined, postalCode: postalCode ?? undefined, diff --git a/packages/destination-actions/src/destinations/reddit-conversions-api/__tests__/index.test.ts b/packages/destination-actions/src/destinations/reddit-conversions-api/__tests__/index.test.ts index 46402c6bd6..b67f70df25 100644 --- a/packages/destination-actions/src/destinations/reddit-conversions-api/__tests__/index.test.ts +++ b/packages/destination-actions/src/destinations/reddit-conversions-api/__tests__/index.test.ts @@ -429,5 +429,80 @@ describe('Reddit Conversions Api', () => { test_mode: false }) }) + + it('should not add empty string for non existing fields', async () => { + const event = createTestEvent({ + timestamp: timestamp, + event: 'Lead Generated', + messageId: 'test-message-id-contact', + type: 'track', + userId: 'user_id_1', + properties: { + click_id: 'click_id_1', + currency: 'USD', + total: 100, + uuid: 'uuid_1', + products: [ + { product_id: 'product_id_1', category: 'category_1', name: 'name_1' }, + { product_id: 'product_id_2', category: 'category_2', name: 'name_2' } + ], + email: 'f660ab912ec121d1b1e928a0bb4bc61b15f5ad44d5efdc4e1c92a25e99b8e44a' + }, + context: { + userAgent: 'test-user-agent', + device: { + advertisingId: 'advertising_id_1' + } + } + }) + + nock('https://ads-api.reddit.com').post('/api/v2.0/conversions/events/ad_account_id_1').reply(200, {}) + const responses = await testDestination.testAction('standardEvent', { + event, + settings, + useDefaultMappings: true, + mapping: { + tracking_type: 'Lead' + }, + features: { 'smart-hashing': true } + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(200) + expect(responses[0].options.json).toMatchObject({ + events: [ + { + click_id: 'click_id_1', + event_at: '2024-01-08T13:52:50.212Z', + event_metadata: { + currency: 'USD', + products: [ + { + category: 'category_1', + id: 'product_id_1', + name: 'name_1' + }, + { + category: 'category_2', + id: 'product_id_2', + name: 'name_2' + } + ], + value_decimal: 100 + }, + event_type: { + tracking_type: 'Lead' + }, + user: { + email: 'f660ab912ec121d1b1e928a0bb4bc61b15f5ad44d5efdc4e1c92a25e99b8e44a', + user_agent: 'test-user-agent', + uuid: 'uuid_1' + } + } + ], + partner: 'SEGMENT', + test_mode: false + }) + }) }) }) diff --git a/packages/destination-actions/src/destinations/reddit-conversions-api/utils.ts b/packages/destination-actions/src/destinations/reddit-conversions-api/utils.ts index aa5d3c7690..ac85303d95 100644 --- a/packages/destination-actions/src/destinations/reddit-conversions-api/utils.ts +++ b/packages/destination-actions/src/destinations/reddit-conversions-api/utils.ts @@ -117,14 +117,7 @@ function getMetadata( item_count: cleanNum(metadata?.item_count), value_decimal: cleanNum(metadata?.value_decimal), products: getProducts(products), - conversion_id: processHashing( - conversion_id ?? '', - 'sha256', - 'hex', - features, - 'actions-reddit-conversions-api', - (value) => value.trim() - ) + conversion_id: smartHash(conversion_id, features, (value) => value.trim()) } } @@ -135,7 +128,7 @@ function getAdId( ): { [key: string]: string | undefined } | undefined { if (!device_type) return undefined if (!advertising_id) return undefined - const hashedAdId = processHashing(advertising_id, 'sha256', 'hex', features, 'actions-reddit-conversions-api') + const hashedAdId = smartHash(advertising_id, features) return device_type === 'ios' ? { idfa: hashedAdId } : { aaid: hashedAdId } } @@ -168,30 +161,9 @@ function getUser( return { ...getAdId(user.device_type, user.advertising_id, features), - email: processHashing( - user.email ?? '', - 'sha256', - 'hex', - features, - 'actions-reddit-conversions-api', - canonicalizeEmail - ), - external_id: processHashing( - user.external_id ?? '', - 'sha256', - 'hex', - features, - 'actions-reddit-conversions-api', - (value) => value.trim() - ), - ip_address: processHashing( - user.ip_address ?? '', - 'sha256', - 'hex', - features, - 'actions-reddit-conversions-api', - (value) => value.trim() - ), + email: smartHash(user.email, features, canonicalizeEmail), + external_id: smartHash(user.external_id, features, (value) => value.trim()), + ip_address: smartHash(user.ip_address, features, (value) => value.trim()), user_agent: clean(user.user_agent), uuid: clean(user.uuid), data_processing_options: getDataProcessingOptions(dataProcessingOptions), @@ -206,3 +178,12 @@ function canonicalizeEmail(value: string): string { const localPart = localPartAndDomain[0].replace(/\./g, '').split('+')[0] return `${localPart.toLowerCase()}@${localPartAndDomain[1].toLowerCase()}` } + +const smartHash = ( + value: string | undefined, + features?: Features, + cleaningFunction?: (value: string) => string +): string | undefined => { + if (value === undefined) return + return processHashing(value, 'sha256', 'hex', features, 'actions-reddit-conversions-api', cleaningFunction) +} From e2e01850ca88fd6d2a7eb05037d442860d22f48a Mon Sep 17 00:00:00 2001 From: harsh-joshi99 <129737395+harsh-joshi99@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:57:16 +0530 Subject: [PATCH 24/78] [Smart Hashing] [Snap Audience] Implement Smart hashing for Snap Audience (#2843) * Implement Smart hashing for Snap Audience * Remove console log --- .../syncAudience/__tests__/index.test.ts | 28 ++++++++------ .../snap-audiences/syncAudience/index.ts | 15 ++++---- .../snap-audiences/syncAudience/utils.ts | 38 +++++++------------ .../src/lib/hashing-utils.ts | 1 + 4 files changed, 38 insertions(+), 44 deletions(-) diff --git a/packages/destination-actions/src/destinations/snap-audiences/syncAudience/__tests__/index.test.ts b/packages/destination-actions/src/destinations/snap-audiences/syncAudience/__tests__/index.test.ts index 71dc3cb306..285f4f3f7a 100644 --- a/packages/destination-actions/src/destinations/snap-audiences/syncAudience/__tests__/index.test.ts +++ b/packages/destination-actions/src/destinations/snap-audiences/syncAudience/__tests__/index.test.ts @@ -1,7 +1,8 @@ import nock from 'nock' import { createTestEvent, createTestIntegration, PayloadValidationError } from '@segment/actions-core' import Destination from '../../index' -import { normalizeAndHash, normalizeAndHashPhone } from '../utils' +import { processHashing } from '../../../../lib/hashing-utils' +import { normalize, normalizePhone } from '../utils' const testDestination = createTestIntegration(Destination) @@ -128,14 +129,17 @@ describe('Snapchat Audiences syncAudience', () => { ).rejects.toThrowError(PayloadValidationError) }) it('should normalize and hash identifiers', async () => { + const features = { 'smart-hashing': true } + const slug = 'actions-snap-audiences' + const email1 = 'person@email.com' const email2 = 'Person@email.com' const email3 = 'Person@email.com ' const hashedEmail = 'b375b7bbddb3de3298fbc7641063d9f03a38e118aa4480c8ab9f58740982e8bd' - const email1Res = normalizeAndHash(email1) - const email2Res = normalizeAndHash(email2) - const email3Res = normalizeAndHash(email3) - const alreadyHashedResEmail = normalizeAndHash(hashedEmail) + const email1Res = processHashing(email1, 'sha256', 'hex', features, slug, normalize) + const email2Res = processHashing(email2, 'sha256', 'hex', features, slug, normalize) + const email3Res = processHashing(email3, 'sha256', 'hex', features, slug, normalize) + const alreadyHashedResEmail = processHashing(hashedEmail, 'sha256', 'hex', features, slug, normalize) expect(email1Res).toEqual(hashedEmail) expect(email2Res).toEqual(hashedEmail) expect(email3Res).toEqual(hashedEmail) @@ -145,10 +149,10 @@ describe('Snapchat Audiences syncAudience', () => { const phone2 = '001(706)-767-5127' const phone3 = '01(706)-767-5127' const hashedPhone = '2fd199db6d3fa9fe754886ff1822f2867fcb104a6639495eca25c2978efe4ed4' - const phone1Res = normalizeAndHashPhone(phone1) - const phone2Res = normalizeAndHashPhone(phone2) - const phone3Res = normalizeAndHashPhone(phone3) - const alreadyHashedResPhone = normalizeAndHashPhone(hashedPhone) + const phone1Res = processHashing(phone1, 'sha256', 'hex', features, slug, normalizePhone) + const phone2Res = processHashing(phone2, 'sha256', 'hex', features, slug, normalizePhone) + const phone3Res = processHashing(phone3, 'sha256', 'hex', features, slug, normalizePhone) + const alreadyHashedResPhone = processHashing(hashedPhone, 'sha256', 'hex', features, slug, normalizePhone) expect(phone1Res).toEqual(hashedPhone) expect(phone2Res).toEqual(hashedPhone) expect(phone3Res).toEqual(hashedPhone) @@ -157,9 +161,9 @@ describe('Snapchat Audiences syncAudience', () => { const mobileAdId1 = 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6' const mobileAdId2 = 'F81D4FAE-7DEC-11D0-A765-00A0C91E6BF6' const hashedMobileAdId = '30a5154b77ab8b2ddbe19f5e7af72f33cc2a4a41f22940d965102650a1c72863' - const mobileAdId1Res = normalizeAndHash(mobileAdId1) - const mobileAdId2Res = normalizeAndHash(mobileAdId2) - const alreadyHashedResMobileAdId = normalizeAndHash(hashedMobileAdId) + const mobileAdId1Res = processHashing(mobileAdId1, 'sha256', 'hex', features, slug, normalize) + const mobileAdId2Res = processHashing(mobileAdId2, 'sha256', 'hex', features, slug, normalize) + const alreadyHashedResMobileAdId = processHashing(hashedMobileAdId, 'sha256', 'hex', features, slug, normalize) expect(mobileAdId1Res).toEqual(hashedMobileAdId) expect(mobileAdId2Res).toEqual(hashedMobileAdId) expect(alreadyHashedResMobileAdId).toEqual(hashedMobileAdId) diff --git a/packages/destination-actions/src/destinations/snap-audiences/syncAudience/index.ts b/packages/destination-actions/src/destinations/snap-audiences/syncAudience/index.ts index 530d79b4e7..ac8eb45a6b 100644 --- a/packages/destination-actions/src/destinations/snap-audiences/syncAudience/index.ts +++ b/packages/destination-actions/src/destinations/snap-audiences/syncAudience/index.ts @@ -1,4 +1,4 @@ -import type { ActionDefinition, RequestClient } from '@segment/actions-core' +import type { ActionDefinition, RequestClient, Features } from '@segment/actions-core' import { PayloadValidationError } from '@segment/actions-core' import type { Settings } from '../generated-types' import type { Payload } from './generated-types' @@ -72,7 +72,6 @@ const action: ActionDefinition = { label: 'Email', description: "The user's email address.", type: 'string', - format: 'email', required: false, default: { '@path': '$.context.traits.email' }, depends_on: { @@ -114,19 +113,19 @@ const action: ActionDefinition = { default: true } }, - perform: async (request, { payload }) => { - return processPayload(request, [payload]) + perform: async (request, { payload, features }) => { + return processPayload(request, [payload], features) }, - performBatch: async (request, { payload }) => { - return processPayload(request, payload) + performBatch: async (request, { payload, features }) => { + return processPayload(request, payload, features) } } export default action -const processPayload = async (request: RequestClient, payload: Payload[]) => { +const processPayload = async (request: RequestClient, payload: Payload[], features?: Features) => { const { external_audience_id, schema_type } = payload[0] - const { enteredAudience, exitedAudience } = sortPayload(payload) + const { enteredAudience, exitedAudience } = sortPayload(payload, features) if (enteredAudience.length === 0 && exitedAudience.length === 0) { throw new PayloadValidationError(`No ${validationError(schema_type)} identifier present in payload(s)`) diff --git a/packages/destination-actions/src/destinations/snap-audiences/syncAudience/utils.ts b/packages/destination-actions/src/destinations/snap-audiences/syncAudience/utils.ts index f9612289b8..337f998612 100644 --- a/packages/destination-actions/src/destinations/snap-audiences/syncAudience/utils.ts +++ b/packages/destination-actions/src/destinations/snap-audiences/syncAudience/utils.ts @@ -1,8 +1,9 @@ +import { Features } from '@segment/actions-core/mapping-kit' import type { Payload } from './generated-types' -import { createHash } from 'crypto' +import { processHashing } from '../../../lib/hashing-utils' // Filters out events with missing identifiers and sorts based on audience entered/exited -export const sortPayload = (payload: Payload[]) => { +export const sortPayload = (payload: Payload[], features?: Features) => { return payload.reduce<{ enteredAudience: string[][] exitedAudience: string[][] @@ -13,7 +14,8 @@ export const sortPayload = (payload: Payload[]) => { payloadItem.schema_type, payloadItem.email, payloadItem.phone, - payloadItem.advertising_id + payloadItem.advertising_id, + features ) if (externalId) { @@ -47,33 +49,24 @@ const validateAndExtractIdentifier = ( schemaType: string, email: string | undefined, phone: string | undefined, - mobileAdId: string | undefined + mobileAdId: string | undefined, + features?: Features ): string | null => { if (schemaType === 'EMAIL_SHA256' && email) { - return normalizeAndHash(email) + return processHashing(email, 'sha256', 'hex', features, 'actions-snap-audiences', normalize) } if (schemaType === 'MOBILE_AD_ID_SHA256' && mobileAdId) { - return normalizeAndHash(mobileAdId) + return processHashing(mobileAdId, 'sha256', 'hex', features, 'actions-snap-audiences', normalize) } if (schemaType === 'PHONE_SHA256' && phone) { - return normalizeAndHashPhone(phone) + return processHashing(phone, 'sha256', 'hex', features, 'actions-snap-audiences', normalizePhone) } return null } -const hash = (value: string): string => { - const hash = createHash('sha256') - hash.update(value) - return hash.digest('hex') -} - -const isHashed = (identifier: string): boolean => new RegExp(/[0-9abcdef]{64}/gi).test(identifier) - -export const normalizeAndHash = (identifier: string): string => { - if (isHashed(identifier)) return identifier - const hashedIdentifier = hash(identifier.trim().toLowerCase()) - return hashedIdentifier +export const normalize = (identifier: string): string => { + return identifier.trim().toLowerCase() } /* @@ -82,9 +75,7 @@ export const normalizeAndHash = (identifier: string): string => { - if the number itself begins with a 0 this should be removed - Also exclude any non-numeric characters such as whitespace, parentheses, '+', or '-'. */ -export const normalizeAndHashPhone = (phone: string): string => { - if (isHashed(phone)) return phone - +export const normalizePhone = (phone: string): string => { // Remove non-numeric characters and parentheses, '+', '-', ' ' let normalizedPhone = phone.replace(/[\s()+-]/g, '') @@ -98,6 +89,5 @@ export const normalizeAndHashPhone = (phone: string): string => { normalizedPhone = normalizedPhone.substring(1) } - const hashedPhone = hash(normalizedPhone) - return hashedPhone + return normalizedPhone } diff --git a/packages/destination-actions/src/lib/hashing-utils.ts b/packages/destination-actions/src/lib/hashing-utils.ts index d090858375..5c19993027 100644 --- a/packages/destination-actions/src/lib/hashing-utils.ts +++ b/packages/destination-actions/src/lib/hashing-utils.ts @@ -27,6 +27,7 @@ export const hashConfigs: { const slugsToBypassFeatureFlag = [ 'actions-facebook-custom-audiences', 'actions-linkedin-audiences', + 'actions-snap-audiences', 'actions-snap-conversions', 'actions-tiktok-offline-conversions', 'tiktok-conversions', From 4ad4de7912ede22dde2ebb15ad1240c0c49eea8f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 18:25:48 +0530 Subject: [PATCH 25/78] Publish (#2845) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- packages/destination-actions/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/destination-actions/package.json b/packages/destination-actions/package.json index 3f02ea7b4f..3b02b4b245 100644 --- a/packages/destination-actions/package.json +++ b/packages/destination-actions/package.json @@ -1,7 +1,7 @@ { "name": "@segment/action-destinations", "description": "Destination Actions engine and definitions.", - "version": "3.380.0", + "version": "3.381.0", "repository": { "type": "git", "url": "https://github.com/segmentio/action-destinations", From a29fb3a427608ac374d70a80255b8fb83bfdc8db Mon Sep 17 00:00:00 2001 From: harsh-joshi99 <129737395+harsh-joshi99@users.noreply.github.com> Date: Wed, 2 Apr 2025 15:55:22 +0530 Subject: [PATCH 26/78] Add hashedPII category to all fields that are hashed using process hashing (#2847) --- packages/core/src/destination-kit/types.ts | 2 +- .../amazon-amc/syncAudiencesToDSP/index.ts | 24 +++++++---- .../delivrai-activate/updateSegment/index.ts | 6 ++- .../syncAudience/index.ts | 3 +- .../fb-capi-user-data.ts | 33 ++++++++++----- .../facebook-custom-audiences/sync/index.ts | 41 +++++++++++++------ .../first-party-dv360/properties.ts | 12 ++++-- .../common-fields.ts | 15 ++++--- .../conversionUpload/index.ts | 3 +- .../uploadClickConversion/index.ts | 6 ++- .../uploadClickConversion2/index.ts | 6 ++- .../uploadConversionAdjustment/index.ts | 6 ++- .../uploadConversionAdjustment2/index.ts | 6 ++- .../userList/index.ts | 12 ++++-- .../updateAudience/index.ts | 3 +- .../streamConversion/index.ts | 3 +- .../pinterset-capi-user-data.ts | 36 ++++++++++------ .../reddit-conversions-api/fields.ts | 15 ++++--- .../snap-audiences/syncAudience/index.ts | 9 ++-- .../snap-capi-input-fields-deprecated.ts | 6 ++- .../snap-capi-input-fields-v3.ts | 33 ++++++++++----- .../tiktok-audiences/properties.ts | 9 ++-- .../tiktok-conversions/common_fields.ts | 18 +++++--- .../common_fields.ts | 14 +++++-- .../yahoo-audiences/updateSegment/index.ts | 6 ++- 25 files changed, 219 insertions(+), 108 deletions(-) diff --git a/packages/core/src/destination-kit/types.ts b/packages/core/src/destination-kit/types.ts index ff6e81fddf..08ddbef84c 100644 --- a/packages/core/src/destination-kit/types.ts +++ b/packages/core/src/destination-kit/types.ts @@ -132,7 +132,7 @@ export interface GlobalSetting export type FieldTypeName = 'string' | 'text' | 'number' | 'integer' | 'datetime' | 'boolean' | 'password' | 'object' /** The supported field categories */ -type FieldCategory = 'identifier' | 'data' | 'internal' | 'config' | 'sync' +type FieldCategory = 'identifier' | 'data' | 'internal' | 'config' | 'sync' | 'hashedPII' /** supported input methods when picking values */ type FieldInputMethods = 'literal' | 'variable' | 'function' | 'enrichment' | 'freeform' diff --git a/packages/destination-actions/src/destinations/amazon-amc/syncAudiencesToDSP/index.ts b/packages/destination-actions/src/destinations/amazon-amc/syncAudiencesToDSP/index.ts index 1943ec37f3..226365c5ad 100644 --- a/packages/destination-actions/src/destinations/amazon-amc/syncAudiencesToDSP/index.ts +++ b/packages/destination-actions/src/destinations/amazon-amc/syncAudiencesToDSP/index.ts @@ -35,56 +35,64 @@ const action: ActionDefinition = { then: { '@path': '$.context.traits.email' }, else: { '@path': '$.properties.email' } } - } + }, + category: 'hashedPII' }, firstName: { label: 'First name', description: 'User first name. Value will be hashed before sending to Amazon.', type: 'string', required: false, - default: { '@path': '$.properties.first_name' } + default: { '@path': '$.properties.first_name' }, + category: 'hashedPII' }, lastName: { label: 'Last name', description: 'User Last name. Value will be hashed before sending to Amazon.', type: 'string', required: false, - default: { '@path': '$.properties.last_name' } + default: { '@path': '$.properties.last_name' }, + category: 'hashedPII' }, phone: { label: 'Phone', description: 'Phone Number. Value will be hashed before sending to Amazon.', type: 'string', required: false, - default: { '@path': '$.properties.phone' } + default: { '@path': '$.properties.phone' }, + category: 'hashedPII' }, postal: { label: 'Postal', description: 'POstal Code. Value will be hashed before sending to Amazon.', type: 'string', required: false, - default: { '@path': '$.properties.postal' } + default: { '@path': '$.properties.postal' }, + category: 'hashedPII' }, state: { label: 'State', description: 'State Code. Value will be hashed before sending to Amazon.', type: 'string', required: false, - default: { '@path': '$.properties.state' } + default: { '@path': '$.properties.state' }, + category: 'hashedPII' }, city: { label: 'City', description: 'City name. Value will be hashed before sending to Amazon.', type: 'string', required: false, - default: { '@path': '$.properties.city' } + default: { '@path': '$.properties.city' }, + category: 'hashedPII' }, address: { label: 'Address', description: 'Address Code. Value will be hashed before sending to Amazon.', type: 'string', required: false, - default: { '@path': '$.properties.address' } + default: { '@path': '$.properties.address' }, + category: 'hashedPII' }, audienceId: { label: 'Audience ID', diff --git a/packages/destination-actions/src/destinations/delivrai-activate/updateSegment/index.ts b/packages/destination-actions/src/destinations/delivrai-activate/updateSegment/index.ts index 19fe78c8d9..e359162677 100644 --- a/packages/destination-actions/src/destinations/delivrai-activate/updateSegment/index.ts +++ b/packages/destination-actions/src/destinations/delivrai-activate/updateSegment/index.ts @@ -32,7 +32,8 @@ const action: ActionDefinition = { then: { '@path': '$.traits.email' }, else: { '@path': '$.context.traits.email' } // Phone is sent as identify's trait or track's context.trait } - } + }, + category: 'hashedPII' }, phone: { label: 'User Phone', @@ -46,7 +47,8 @@ const action: ActionDefinition = { then: { '@path': '$.traits.phone' }, // Phone is sent as identify's trait or track's property else: { '@path': '$.properties.phone' } } - } + }, + category: 'hashedPII' }, device_type: { label: 'User Mobile Device Type', // This field is required to determine the type of the advertising Id: IDFA or GAID diff --git a/packages/destination-actions/src/destinations/dynamic-yield-audiences/syncAudience/index.ts b/packages/destination-actions/src/destinations/dynamic-yield-audiences/syncAudience/index.ts index 6839354cc1..7ad54c3eea 100644 --- a/packages/destination-actions/src/destinations/dynamic-yield-audiences/syncAudience/index.ts +++ b/packages/destination-actions/src/destinations/dynamic-yield-audiences/syncAudience/index.ts @@ -89,7 +89,8 @@ const action: ActionDefinition = { then: { '@path': '$.traits.email' }, else: { '@path': '$.context.traits.email' } } - } + }, + category: 'hashedPII' }, anonymousId: { label: 'Segment Anonymous Id', diff --git a/packages/destination-actions/src/destinations/facebook-conversions-api/fb-capi-user-data.ts b/packages/destination-actions/src/destinations/facebook-conversions-api/fb-capi-user-data.ts index c6945d9696..88b4fee3e0 100644 --- a/packages/destination-actions/src/destinations/facebook-conversions-api/fb-capi-user-data.ts +++ b/packages/destination-actions/src/destinations/facebook-conversions-api/fb-capi-user-data.ts @@ -19,58 +19,69 @@ export const user_data_field: InputField = { description: 'Any unique ID from the advertiser, such as loyalty membership IDs, user IDs, and external cookie IDs. You can send one or more external IDs for a given event.', type: 'string', - multiple: true // changed the type from string to array of strings. + multiple: true, // changed the type from string to array of strings. + category: 'hashedPII' }, email: { label: 'Email', description: 'An email address in lowercase.', - type: 'string' + type: 'string', + category: 'hashedPII' }, phone: { label: 'Phone', description: 'A phone number. Include only digits with country code, area code, and number. Remove symbols, letters, and any leading zeros. In addition, always include the country code, even if all of the data is from the same country, as the country code is used for matching.', - type: 'string' + type: 'string', + category: 'hashedPII' }, gender: { label: 'Gender', description: 'Gender in lowercase. Either f or m.', - type: 'string' + type: 'string', + category: 'hashedPII' }, dateOfBirth: { label: 'Date of Birth', description: 'A date of birth given as year, month, and day. Example: 19971226 for December 26, 1997.', - type: 'string' + type: 'string', + category: 'hashedPII' }, lastName: { label: 'Last Name', description: 'A last name in lowercase.', - type: 'string' + type: 'string', + category: 'hashedPII' }, firstName: { label: 'First Name', description: 'A first name in lowercase.', - type: 'string' + type: 'string', + category: 'hashedPII' }, city: { label: 'City', description: 'A city in lowercase without spaces or punctuation. Example: menlopark.', - type: 'string' + type: 'string', + category: 'hashedPII' }, state: { label: 'State', description: 'A two-letter state code in lowercase. Example: ca.', - type: 'string' + type: 'string', + category: 'hashedPII' }, zip: { label: 'Zip Code', description: 'A five-digit zip code for United States. For other locations, follow each country`s standards.', - type: 'string' + type: 'string', + category: 'hashedPII' }, country: { label: 'Country', description: 'A two-letter country code in lowercase.', - type: 'string' + type: 'string', + category: 'hashedPII' }, client_ip_address: { label: 'Client IP Address', diff --git a/packages/destination-actions/src/destinations/facebook-custom-audiences/sync/index.ts b/packages/destination-actions/src/destinations/facebook-custom-audiences/sync/index.ts index 9c1af4ff99..2e7d07dd08 100644 --- a/packages/destination-actions/src/destinations/facebook-custom-audiences/sync/index.ts +++ b/packages/destination-actions/src/destinations/facebook-custom-audiences/sync/index.ts @@ -157,24 +157,28 @@ const action: ActionDefinition = { type: 'string', required: true, label: 'External ID', + category: 'hashedPII', description: 'Your company’s custom identifier for this user. This can be any unique ID, such as loyalty membership IDs, user IDs, and external cookie IDs.' }, email: { type: 'string', label: 'Email', - description: 'User’s email (ex: foo@bar.com)' + description: 'User’s email (ex: foo@bar.com)', + category: 'hashedPII' }, phone: { type: 'string', label: 'Phone', description: - 'User’s phone number, including country code. Punctuation and spaces are ok (ex: 1-234-567-8910 or +44 844 412 4653)' + 'User’s phone number, including country code. Punctuation and spaces are ok (ex: 1-234-567-8910 or +44 844 412 4653)', + category: 'hashedPII' }, country: { type: 'string', label: 'Country', - description: 'User’s country. Use 2-letter country codes in ISO 3166-1 alpha-2 format.' + description: 'User’s country. Use 2-letter country codes in ISO 3166-1 alpha-2 format.', + category: 'hashedPII' }, birth: { type: 'object', @@ -194,21 +198,25 @@ const action: ActionDefinition = { type: 'string', label: 'Day' } - } + }, + category: 'hashedPII' }, name: { type: 'object', label: 'Name', description: 'User’s name. Include as many fields as possible for better match rates. Use a-z only. No punctuation. Special characters in UTF-8 format', + category: 'hashedPII', properties: { first: { type: 'string', - label: 'First Name' + label: 'First Name', + category: 'hashedPII' }, last: { type: 'string', - label: 'Last Name' + label: 'Last Name', + category: 'hashedPII' }, firstInitial: { type: 'string', @@ -219,40 +227,47 @@ const action: ActionDefinition = { city: { type: 'string', label: 'City', - description: 'User’s city. Use a-z only. No punctuation. No special characters.' + description: 'User’s city. Use a-z only. No punctuation. No special characters.', + category: 'hashedPII' }, state: { type: 'string', label: 'State', description: - 'User’s state. Use the 2-character ANSI abbreviation code, Normalize states outside the US with no punctuation and no special characters.' + 'User’s state. Use the 2-character ANSI abbreviation code, Normalize states outside the US with no punctuation and no special characters.', + category: 'hashedPII' }, zip: { type: 'string', label: 'Postal Code', description: - 'User’s postal code. For the US, use only the first 5 digits. For the UK, use the Area/District/Sector format.' + 'User’s postal code. For the US, use only the first 5 digits. For the UK, use the Area/District/Sector format.', + category: 'hashedPII' }, gender: { type: 'string', label: 'Gender', - description: 'User’s gender (m for male, f for female)' + description: 'User’s gender (m for male, f for female)', + category: 'hashedPII' }, mobileAdId: { type: 'string', label: 'Mobile Advertising ID', description: - 'User’s Apple IDFA, Android Ad ID, or Facebook app scoped ID. Keep hyphens (ex: AB1234CD-E123-12FG-J123)' + 'User’s Apple IDFA, Android Ad ID, or Facebook app scoped ID. Keep hyphens (ex: AB1234CD-E123-12FG-J123)', + category: 'hashedPII' }, appId: { type: 'string', label: 'App ID', - description: 'The app ID of the user.' + description: 'The app ID of the user.', + category: 'hashedPII' }, pageId: { type: 'string', label: 'Page ID', - description: 'The page ID of the user.' + description: 'The page ID of the user.', + category: 'hashedPII' }, enable_batching, batch_size diff --git a/packages/destination-actions/src/destinations/first-party-dv360/properties.ts b/packages/destination-actions/src/destinations/first-party-dv360/properties.ts index cc0bcb4a5f..1ee66f9cb0 100644 --- a/packages/destination-actions/src/destinations/first-party-dv360/properties.ts +++ b/packages/destination-actions/src/destinations/first-party-dv360/properties.ts @@ -26,7 +26,8 @@ export const emails: InputField = { type: 'string', default: { '@path': '$.context.traits.emails' - } + }, + category: 'hashedPII' } export const phoneNumbers: InputField = { @@ -35,7 +36,8 @@ export const phoneNumbers: InputField = { type: 'string', default: { '@path': '$.context.traits.phoneNumbers' - } + }, + category: 'hashedPII' } export const zipCodes: InputField = { @@ -53,7 +55,8 @@ export const firstName: InputField = { type: 'string', default: { '@path': '$.context.traits.firstName' - } + }, + category: 'hashedPII' } export const lastName: InputField = { @@ -62,7 +65,8 @@ export const lastName: InputField = { type: 'string', default: { '@path': '$.context.traits.lastName' - } + }, + category: 'hashedPII' } export const countryCode: InputField = { diff --git a/packages/destination-actions/src/destinations/google-campaign-manager-360/common-fields.ts b/packages/destination-actions/src/destinations/google-campaign-manager-360/common-fields.ts index 253bd1ab20..025b625be8 100644 --- a/packages/destination-actions/src/destinations/google-campaign-manager-360/common-fields.ts +++ b/packages/destination-actions/src/destinations/google-campaign-manager-360/common-fields.ts @@ -60,32 +60,37 @@ export const commonFields: ActionDefinition['fields'] = { label: 'Email', description: "The user's email address. If unhashed, Segment will hash before sending to Campaign Manager 360.", type: 'string', - required: false + required: false, + category: 'hashedPII' }, phone: { label: 'Phone', description: "The user's phone number. If unhashed, Segment will hash before sending to Campaign Manager 360.", type: 'string', - required: false + required: false, + category: 'hashedPII' }, firstName: { label: 'First Name', description: 'First name of the user. If unhashed, Segment will hash before sending to Campaign Manager 360.', type: 'string', - required: false + required: false, + category: 'hashedPII' }, lastName: { label: 'Last Name', description: 'Last name of the user. If unhashed, Segment will hash before sending to Campaign Manager 360.', type: 'string', - required: false + required: false, + category: 'hashedPII' }, streetAddress: { label: 'Street Address', description: 'The street address of the user. If unhashed, Segment will hash before sending to Campaign Manager 360.', type: 'string', - required: false + required: false, + category: 'hashedPII' }, city: { label: 'City', diff --git a/packages/destination-actions/src/destinations/google-campaign-manager-360/conversionUpload/index.ts b/packages/destination-actions/src/destinations/google-campaign-manager-360/conversionUpload/index.ts index 6196544ca7..526e87310f 100644 --- a/packages/destination-actions/src/destinations/google-campaign-manager-360/conversionUpload/index.ts +++ b/packages/destination-actions/src/destinations/google-campaign-manager-360/conversionUpload/index.ts @@ -99,7 +99,8 @@ const action: ActionDefinition = { description: 'A comma separated list of the alphanumeric encrypted user IDs. Any user ID with exposure prior to the conversion timestamp will be used in the inserted conversion. If no such user ID is found then the conversion will be rejected with INVALID_ARGUMENT error. When set, `encryptionInfo` should also be specified.', type: 'string', - required: false + required: false, + category: 'hashedPII' } }, default: { diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/index.ts index c2f6a2568d..4ed2bda788 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/index.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/index.ts @@ -75,7 +75,8 @@ const action: ActionDefinition = { then: { '@path': '$.properties.email' }, else: { '@path': '$.context.traits.email' } } - } + }, + category: 'hashedPII' }, phone_number: { label: 'Phone Number', @@ -88,7 +89,8 @@ const action: ActionDefinition = { then: { '@path': '$.properties.phone' }, else: { '@path': '$.context.traits.phone' } } - } + }, + category: 'hashedPII' }, order_id: { label: 'Order ID', diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/index.ts index e47a399b15..e35352eb4f 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/index.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/index.ts @@ -82,7 +82,8 @@ const action: ActionDefinition = { then: { '@path': '$.properties.email' }, else: { '@path': '$.context.traits.email' } } - } + }, + category: 'hashedPII' }, phone_number: { label: 'Phone Number', @@ -95,7 +96,8 @@ const action: ActionDefinition = { then: { '@path': '$.properties.phone' }, else: { '@path': '$.context.traits.phone' } } - } + }, + category: 'hashedPII' }, order_id: { label: 'Order ID', diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/index.ts index 49f76829b2..84258edf5a 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/index.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/index.ts @@ -113,7 +113,8 @@ const action: ActionDefinition = { then: { '@path': '$.properties.email' }, else: { '@path': '$.context.traits.email' } } - } + }, + category: 'hashedPII' }, phone_number: { label: 'Phone Number', @@ -126,7 +127,8 @@ const action: ActionDefinition = { then: { '@path': '$.properties.phone' }, else: { '@path': '$.context.traits.phone' } } - } + }, + category: 'hashedPII' }, first_name: { label: 'First Name', diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/index.ts index e1376cd569..e141029b50 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/index.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/index.ts @@ -125,7 +125,8 @@ const action: ActionDefinition = { then: { '@path': '$.properties.email' }, else: { '@path': '$.context.traits.email' } } - } + }, + category: 'hashedPII' }, phone_number: { label: 'Phone Number', @@ -138,7 +139,8 @@ const action: ActionDefinition = { then: { '@path': '$.properties.phone' }, else: { '@path': '$.context.traits.phone' } } - } + }, + category: 'hashedPII' }, first_name: { label: 'First Name', diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/userList/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/userList/index.ts index 51e6ced8a1..f3b6112352 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/userList/index.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/userList/index.ts @@ -30,7 +30,8 @@ const action: ActionDefinition = { then: { '@path': '$.context.traits.firstName' }, else: { '@path': '$.properties.firstName' } } - } + }, + category: 'hashedPII' }, last_name: { label: 'Last Name', @@ -42,7 +43,8 @@ const action: ActionDefinition = { then: { '@path': '$.context.traits.lastName' }, else: { '@path': '$.properties.lastName' } } - } + }, + category: 'hashedPII' }, email: { label: 'Email', @@ -54,7 +56,8 @@ const action: ActionDefinition = { then: { '@path': '$.context.traits.email' }, else: { '@path': '$.properties.email' } } - } + }, + category: 'hashedPII' }, phone: { label: 'Phone', @@ -67,7 +70,8 @@ const action: ActionDefinition = { then: { '@path': '$.context.traits.phone' }, else: { '@path': '$.properties.phone' } } - } + }, + category: 'hashedPII' }, phone_country_code: { label: 'Phone Number Country Code', 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 830108d557..85cca6b514 100644 --- a/packages/destination-actions/src/destinations/linkedin-audiences/updateAudience/index.ts +++ b/packages/destination-actions/src/destinations/linkedin-audiences/updateAudience/index.ts @@ -36,7 +36,8 @@ const action: ActionDefinition = { then: { '@path': '$.context.traits.email' }, else: { '@path': '$.traits.email' } } - } + }, + category: 'hashedPII' }, first_name: { label: 'User First Name', diff --git a/packages/destination-actions/src/destinations/linkedin-conversions/streamConversion/index.ts b/packages/destination-actions/src/destinations/linkedin-conversions/streamConversion/index.ts index 4d67bd8916..6a562ea3da 100644 --- a/packages/destination-actions/src/destinations/linkedin-conversions/streamConversion/index.ts +++ b/packages/destination-actions/src/destinations/linkedin-conversions/streamConversion/index.ts @@ -223,7 +223,8 @@ const action: ActionDefinition = { value: 'PHONE_SHA256' } ] - } + }, + category: 'hashedPII' }, email: { label: 'Email', @@ -83,7 +84,8 @@ const action: ActionDefinition = { value: 'EMAIL_SHA256' } ] - } + }, + category: 'hashedPII' }, advertising_id: { label: 'Mobile Advertising ID', @@ -103,7 +105,8 @@ const action: ActionDefinition = { value: 'MOBILE_AD_ID_SHA256' } ] - } + }, + category: 'hashedPII' }, enable_batching: { label: 'Enable Batching', diff --git a/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/snap-capi-input-fields-deprecated.ts b/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/snap-capi-input-fields-deprecated.ts index 1c4bf00f7b..1d3f57866f 100644 --- a/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/snap-capi-input-fields-deprecated.ts +++ b/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/snap-capi-input-fields-deprecated.ts @@ -40,7 +40,8 @@ const device_model: InputField = { const email: InputField = { label: '[Deprecated] Email', description: 'Deprecated. Use User Data email field.', - type: 'string' + type: 'string', + category: 'hashedPII' } const event_conversion_type: InputField = { @@ -123,7 +124,8 @@ const page_url: InputField = { const phone_number: InputField = { label: '[Deprecated] Phone Number', description: 'Deprecated. Use User Data phone field.', - type: 'string' + type: 'string', + category: 'hashedPII' } const price: InputField = { diff --git a/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/snap-capi-input-fields-v3.ts b/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/snap-capi-input-fields-v3.ts index 56a0bcc5ac..f71e98d3d0 100644 --- a/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/snap-capi-input-fields-v3.ts +++ b/packages/destination-actions/src/destinations/snap-conversions-api/reportConversionEvent/snap-capi-input-fields-v3.ts @@ -344,58 +344,69 @@ const user_data: InputField = { description: 'Any unique ID from the advertiser, such as loyalty membership IDs, user IDs, and external cookie IDs. You can send one or more external IDs for a given event.', type: 'string', - multiple: true // changed the type from string to array of strings. + multiple: true, // changed the type from string to array of strings. + category: 'hashedPII' }, email: { label: 'Email', description: 'An email address in lowercase.', - type: 'string' + type: 'string', + category: 'hashedPII' }, phone: { label: 'Phone', description: 'A phone number. Include only digits with country code, area code, and number. Remove symbols, letters, and any leading zeros. In addition, always include the country code, even if all of the data is from the same country, as the country code is used for matching.', - type: 'string' + type: 'string', + category: 'hashedPII' }, gender: { label: 'Gender', description: 'Gender in lowercase. Either f or m.', - type: 'string' + type: 'string', + category: 'hashedPII' }, dateOfBirth: { label: 'Date of Birth', description: 'A date of birth given as year, month, and day. Example: 19971226 for December 26, 1997.', - type: 'string' + type: 'string', + category: 'hashedPII' }, lastName: { label: 'Last Name', description: 'A last name in lowercase.', - type: 'string' + type: 'string', + category: 'hashedPII' }, firstName: { label: 'First Name', description: 'A first name in lowercase.', - type: 'string' + type: 'string', + category: 'hashedPII' }, city: { label: 'City', description: 'A city in lowercase without spaces or punctuation. Example: menlopark.', - type: 'string' + type: 'string', + category: 'hashedPII' }, state: { label: 'State', description: 'A two-letter state code in lowercase. Example: ca.', - type: 'string' + type: 'string', + category: 'hashedPII' }, zip: { label: 'Zip Code', description: 'A five-digit zip code for United States. For other locations, follow each country`s standards.', - type: 'string' + type: 'string', + category: 'hashedPII' }, country: { label: 'Country', description: 'A two-letter country code in lowercase.', - type: 'string' + type: 'string', + category: 'hashedPII' }, client_ip_address: { label: 'Client IP Address', diff --git a/packages/destination-actions/src/destinations/tiktok-audiences/properties.ts b/packages/destination-actions/src/destinations/tiktok-audiences/properties.ts index 680cd6b56a..b1ecf35483 100644 --- a/packages/destination-actions/src/destinations/tiktok-audiences/properties.ts +++ b/packages/destination-actions/src/destinations/tiktok-audiences/properties.ts @@ -35,7 +35,8 @@ export const email: InputField = { then: { '@path': '$.context.traits.email' }, else: { '@path': '$.properties.email' } } - } + }, + category: 'hashedPII' } export const send_email: InputField = { @@ -55,7 +56,8 @@ export const phone: InputField = { then: { '@path': '$.context.traits.phone' }, else: { '@path': '$.properties.phone' } } - } + }, + category: 'hashedPII' } export const send_phone: InputField = { @@ -71,7 +73,8 @@ export const advertising_id: InputField = { type: 'string', default: { '@path': '$.context.device.advertisingId' - } + }, + category: 'hashedPII' } export const send_advertising_id: InputField = { diff --git a/packages/destination-actions/src/destinations/tiktok-conversions/common_fields.ts b/packages/destination-actions/src/destinations/tiktok-conversions/common_fields.ts index 31ba0eb75b..c147678a12 100644 --- a/packages/destination-actions/src/destinations/tiktok-conversions/common_fields.ts +++ b/packages/destination-actions/src/destinations/tiktok-conversions/common_fields.ts @@ -36,7 +36,8 @@ export const commonFields: Record = { then: { '@path': '$.properties.phone' }, else: { '@path': '$.context.traits.phone' } } - } + }, + category: 'hashedPII' }, email: { label: 'Email', @@ -50,7 +51,8 @@ export const commonFields: Record = { then: { '@path': '$.properties.email' }, else: { '@path': '$.context.traits.email' } } - } + }, + category: 'hashedPII' }, first_name: { label: 'First Name', @@ -63,7 +65,8 @@ export const commonFields: Record = { then: { '@path': '$.properties.first_name' }, else: { '@path': '$.context.traits.first_name' } } - } + }, + category: 'hashedPII' }, last_name: { label: 'Last Name', @@ -76,7 +79,8 @@ export const commonFields: Record = { then: { '@path': '$.properties.last_name' }, else: { '@path': '$.context.traits.last_name' } } - } + }, + category: 'hashedPII' }, address: { label: 'Address', @@ -97,7 +101,8 @@ export const commonFields: Record = { zip_code: { label: 'Zip Code', type: 'string', - description: "The customer's Zip Code." + description: "The customer's Zip Code.", + category: 'hashedPII' }, state: { label: 'State', @@ -164,7 +169,8 @@ export const commonFields: Record = { then: { '@path': '$.userId' }, else: { '@path': '$.anonymousId' } } - } + }, + category: 'hashedPII' }, ttclid: { label: 'TikTok Click ID', diff --git a/packages/destination-actions/src/destinations/tiktok-offline-conversions/common_fields.ts b/packages/destination-actions/src/destinations/tiktok-offline-conversions/common_fields.ts index cdc9990a7e..2d1e7c7b51 100644 --- a/packages/destination-actions/src/destinations/tiktok-offline-conversions/common_fields.ts +++ b/packages/destination-actions/src/destinations/tiktok-offline-conversions/common_fields.ts @@ -36,7 +36,8 @@ export const commonFields: Record = { then: { '@path': '$.properties.phone' }, else: { '@path': '$.context.traits.phone' } } - } + }, + category: 'hashedPII' }, email_addresses: { label: 'Email', @@ -50,7 +51,8 @@ export const commonFields: Record = { then: { '@path': '$.properties.email' }, else: { '@path': '$.context.traits.email' } } - } + }, + category: 'hashedPII' }, order_id: { label: 'Order ID', @@ -80,7 +82,8 @@ export const commonFields: Record = { then: { '@path': '$.userId' }, else: { '@path': '$.anonymousId' } } - } + }, + category: 'hashedPII' }, ttclid: { label: 'TikTok Click ID', @@ -200,7 +203,10 @@ export const commonFields: Record = { description: 'Type of the product item. When the `content_id` in the `Contents` field is specified as a `sku_id`, set this field to `product`. When the `content_id` in the `Contents` field is specified as an `item_group_id`, set this field to `product_group`.', type: 'string', - choices: [ { label: 'product', value: 'product' }, { label: 'product_group', value: 'product_group' }], + choices: [ + { label: 'product', value: 'product' }, + { label: 'product_group', value: 'product_group' } + ], default: 'product' }, currency: { diff --git a/packages/destination-actions/src/destinations/yahoo-audiences/updateSegment/index.ts b/packages/destination-actions/src/destinations/yahoo-audiences/updateSegment/index.ts index 181f210f43..a50e09e708 100644 --- a/packages/destination-actions/src/destinations/yahoo-audiences/updateSegment/index.ts +++ b/packages/destination-actions/src/destinations/yahoo-audiences/updateSegment/index.ts @@ -71,7 +71,8 @@ const action: ActionDefinition = { then: { '@path': '$.traits.phone' }, // Phone is sent as identify's trait or track's property else: { '@path': '$.properties.phone' } } - } + }, + category: 'hashedPII' }, email: { label: 'User Email', @@ -85,7 +86,8 @@ const action: ActionDefinition = { then: { '@path': '$.traits.email' }, else: { '@path': '$.context.traits.email' } // Phone is sent as identify's trait or track's context.trait } - } + }, + category: 'hashedPII' }, advertising_id: { label: 'User Mobile Advertising ID', From e41b6ced8796328cedac00a10f85a80fec9f8ea3 Mon Sep 17 00:00:00 2001 From: Varadarajan V <109586712+varadarajan-tw@users.noreply.github.com> Date: Thu, 3 Apr 2025 12:29:31 +0530 Subject: [PATCH 27/78] [Smart Hashing] Remove redundant hashing descriptions (#2848) * [GEC] update description * [Amazon AMC] Update description * [Tiktok-offline-conversions] Update hashing description * update generated-types * Update packages/destination-actions/src/destinations/amazon-amc/syncAudiencesToDSP/index.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * [Amazon AMC] update types --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../syncAudiencesToDSP/generated-types.ts | 16 +++++++-------- .../amazon-amc/syncAudiencesToDSP/index.ts | 16 +++++++-------- .../postConversion/index.ts | 3 ++- .../uploadClickConversion/generated-types.ts | 4 ++-- .../uploadClickConversion/index.ts | 5 ++--- .../uploadClickConversion2/generated-types.ts | 4 ++-- .../uploadClickConversion2/index.ts | 5 ++--- .../generated-types.ts | 10 +++++----- .../uploadConversionAdjustment/index.ts | 17 +++++++--------- .../generated-types.ts | 10 +++++----- .../uploadConversionAdjustment2/index.ts | 20 +++++++++---------- .../userList/generated-types.ts | 8 ++++---- .../userList/index.ts | 9 ++++----- .../common_fields.ts | 6 +++--- .../reportOfflineEvent/generated-types.ts | 6 +++--- .../generated-types.ts | 6 +++--- .../generated-types.ts | 6 +++--- 17 files changed, 72 insertions(+), 79 deletions(-) diff --git a/packages/destination-actions/src/destinations/amazon-amc/syncAudiencesToDSP/generated-types.ts b/packages/destination-actions/src/destinations/amazon-amc/syncAudiencesToDSP/generated-types.ts index 772c90ade8..c6d78db097 100644 --- a/packages/destination-actions/src/destinations/amazon-amc/syncAudiencesToDSP/generated-types.ts +++ b/packages/destination-actions/src/destinations/amazon-amc/syncAudiencesToDSP/generated-types.ts @@ -10,35 +10,35 @@ export interface Payload { */ externalUserId: string /** - * User email address. Vaule will be hashed before sending to Amazon. + * User email address. */ email?: string /** - * User first name. Value will be hashed before sending to Amazon. + * User first name. */ firstName?: string /** - * User Last name. Value will be hashed before sending to Amazon. + * User Last name. */ lastName?: string /** - * Phone Number. Value will be hashed before sending to Amazon. + * Phone Number. */ phone?: string /** - * POstal Code. Value will be hashed before sending to Amazon. + * Postal Code. */ postal?: string /** - * State Code. Value will be hashed before sending to Amazon. + * State Code. */ state?: string /** - * City name. Value will be hashed before sending to Amazon. + * City name. */ city?: string /** - * Address Code. Value will be hashed before sending to Amazon. + * Address Code. */ address?: string /** diff --git a/packages/destination-actions/src/destinations/amazon-amc/syncAudiencesToDSP/index.ts b/packages/destination-actions/src/destinations/amazon-amc/syncAudiencesToDSP/index.ts index 226365c5ad..af0fd476d3 100644 --- a/packages/destination-actions/src/destinations/amazon-amc/syncAudiencesToDSP/index.ts +++ b/packages/destination-actions/src/destinations/amazon-amc/syncAudiencesToDSP/index.ts @@ -26,7 +26,7 @@ const action: ActionDefinition = { }, email: { label: 'Email', - description: 'User email address. Vaule will be hashed before sending to Amazon.', + description: 'User email address.', type: 'string', required: false, default: { @@ -40,7 +40,7 @@ const action: ActionDefinition = { }, firstName: { label: 'First name', - description: 'User first name. Value will be hashed before sending to Amazon.', + description: 'User first name.', type: 'string', required: false, default: { '@path': '$.properties.first_name' }, @@ -48,7 +48,7 @@ const action: ActionDefinition = { }, lastName: { label: 'Last name', - description: 'User Last name. Value will be hashed before sending to Amazon.', + description: 'User Last name.', type: 'string', required: false, default: { '@path': '$.properties.last_name' }, @@ -56,7 +56,7 @@ const action: ActionDefinition = { }, phone: { label: 'Phone', - description: 'Phone Number. Value will be hashed before sending to Amazon.', + description: 'Phone Number.', type: 'string', required: false, default: { '@path': '$.properties.phone' }, @@ -64,7 +64,7 @@ const action: ActionDefinition = { }, postal: { label: 'Postal', - description: 'POstal Code. Value will be hashed before sending to Amazon.', + description: 'Postal Code.', type: 'string', required: false, default: { '@path': '$.properties.postal' }, @@ -72,7 +72,7 @@ const action: ActionDefinition = { }, state: { label: 'State', - description: 'State Code. Value will be hashed before sending to Amazon.', + description: 'State Code.', type: 'string', required: false, default: { '@path': '$.properties.state' }, @@ -80,7 +80,7 @@ const action: ActionDefinition = { }, city: { label: 'City', - description: 'City name. Value will be hashed before sending to Amazon.', + description: 'City name.', type: 'string', required: false, default: { '@path': '$.properties.city' }, @@ -88,7 +88,7 @@ const action: ActionDefinition = { }, address: { label: 'Address', - description: 'Address Code. Value will be hashed before sending to Amazon.', + description: 'Address Code.', type: 'string', required: false, default: { '@path': '$.properties.address' }, diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/index.ts index cfa4bf0305..eb65d54789 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/index.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/index.ts @@ -158,7 +158,8 @@ const action: ActionDefinition = { then: { '@path': '$.properties.address.street' }, else: { '@path': '$.traits.address.street' } } - } + }, + category: 'hashedPII' }, city: { label: 'City', diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/generated-types.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/generated-types.ts index 2e3fe2c159..ec9bd8ee13 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/generated-types.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/generated-types.ts @@ -22,11 +22,11 @@ export interface Payload { */ conversion_timestamp: string /** - * Email address of the individual who triggered the conversion event. Segment will hash this value before sending to Google. + * Email address of the individual who triggered the conversion event */ email_address?: string /** - * Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000. Segment will hash this value before sending to Google. + * Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000 */ phone_number?: string /** diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/index.ts index 4ed2bda788..f517b19de0 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/index.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/index.ts @@ -66,8 +66,7 @@ const action: ActionDefinition = { }, email_address: { label: 'Email Address', - description: - 'Email address of the individual who triggered the conversion event. Segment will hash this value before sending to Google.', + description: 'Email address of the individual who triggered the conversion event', type: 'string', default: { '@if': { @@ -81,7 +80,7 @@ const action: ActionDefinition = { phone_number: { label: 'Phone Number', description: - 'Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000. Segment will hash this value before sending to Google.', + 'Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000', type: 'string', default: { '@if': { diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/generated-types.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/generated-types.ts index 2e3fe2c159..ec9bd8ee13 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/generated-types.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/generated-types.ts @@ -22,11 +22,11 @@ export interface Payload { */ conversion_timestamp: string /** - * Email address of the individual who triggered the conversion event. Segment will hash this value before sending to Google. + * Email address of the individual who triggered the conversion event */ email_address?: string /** - * Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000. Segment will hash this value before sending to Google. + * Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000 */ phone_number?: string /** diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/index.ts index e35352eb4f..f66b5c8110 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/index.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/index.ts @@ -73,8 +73,7 @@ const action: ActionDefinition = { }, email_address: { label: 'Email Address', - description: - 'Email address of the individual who triggered the conversion event. Segment will hash this value before sending to Google.', + description: 'Email address of the individual who triggered the conversion event', type: 'string', default: { '@if': { @@ -88,7 +87,7 @@ const action: ActionDefinition = { phone_number: { label: 'Phone Number', description: - 'Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000. Segment will hash this value before sending to Google.', + 'Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000', type: 'string', default: { '@if': { diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/generated-types.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/generated-types.ts index 496374b75b..a24c2683ae 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/generated-types.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/generated-types.ts @@ -34,19 +34,19 @@ export interface Payload { */ restatement_currency_code?: string /** - * Email address of the individual who triggered the conversion event. Segment will hash this value before sending to Google. + * Email address of the individual who triggered the conversion event. */ email_address?: string /** - * Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000. Segment will hash this value before sending to Google. + * Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000 */ phone_number?: string /** - * First name of the user who performed the conversion. Segment will hash this value before sending to Google. + * First name of the user who performed the conversion */ first_name?: string /** - * Last name of the user who performed the conversion. Segment will hash this value before sending to Google. + * Last name of the user who performed the conversion */ last_name?: string /** @@ -66,7 +66,7 @@ export interface Payload { */ postal_code?: string /** - * Street address of the user who performed the conversion. Segment will hash this value before sending to Google. + * Street address of the user who performed the conversion */ street_address?: string /** diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/index.ts index 84258edf5a..911a5c937d 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/index.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/index.ts @@ -104,8 +104,7 @@ const action: ActionDefinition = { }, email_address: { label: 'Email Address', - description: - 'Email address of the individual who triggered the conversion event. Segment will hash this value before sending to Google.', + description: 'Email address of the individual who triggered the conversion event.', type: 'string', default: { '@if': { @@ -119,7 +118,7 @@ const action: ActionDefinition = { phone_number: { label: 'Phone Number', description: - 'Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000. Segment will hash this value before sending to Google.', + 'Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000', type: 'string', default: { '@if': { @@ -132,8 +131,7 @@ const action: ActionDefinition = { }, first_name: { label: 'First Name', - description: - 'First name of the user who performed the conversion. Segment will hash this value before sending to Google.', + description: 'First name of the user who performed the conversion', type: 'string', default: { '@if': { @@ -145,8 +143,7 @@ const action: ActionDefinition = { }, last_name: { label: 'Last Name', - description: - 'Last name of the user who performed the conversion. Segment will hash this value before sending to Google.', + description: 'Last name of the user who performed the conversion', type: 'string', default: { '@if': { @@ -154,7 +151,8 @@ const action: ActionDefinition = { then: { '@path': '$.properties.lastName' }, else: { '@path': '$.context.traits.lastName' } } - } + }, + category: 'hashedPII' }, city: { label: 'City', @@ -206,8 +204,7 @@ const action: ActionDefinition = { }, street_address: { label: 'Street Address', - description: - 'Street address of the user who performed the conversion. Segment will hash this value before sending to Google.', + description: 'Street address of the user who performed the conversion', type: 'string', default: { '@if': { diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/generated-types.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/generated-types.ts index 496374b75b..7079be7dd0 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/generated-types.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/generated-types.ts @@ -34,19 +34,19 @@ export interface Payload { */ restatement_currency_code?: string /** - * Email address of the individual who triggered the conversion event. Segment will hash this value before sending to Google. + * Email address of the individual who triggered the conversion event. */ email_address?: string /** - * Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000. Segment will hash this value before sending to Google. + * Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000. */ phone_number?: string /** - * First name of the user who performed the conversion. Segment will hash this value before sending to Google. + * First name of the user who performed the conversion. */ first_name?: string /** - * Last name of the user who performed the conversion. Segment will hash this value before sending to Google. + * Last name of the user who performed the conversion. */ last_name?: string /** @@ -66,7 +66,7 @@ export interface Payload { */ postal_code?: string /** - * Street address of the user who performed the conversion. Segment will hash this value before sending to Google. + * Street address of the user who performed the conversion. */ street_address?: string /** diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/index.ts index e141029b50..1902b86118 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/index.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/index.ts @@ -116,8 +116,7 @@ const action: ActionDefinition = { }, email_address: { label: 'Email Address', - description: - 'Email address of the individual who triggered the conversion event. Segment will hash this value before sending to Google.', + description: 'Email address of the individual who triggered the conversion event.', type: 'string', default: { '@if': { @@ -131,7 +130,7 @@ const action: ActionDefinition = { phone_number: { label: 'Phone Number', description: - 'Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000. Segment will hash this value before sending to Google.', + 'Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000.', type: 'string', default: { '@if': { @@ -144,8 +143,7 @@ const action: ActionDefinition = { }, first_name: { label: 'First Name', - description: - 'First name of the user who performed the conversion. Segment will hash this value before sending to Google.', + description: 'First name of the user who performed the conversion.', type: 'string', default: { '@if': { @@ -157,8 +155,7 @@ const action: ActionDefinition = { }, last_name: { label: 'Last Name', - description: - 'Last name of the user who performed the conversion. Segment will hash this value before sending to Google.', + description: 'Last name of the user who performed the conversion.', type: 'string', default: { '@if': { @@ -166,7 +163,8 @@ const action: ActionDefinition = { then: { '@path': '$.properties.lastName' }, else: { '@path': '$.context.traits.lastName' } } - } + }, + category: 'hashedPII' }, city: { label: 'City', @@ -218,8 +216,7 @@ const action: ActionDefinition = { }, street_address: { label: 'Street Address', - description: - 'Street address of the user who performed the conversion. Segment will hash this value before sending to Google.', + description: 'Street address of the user who performed the conversion.', type: 'string', default: { '@if': { @@ -227,7 +224,8 @@ const action: ActionDefinition = { then: { '@path': '$.properties.address.street,' }, else: { '@path': '$.context.traits.address.street' } } - } + }, + category: 'hashedPII' }, user_agent: { label: 'User Agent', diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/userList/generated-types.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/userList/generated-types.ts index a4f5719215..ab47bc04f9 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/userList/generated-types.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/userList/generated-types.ts @@ -2,19 +2,19 @@ export interface Payload { /** - * The user's first name. If not hashed, Segment will normalize and hash this value. + * The user's first name. */ first_name?: string /** - * The user's last name. If not hashed, Segment will normalize and hash this value. + * The user's last name. */ last_name?: string /** - * The user's email address. If not hashed, Segment will normalize and hash this value. + * The user's email address. */ email?: string /** - * The user's phone number. If not hashed, Segment will convert the phone number to the E.164 format and hash this value. + * The user's phone number. */ phone?: string /** diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/userList/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/userList/index.ts index f3b6112352..b08712e8b4 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/userList/index.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/userList/index.ts @@ -22,7 +22,7 @@ const action: ActionDefinition = { fields: { first_name: { label: 'First Name', - description: "The user's first name. If not hashed, Segment will normalize and hash this value.", + description: "The user's first name.", type: 'string', default: { '@if': { @@ -35,7 +35,7 @@ const action: ActionDefinition = { }, last_name: { label: 'Last Name', - description: "The user's last name. If not hashed, Segment will normalize and hash this value.", + description: "The user's last name.", type: 'string', default: { '@if': { @@ -48,7 +48,7 @@ const action: ActionDefinition = { }, email: { label: 'Email', - description: "The user's email address. If not hashed, Segment will normalize and hash this value.", + description: "The user's email address.", type: 'string', default: { '@if': { @@ -61,8 +61,7 @@ const action: ActionDefinition = { }, phone: { label: 'Phone', - description: - "The user's phone number. If not hashed, Segment will convert the phone number to the E.164 format and hash this value.", + description: "The user's phone number. ", type: 'string', default: { '@if': { diff --git a/packages/destination-actions/src/destinations/tiktok-offline-conversions/common_fields.ts b/packages/destination-actions/src/destinations/tiktok-offline-conversions/common_fields.ts index 2d1e7c7b51..a9bdd3bc58 100644 --- a/packages/destination-actions/src/destinations/tiktok-offline-conversions/common_fields.ts +++ b/packages/destination-actions/src/destinations/tiktok-offline-conversions/common_fields.ts @@ -27,7 +27,7 @@ export const commonFields: Record = { phone_numbers: { label: 'Phone Number', description: - 'A single phone number or array of phone numbers in E.164 standard format. Segment will hash this value before sending to TikTok. At least one phone number value is required if both Email and External ID fields are empty.', + 'A single phone number or array of phone numbers in E.164 standard format. At least one phone number value is required if both Email and External ID fields are empty.', type: 'string', multiple: true, default: { @@ -42,7 +42,7 @@ export const commonFields: Record = { email_addresses: { label: 'Email', description: - 'A single email address or an array of email addresses. Segment will hash this value before sending to TikTok. At least one email value is required if both Phone Number and External ID fields are empty.', + 'A single email address or an array of email addresses. At least one email value is required if both Phone Number and External ID fields are empty.', type: 'string', multiple: true, default: { @@ -73,7 +73,7 @@ export const commonFields: Record = { external_ids: { label: 'External ID', description: - 'Uniquely identifies the user who triggered the conversion event. Segment will hash this value before sending to TikTok. TikTok Offline Conversions Destination supports both string and string[] types for sending external ID(s). At least one external ID value is required if both Email and Phone Number fields are empty.', + 'Uniquely identifies the user who triggered the conversion event. TikTok Offline Conversions Destination supports both string and string[] types for sending external ID(s). At least one external ID value is required if both Email and Phone Number fields are empty.', type: 'string', multiple: true, default: { diff --git a/packages/destination-actions/src/destinations/tiktok-offline-conversions/reportOfflineEvent/generated-types.ts b/packages/destination-actions/src/destinations/tiktok-offline-conversions/reportOfflineEvent/generated-types.ts index ac9476c6ac..74502a1fe0 100644 --- a/packages/destination-actions/src/destinations/tiktok-offline-conversions/reportOfflineEvent/generated-types.ts +++ b/packages/destination-actions/src/destinations/tiktok-offline-conversions/reportOfflineEvent/generated-types.ts @@ -14,11 +14,11 @@ export interface Payload { */ timestamp?: string /** - * A single phone number or array of phone numbers in E.164 standard format. Segment will hash this value before sending to TikTok. At least one phone number value is required if both Email and External ID fields are empty. + * A single phone number or array of phone numbers in E.164 standard format. At least one phone number value is required if both Email and External ID fields are empty. */ phone_numbers?: string[] /** - * A single email address or an array of email addresses. Segment will hash this value before sending to TikTok. At least one email value is required if both Phone Number and External ID fields are empty. + * A single email address or an array of email addresses. At least one email value is required if both Phone Number and External ID fields are empty. */ email_addresses?: string[] /** @@ -30,7 +30,7 @@ export interface Payload { */ shop_id?: string /** - * Uniquely identifies the user who triggered the conversion event. Segment will hash this value before sending to TikTok. TikTok Offline Conversions Destination supports both string and string[] types for sending external ID(s). At least one external ID value is required if both Email and Phone Number fields are empty. + * Uniquely identifies the user who triggered the conversion event. TikTok Offline Conversions Destination supports both string and string[] types for sending external ID(s). At least one external ID value is required if both Email and Phone Number fields are empty. */ external_ids?: string[] /** diff --git a/packages/destination-actions/src/destinations/tiktok-offline-conversions/trackNonPaymentOfflineConversion/generated-types.ts b/packages/destination-actions/src/destinations/tiktok-offline-conversions/trackNonPaymentOfflineConversion/generated-types.ts index ac9476c6ac..74502a1fe0 100644 --- a/packages/destination-actions/src/destinations/tiktok-offline-conversions/trackNonPaymentOfflineConversion/generated-types.ts +++ b/packages/destination-actions/src/destinations/tiktok-offline-conversions/trackNonPaymentOfflineConversion/generated-types.ts @@ -14,11 +14,11 @@ export interface Payload { */ timestamp?: string /** - * A single phone number or array of phone numbers in E.164 standard format. Segment will hash this value before sending to TikTok. At least one phone number value is required if both Email and External ID fields are empty. + * A single phone number or array of phone numbers in E.164 standard format. At least one phone number value is required if both Email and External ID fields are empty. */ phone_numbers?: string[] /** - * A single email address or an array of email addresses. Segment will hash this value before sending to TikTok. At least one email value is required if both Phone Number and External ID fields are empty. + * A single email address or an array of email addresses. At least one email value is required if both Phone Number and External ID fields are empty. */ email_addresses?: string[] /** @@ -30,7 +30,7 @@ export interface Payload { */ shop_id?: string /** - * Uniquely identifies the user who triggered the conversion event. Segment will hash this value before sending to TikTok. TikTok Offline Conversions Destination supports both string and string[] types for sending external ID(s). At least one external ID value is required if both Email and Phone Number fields are empty. + * Uniquely identifies the user who triggered the conversion event. TikTok Offline Conversions Destination supports both string and string[] types for sending external ID(s). At least one external ID value is required if both Email and Phone Number fields are empty. */ external_ids?: string[] /** diff --git a/packages/destination-actions/src/destinations/tiktok-offline-conversions/trackPaymentOfflineConversion/generated-types.ts b/packages/destination-actions/src/destinations/tiktok-offline-conversions/trackPaymentOfflineConversion/generated-types.ts index ac9476c6ac..74502a1fe0 100644 --- a/packages/destination-actions/src/destinations/tiktok-offline-conversions/trackPaymentOfflineConversion/generated-types.ts +++ b/packages/destination-actions/src/destinations/tiktok-offline-conversions/trackPaymentOfflineConversion/generated-types.ts @@ -14,11 +14,11 @@ export interface Payload { */ timestamp?: string /** - * A single phone number or array of phone numbers in E.164 standard format. Segment will hash this value before sending to TikTok. At least one phone number value is required if both Email and External ID fields are empty. + * A single phone number or array of phone numbers in E.164 standard format. At least one phone number value is required if both Email and External ID fields are empty. */ phone_numbers?: string[] /** - * A single email address or an array of email addresses. Segment will hash this value before sending to TikTok. At least one email value is required if both Phone Number and External ID fields are empty. + * A single email address or an array of email addresses. At least one email value is required if both Phone Number and External ID fields are empty. */ email_addresses?: string[] /** @@ -30,7 +30,7 @@ export interface Payload { */ shop_id?: string /** - * Uniquely identifies the user who triggered the conversion event. Segment will hash this value before sending to TikTok. TikTok Offline Conversions Destination supports both string and string[] types for sending external ID(s). At least one external ID value is required if both Email and Phone Number fields are empty. + * Uniquely identifies the user who triggered the conversion event. TikTok Offline Conversions Destination supports both string and string[] types for sending external ID(s). At least one external ID value is required if both Email and Phone Number fields are empty. */ external_ids?: string[] /** From edf06e9dc0e0ebff41618ee77343dc09233bb48c Mon Sep 17 00:00:00 2001 From: Drew Thompson <37553273+drew-thompson@users.noreply.github.com> Date: Thu, 3 Apr 2025 14:43:12 -0700 Subject: [PATCH 28/78] Correct Presets for Linked Audience Activations --- .../destination-actions/src/destinations/klaviyo/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/destination-actions/src/destinations/klaviyo/index.ts b/packages/destination-actions/src/destinations/klaviyo/index.ts index b5af281c51..a749e00fe0 100644 --- a/packages/destination-actions/src/destinations/klaviyo/index.ts +++ b/packages/destination-actions/src/destinations/klaviyo/index.ts @@ -157,15 +157,15 @@ const destination: AudienceDestinationDefinition = { presets: [ { name: 'Entities Audience Entered', - partnerAction: 'upsertProfile', - mapping: defaultValues(upsertProfile.fields), + partnerAction: 'addProfileToList', + mapping: defaultValues(addProfileToList.fields), type: 'specificEvent', eventSlug: 'warehouse_audience_entered_track' }, { name: 'Entities Audience Exited', - partnerAction: 'removeProfile', - mapping: defaultValues(removeProfile.fields), + partnerAction: 'removeProfileFromList', + mapping: defaultValues(removeProfileFromList.fields), type: 'specificEvent', eventSlug: 'warehouse_audience_exited_track' } From eaef83c7076bf95ca57c0e9049e3ea87928d153c Mon Sep 17 00:00:00 2001 From: Varadarajan V <109586712+varadarajan-tw@users.noreply.github.com> Date: Mon, 7 Apr 2025 19:47:05 +0530 Subject: [PATCH 29/78] Handles non-http error in SFMC multistatus handler (#2854) * Handles non-http error in SFMC multistatus handler * Update tests * Update test case --- .../_tests_/multistatus.test.ts | 50 +++++++++++++++++++ .../dataExtension/index.ts | 4 +- .../sfmc-operations.ts | 24 +++++++-- 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/_tests_/multistatus.test.ts b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/_tests_/multistatus.test.ts index 05e7146dfe..48450e0d80 100644 --- a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/_tests_/multistatus.test.ts +++ b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/_tests_/multistatus.test.ts @@ -550,4 +550,54 @@ describe('Multistatus', () => { }) }) }) + + it('should handle non-http error responses gracefully', async () => { + const errorResponse = { + message: 'Network Error', + code: 'ECONNREFUSED' + } + + nock(requestUrl).post('').replyWithError(errorResponse) + + const events: SegmentEvent[] = [ + createTestEvent({ + type: 'track', + userId: 'harry-1', + properties: { + id: '1234567890', + keys: { + contactKey: 'harry-1', + id: 'HS1' + }, + values: { + name: 'Harry Styles' + } + } + }) + ] + + const response = await testDestination.executeBatch('dataExtension', { + events, + settings, + mapping, + statsContext: { + statsClient: { + incr: jest.fn(), + histogram: jest.fn(), + set: jest.fn(), + _tags: jest.fn(), + observe: jest.fn(), + _name: jest.fn() + }, + tags: [] + } + }) + + expect(response[0]).toMatchObject({ + status: 500, + errortype: 'INTERNAL_SERVER_ERROR', + errormessage: + 'request to https://test123.rest.marketingcloudapis.com/hub/v1/dataevents/1234567890/rowset failed, reason: Network Error' + }) + }) }) diff --git a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/dataExtension/index.ts b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/dataExtension/index.ts index 6635ac64ba..49a17f7680 100644 --- a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/dataExtension/index.ts +++ b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/dataExtension/index.ts @@ -18,8 +18,8 @@ const action: ActionDefinition = { perform: async (request, { settings, payload }) => { return upsertRows(request, settings.subdomain, [payload]) }, - performBatch: async (request, { settings, payload }) => { - return executeUpsertWithMultiStatus(request, settings.subdomain, payload) + performBatch: async (request, { settings, payload, statsContext }) => { + return executeUpsertWithMultiStatus(request, settings.subdomain, payload, undefined, statsContext) } } diff --git a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-operations.ts b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-operations.ts index 3442632fbb..a111f86adc 100644 --- a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-operations.ts +++ b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-operations.ts @@ -7,7 +7,8 @@ import { ActionHookResponse, DynamicFieldResponse, DynamicFieldError, - DynamicFieldItem + DynamicFieldItem, + StatsContext } from '@segment/actions-core' import { Payload as payload_dataExtension } from './dataExtension/generated-types' import { Payload as payload_contactDataExtension } from './contactDataExtension/generated-types' @@ -79,7 +80,8 @@ export async function executeUpsertWithMultiStatus( request: RequestClient, subdomain: String, payloads: payload_dataExtension[] | payload_contactDataExtension[], - dataExtensionId?: string + dataExtensionId?: string, + statsContext?: StatsContext ): Promise { const multiStatusResponse = new MultiStatusResponse() let response: ModifiedResponse | undefined @@ -112,6 +114,22 @@ export async function executeUpsertWithMultiStatus( }) return multiStatusResponse } + + // If the errors is not a http resposne error, we treat it as a generic error + if (!(error as ErrorResponse).response?.data) { + if (statsContext) { + const { tags, statsClient } = statsContext + statsClient?.incr('sfmc_upsert_rows_error', 1, [...tags]) + } + payloads.forEach((_, index) => { + multiStatusResponse.setErrorResponseAtIndex(index, { + status: 500, + errormessage: error.message + }) + }) + return multiStatusResponse + } + const err = error as ErrorResponse if (err?.response?.status === 401) { throw error @@ -125,7 +143,7 @@ export async function executeUpsertWithMultiStatus( payloads.forEach((_, index) => { multiStatusResponse.setErrorResponseAtIndex(index, { - status: err?.response?.status || 400, + status: err?.response?.status || 500, errormessage: additionalError ? additionalError[0].message : errData?.message || '', sent: rows[index] as Object as JSONLikeObject, /* From b659a698a5045ba292d43fd6abd19b5211cf53ef Mon Sep 17 00:00:00 2001 From: Sayan Das <109198085+sayan-das-in@users.noreply.github.com> Date: Mon, 7 Apr 2025 19:47:15 +0530 Subject: [PATCH 30/78] Amazon AMC - Increased http client timeout to 15 seconds (#2852) --- .../src/destinations/amazon-amc/function.ts | 6 ++++-- .../src/destinations/amazon-amc/index.ts | 9 ++++++--- .../src/destinations/amazon-amc/utils.ts | 3 ++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/destination-actions/src/destinations/amazon-amc/function.ts b/packages/destination-actions/src/destinations/amazon-amc/function.ts index f4863f7a7a..d525147dcc 100644 --- a/packages/destination-actions/src/destinations/amazon-amc/function.ts +++ b/packages/destination-actions/src/destinations/amazon-amc/function.ts @@ -22,7 +22,8 @@ export async function processPayload( body: payloadString, headers: { 'Content-Type': 'application/vnd.amcaudiences.v1+json' - } + }, + timeout: 15000 }) const result = response.data @@ -155,7 +156,8 @@ export async function processBatchPayload( throwHttpErrors: false, headers: { 'Content-Type': 'application/vnd.amcaudiences.v1+json' - } + }, + timeout: 15000 }) if (!response.ok && response.status == 401) { throw new InvalidAuthenticationError(response.statusText) diff --git a/packages/destination-actions/src/destinations/amazon-amc/index.ts b/packages/destination-actions/src/destinations/amazon-amc/index.ts index a5385bdee0..d1386bc77e 100644 --- a/packages/destination-actions/src/destinations/amazon-amc/index.ts +++ b/packages/destination-actions/src/destinations/amazon-amc/index.ts @@ -48,7 +48,8 @@ const destination: AudienceDestinationDefinition = { method: 'GET', headers: { 'Content-Type': 'application/json' - } + }, + timeout: 15000 }) } catch (e: any) { const error = e as AmazonTestAuthenticationError @@ -208,7 +209,8 @@ const destination: AudienceDestinationDefinition = { body: payloadString, headers: { 'Content-Type': 'application/vnd.amcaudiences.v1+json' - } + }, + timeout: 15000 }) const res = await response.text() @@ -228,7 +230,8 @@ const destination: AudienceDestinationDefinition = { throw new IntegrationError('Missing audienceId value', 'MISSING_REQUIRED_FIELD', 400) } const response = await request(`${endpoint}/amc/audiences/metadata/${audience_id}`, { - method: 'GET' + method: 'GET', + timeout: 15000 }) const res = await response.text() // Regular expression to find a audienceId number and replace the audienceId with quoted string diff --git a/packages/destination-actions/src/destinations/amazon-amc/utils.ts b/packages/destination-actions/src/destinations/amazon-amc/utils.ts index 036e2d8a6e..052454200a 100644 --- a/packages/destination-actions/src/destinations/amazon-amc/utils.ts +++ b/packages/destination-actions/src/destinations/amazon-amc/utils.ts @@ -74,7 +74,8 @@ export const getAuthToken = async (request: RequestClient, settings: Settings, a headers: { // Amazon ads refresh token API throws error with authorization header so explicity overriding Authorization header here. authorization: '' - } + }, + timeout: 15000 }) return data.access_token } catch (e: any) { From 61c3d68a888112eaa7a41d6d8a8db653d0e50f77 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 19:59:49 +0530 Subject: [PATCH 31/78] Publish (#2855) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- packages/actions-shared/package.json | 4 +- .../browser-destination-runtime/package.json | 4 +- .../destinations/1flow/package.json | 4 +- .../destinations/adobe-target/package.json | 4 +- .../destinations/algolia-plugins/package.json | 4 +- .../amplitude-plugins/package.json | 4 +- .../braze-cloud-plugins/package.json | 6 +- .../destinations/braze/package.json | 6 +- .../destinations/bucket/package.json | 6 +- .../destinations/cdpresolution/package.json | 4 +- .../destinations/commandbar/package.json | 6 +- .../contentstack-web/package.json | 4 +- .../destinations/devrev/package.json | 4 +- .../destinations/evolv-ai/package.json | 4 +- .../destinations/friendbuy/package.json | 8 +- .../destinations/fullstory/package.json | 6 +- .../google-analytics-4-web/package.json | 6 +- .../google-campaign-manager/package.json | 4 +- .../destinations/heap/package.json | 6 +- .../destinations/hubble-web/package.json | 6 +- .../destinations/hubspot-web/package.json | 6 +- .../destinations/intercom/package.json | 8 +- .../destinations/iterate/package.json | 6 +- .../destinations/jimo/package.json | 4 +- .../destinations/koala/package.json | 6 +- .../destinations/logrocket/package.json | 6 +- .../nextdoor-plugins/package.json | 4 +- .../pendo-web-actions/package.json | 4 +- .../destinations/playerzero-web/package.json | 6 +- .../destinations/reddit-plugins/package.json | 4 +- .../destinations/replaybird/package.json | 4 +- .../destinations/ripe/package.json | 6 +- .../destinations/rupt/package.json | 6 +- .../destinations/screeb/package.json | 6 +- .../segment-utilities-web/package.json | 4 +- .../destinations/snap-plugins/package.json | 4 +- .../destinations/sprig-web/package.json | 6 +- .../destinations/stackadapt/package.json | 6 +- .../destinations/survicate/package.json | 4 +- .../destinations/tiktok-pixel/package.json | 6 +- .../destinations/upollo/package.json | 6 +- .../destinations/userpilot/package.json | 6 +- .../destinations/vwo/package.json | 6 +- .../destinations/wisepops/package.json | 6 +- packages/core/package.json | 2 +- packages/destination-actions/package.json | 6 +- packages/destinations-manifest/package.json | 80 +++++++++---------- 47 files changed, 159 insertions(+), 159 deletions(-) diff --git a/packages/actions-shared/package.json b/packages/actions-shared/package.json index ca3f1e7d26..f3ddeb0705 100644 --- a/packages/actions-shared/package.json +++ b/packages/actions-shared/package.json @@ -1,7 +1,7 @@ { "name": "@segment/actions-shared", "description": "Shared destination action methods and definitions.", - "version": "1.131.0", + "version": "1.132.0", "repository": { "type": "git", "url": "https://github.com/segmentio/action-destinations", @@ -37,7 +37,7 @@ }, "dependencies": { "@amplitude/ua-parser-js": "^0.7.25", - "@segment/actions-core": "^3.150.0", + "@segment/actions-core": "^3.151.0", "cheerio": "^1.0.0-rc.10", "dayjs": "^1.10.7", "escape-goat": "^3", diff --git a/packages/browser-destination-runtime/package.json b/packages/browser-destination-runtime/package.json index 75f6b0759e..5f3ac8c877 100644 --- a/packages/browser-destination-runtime/package.json +++ b/packages/browser-destination-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@segment/browser-destination-runtime", - "version": "1.79.0", + "version": "1.80.0", "license": "MIT", "publishConfig": { "access": "public", @@ -62,7 +62,7 @@ } }, "dependencies": { - "@segment/actions-core": "^3.150.0" + "@segment/actions-core": "^3.151.0" }, "devDependencies": { "@segment/analytics-next": "*" diff --git a/packages/browser-destinations/destinations/1flow/package.json b/packages/browser-destinations/destinations/1flow/package.json index 8f628ee8cc..f133a554ca 100644 --- a/packages/browser-destinations/destinations/1flow/package.json +++ b/packages/browser-destinations/destinations/1flow/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-1flow", - "version": "1.62.0", + "version": "1.63.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/adobe-target/package.json b/packages/browser-destinations/destinations/adobe-target/package.json index 61f984e248..d066889ae0 100644 --- a/packages/browser-destinations/destinations/adobe-target/package.json +++ b/packages/browser-destinations/destinations/adobe-target/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-adobe-target", - "version": "1.81.0", + "version": "1.82.0", "license": "MIT", "publishConfig": { "access": "public", @@ -16,7 +16,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/algolia-plugins/package.json b/packages/browser-destinations/destinations/algolia-plugins/package.json index 87f5b103be..23c1f81e64 100644 --- a/packages/browser-destinations/destinations/algolia-plugins/package.json +++ b/packages/browser-destinations/destinations/algolia-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-algolia-plugins", - "version": "1.57.0", + "version": "1.58.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/amplitude-plugins/package.json b/packages/browser-destinations/destinations/amplitude-plugins/package.json index 13249f0f26..4c5dd16299 100644 --- a/packages/browser-destinations/destinations/amplitude-plugins/package.json +++ b/packages/browser-destinations/destinations/amplitude-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-amplitude-plugins", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/braze-cloud-plugins/package.json b/packages/browser-destinations/destinations/braze-cloud-plugins/package.json index f4ab49a3a9..7a6c7709be 100644 --- a/packages/browser-destinations/destinations/braze-cloud-plugins/package.json +++ b/packages/browser-destinations/destinations/braze-cloud-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-braze-cloud-plugins", - "version": "1.88.0", + "version": "1.89.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/analytics-browser-actions-braze": "^1.88.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/analytics-browser-actions-braze": "^1.89.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/braze/package.json b/packages/browser-destinations/destinations/braze/package.json index 22d77bfe5f..66496701c8 100644 --- a/packages/browser-destinations/destinations/braze/package.json +++ b/packages/browser-destinations/destinations/braze/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-braze", - "version": "1.88.0", + "version": "1.89.0", "license": "MIT", "publishConfig": { "access": "public", @@ -35,8 +35,8 @@ "dependencies": { "@braze/web-sdk": "npm:@braze/web-sdk@^5", "@braze/web-sdk-v3": "npm:@braze/web-sdk@^3.5.1", - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/bucket/package.json b/packages/browser-destinations/destinations/bucket/package.json index 682bec2bc0..8f1e91192f 100644 --- a/packages/browser-destinations/destinations/bucket/package.json +++ b/packages/browser-destinations/destinations/bucket/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-bucket", - "version": "1.61.0", + "version": "1.62.0", "license": "MIT", "publishConfig": { "access": "public", @@ -16,8 +16,8 @@ "typings": "./dist/esm", "dependencies": { "@bucketco/tracking-sdk": "^2.0.0", - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/cdpresolution/package.json b/packages/browser-destinations/destinations/cdpresolution/package.json index 39094610a8..e37d4da450 100644 --- a/packages/browser-destinations/destinations/cdpresolution/package.json +++ b/packages/browser-destinations/destinations/cdpresolution/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-cdpresolution", - "version": "1.67.0", + "version": "1.68.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/commandbar/package.json b/packages/browser-destinations/destinations/commandbar/package.json index 1181226ec1..02be121e53 100644 --- a/packages/browser-destinations/destinations/commandbar/package.json +++ b/packages/browser-destinations/destinations/commandbar/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-commandbar", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/contentstack-web/package.json b/packages/browser-destinations/destinations/contentstack-web/package.json index c38e01b8a6..030b6a233b 100644 --- a/packages/browser-destinations/destinations/contentstack-web/package.json +++ b/packages/browser-destinations/destinations/contentstack-web/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-contentstack-web", - "version": "1.23.0", + "version": "1.24.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/devrev/package.json b/packages/browser-destinations/destinations/devrev/package.json index 878c4e3e98..72c42a4c36 100644 --- a/packages/browser-destinations/destinations/devrev/package.json +++ b/packages/browser-destinations/destinations/devrev/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-devrev", - "version": "1.67.0", + "version": "1.68.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/evolv-ai/package.json b/packages/browser-destinations/destinations/evolv-ai/package.json index cd07cb4b9b..1ad4c4369d 100644 --- a/packages/browser-destinations/destinations/evolv-ai/package.json +++ b/packages/browser-destinations/destinations/evolv-ai/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-evolv-ai", - "version": "1.23.0", + "version": "1.24.0", "license": "MIT", "repository": { "type": "git", @@ -16,7 +16,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/friendbuy/package.json b/packages/browser-destinations/destinations/friendbuy/package.json index 2eeea3a42a..ac6ca015cf 100644 --- a/packages/browser-destinations/destinations/friendbuy/package.json +++ b/packages/browser-destinations/destinations/friendbuy/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-friendbuy", - "version": "1.81.0", + "version": "1.82.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,9 +15,9 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/actions-shared": "^1.131.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/actions-shared": "^1.132.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/fullstory/package.json b/packages/browser-destinations/destinations/fullstory/package.json index e73525fd85..e47a4acf74 100644 --- a/packages/browser-destinations/destinations/fullstory/package.json +++ b/packages/browser-destinations/destinations/fullstory/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-fullstory", - "version": "1.82.0", + "version": "1.83.0", "license": "MIT", "publishConfig": { "access": "public", @@ -16,8 +16,8 @@ "typings": "./dist/esm", "dependencies": { "@fullstory/browser": "^2.0.3", - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/google-analytics-4-web/package.json b/packages/browser-destinations/destinations/google-analytics-4-web/package.json index 4b0a9e7f63..a035c83794 100644 --- a/packages/browser-destinations/destinations/google-analytics-4-web/package.json +++ b/packages/browser-destinations/destinations/google-analytics-4-web/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-google-analytics-4", - "version": "1.87.0", + "version": "1.88.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/google-campaign-manager/package.json b/packages/browser-destinations/destinations/google-campaign-manager/package.json index e6a945c881..3b40887c95 100644 --- a/packages/browser-destinations/destinations/google-campaign-manager/package.json +++ b/packages/browser-destinations/destinations/google-campaign-manager/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-google-campaign-manager", - "version": "1.71.0", + "version": "1.72.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/heap/package.json b/packages/browser-destinations/destinations/heap/package.json index ff35299f1f..add768a485 100644 --- a/packages/browser-destinations/destinations/heap/package.json +++ b/packages/browser-destinations/destinations/heap/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-heap", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/hubble-web/package.json b/packages/browser-destinations/destinations/hubble-web/package.json index 848dd51de3..9c6543f9f4 100644 --- a/packages/browser-destinations/destinations/hubble-web/package.json +++ b/packages/browser-destinations/destinations/hubble-web/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-hubble-web", - "version": "1.66.0", + "version": "1.67.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/hubspot-web/package.json b/packages/browser-destinations/destinations/hubspot-web/package.json index 92aa5a76f1..4ea7f60dff 100644 --- a/packages/browser-destinations/destinations/hubspot-web/package.json +++ b/packages/browser-destinations/destinations/hubspot-web/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-hubspot", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/intercom/package.json b/packages/browser-destinations/destinations/intercom/package.json index 194fd6e715..652970b01a 100644 --- a/packages/browser-destinations/destinations/intercom/package.json +++ b/packages/browser-destinations/destinations/intercom/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-intercom", - "version": "1.85.0", + "version": "1.86.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,9 +15,9 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/actions-shared": "^1.131.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/actions-shared": "^1.132.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/iterate/package.json b/packages/browser-destinations/destinations/iterate/package.json index 6a2ca4c1f3..2a446a65a5 100644 --- a/packages/browser-destinations/destinations/iterate/package.json +++ b/packages/browser-destinations/destinations/iterate/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-iterate", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/jimo/package.json b/packages/browser-destinations/destinations/jimo/package.json index f763663d60..a65882aad1 100644 --- a/packages/browser-destinations/destinations/jimo/package.json +++ b/packages/browser-destinations/destinations/jimo/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-jimo", - "version": "1.70.0", + "version": "1.71.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/koala/package.json b/packages/browser-destinations/destinations/koala/package.json index f2d7c5923b..98ebbd54b8 100644 --- a/packages/browser-destinations/destinations/koala/package.json +++ b/packages/browser-destinations/destinations/koala/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-koala", - "version": "1.82.0", + "version": "1.83.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/logrocket/package.json b/packages/browser-destinations/destinations/logrocket/package.json index f72c5c3821..1f899716ef 100644 --- a/packages/browser-destinations/destinations/logrocket/package.json +++ b/packages/browser-destinations/destinations/logrocket/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-logrocket", - "version": "1.81.0", + "version": "1.82.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0", + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0", "logrocket": "^9.0.0" }, "peerDependencies": { diff --git a/packages/browser-destinations/destinations/nextdoor-plugins/package.json b/packages/browser-destinations/destinations/nextdoor-plugins/package.json index 311a1804c8..7ca9cffa63 100644 --- a/packages/browser-destinations/destinations/nextdoor-plugins/package.json +++ b/packages/browser-destinations/destinations/nextdoor-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-nextdoor-plugins", - "version": "1.23.0", + "version": "1.24.0", "license": "MIT", "repository": { "type": "git", @@ -16,7 +16,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/pendo-web-actions/package.json b/packages/browser-destinations/destinations/pendo-web-actions/package.json index a031dbae0e..e7c4ff9883 100644 --- a/packages/browser-destinations/destinations/pendo-web-actions/package.json +++ b/packages/browser-destinations/destinations/pendo-web-actions/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-pendo-web-actions", - "version": "1.70.0", + "version": "1.71.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/playerzero-web/package.json b/packages/browser-destinations/destinations/playerzero-web/package.json index 319596eee5..9c48d1d528 100644 --- a/packages/browser-destinations/destinations/playerzero-web/package.json +++ b/packages/browser-destinations/destinations/playerzero-web/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-playerzero", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/reddit-plugins/package.json b/packages/browser-destinations/destinations/reddit-plugins/package.json index 04053ded05..0f30710053 100644 --- a/packages/browser-destinations/destinations/reddit-plugins/package.json +++ b/packages/browser-destinations/destinations/reddit-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-reddit-plugins", - "version": "1.23.0", + "version": "1.24.0", "license": "MIT", "repository": { "type": "git", @@ -16,7 +16,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/replaybird/package.json b/packages/browser-destinations/destinations/replaybird/package.json index 3ee79f1b45..681ca9e6c2 100644 --- a/packages/browser-destinations/destinations/replaybird/package.json +++ b/packages/browser-destinations/destinations/replaybird/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-replaybird", - "version": "1.61.0", + "version": "1.62.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/ripe/package.json b/packages/browser-destinations/destinations/ripe/package.json index e92d7b0fec..8f33333149 100644 --- a/packages/browser-destinations/destinations/ripe/package.json +++ b/packages/browser-destinations/destinations/ripe/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-ripe", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/rupt/package.json b/packages/browser-destinations/destinations/rupt/package.json index e626f556d9..78beadd35f 100644 --- a/packages/browser-destinations/destinations/rupt/package.json +++ b/packages/browser-destinations/destinations/rupt/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-rupt", - "version": "1.69.0", + "version": "1.70.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/screeb/package.json b/packages/browser-destinations/destinations/screeb/package.json index 2a83e602e5..33e7dcee2f 100644 --- a/packages/browser-destinations/destinations/screeb/package.json +++ b/packages/browser-destinations/destinations/screeb/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-screeb", - "version": "1.81.0", + "version": "1.82.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/segment-utilities-web/package.json b/packages/browser-destinations/destinations/segment-utilities-web/package.json index 514d9e9519..e8799303f2 100644 --- a/packages/browser-destinations/destinations/segment-utilities-web/package.json +++ b/packages/browser-destinations/destinations/segment-utilities-web/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-utils", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/snap-plugins/package.json b/packages/browser-destinations/destinations/snap-plugins/package.json index 21cf9bbebc..ebc9959b88 100644 --- a/packages/browser-destinations/destinations/snap-plugins/package.json +++ b/packages/browser-destinations/destinations/snap-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-snap-plugins", - "version": "1.61.0", + "version": "1.62.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/sprig-web/package.json b/packages/browser-destinations/destinations/sprig-web/package.json index 8c5110711e..1d1c4b8edc 100644 --- a/packages/browser-destinations/destinations/sprig-web/package.json +++ b/packages/browser-destinations/destinations/sprig-web/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-sprig", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/stackadapt/package.json b/packages/browser-destinations/destinations/stackadapt/package.json index 9dc20efd5e..d1070765a5 100644 --- a/packages/browser-destinations/destinations/stackadapt/package.json +++ b/packages/browser-destinations/destinations/stackadapt/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-stackadapt", - "version": "1.82.0", + "version": "1.83.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/survicate/package.json b/packages/browser-destinations/destinations/survicate/package.json index ae5343dff0..420d46ea6a 100644 --- a/packages/browser-destinations/destinations/survicate/package.json +++ b/packages/browser-destinations/destinations/survicate/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-survicate", - "version": "1.56.0", + "version": "1.57.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/tiktok-pixel/package.json b/packages/browser-destinations/destinations/tiktok-pixel/package.json index d2bf75dc0d..ad83c9600b 100644 --- a/packages/browser-destinations/destinations/tiktok-pixel/package.json +++ b/packages/browser-destinations/destinations/tiktok-pixel/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-tiktok-pixel", - "version": "1.81.0", + "version": "1.82.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/upollo/package.json b/packages/browser-destinations/destinations/upollo/package.json index 3218995584..576ed8bc41 100644 --- a/packages/browser-destinations/destinations/upollo/package.json +++ b/packages/browser-destinations/destinations/upollo/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-upollo", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/userpilot/package.json b/packages/browser-destinations/destinations/userpilot/package.json index fe840c6438..9a7821b5ec 100644 --- a/packages/browser-destinations/destinations/userpilot/package.json +++ b/packages/browser-destinations/destinations/userpilot/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-userpilot", - "version": "1.80.0", + "version": "1.81.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/vwo/package.json b/packages/browser-destinations/destinations/vwo/package.json index 50fb08c609..1ab369d0bc 100644 --- a/packages/browser-destinations/destinations/vwo/package.json +++ b/packages/browser-destinations/destinations/vwo/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-vwo", - "version": "1.83.0", + "version": "1.84.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/browser-destinations/destinations/wisepops/package.json b/packages/browser-destinations/destinations/wisepops/package.json index 9f79edd038..3397262a6f 100644 --- a/packages/browser-destinations/destinations/wisepops/package.json +++ b/packages/browser-destinations/destinations/wisepops/package.json @@ -1,6 +1,6 @@ { "name": "@segment/analytics-browser-actions-wiseops", - "version": "1.81.0", + "version": "1.82.0", "license": "MIT", "publishConfig": { "access": "public", @@ -15,8 +15,8 @@ }, "typings": "./dist/esm", "dependencies": { - "@segment/actions-core": "^3.150.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/actions-core": "^3.151.0", + "@segment/browser-destination-runtime": "^1.80.0" }, "peerDependencies": { "@segment/analytics-next": ">=1.55.0" diff --git a/packages/core/package.json b/packages/core/package.json index 4a710f1928..e36219a776 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@segment/actions-core", "description": "Core runtime for Destinations Actions.", - "version": "3.150.0", + "version": "3.151.0", "repository": { "type": "git", "url": "https://github.com/segmentio/fab-5-engine", diff --git a/packages/destination-actions/package.json b/packages/destination-actions/package.json index 3b02b4b245..4993eb6a2c 100644 --- a/packages/destination-actions/package.json +++ b/packages/destination-actions/package.json @@ -1,7 +1,7 @@ { "name": "@segment/action-destinations", "description": "Destination Actions engine and definitions.", - "version": "3.381.0", + "version": "3.382.0", "repository": { "type": "git", "url": "https://github.com/segmentio/action-destinations", @@ -45,8 +45,8 @@ "@aws-sdk/client-s3": "^3.600.0", "@bufbuild/protobuf": "^2.2.3", "@segment/a1-notation": "^2.1.4", - "@segment/actions-core": "^3.150.0", - "@segment/actions-shared": "^1.131.0", + "@segment/actions-core": "^3.151.0", + "@segment/actions-shared": "^1.132.0", "@types/node": "^22.13.1", "ajv-formats": "^2.1.1", "aws4": "^1.12.0", diff --git a/packages/destinations-manifest/package.json b/packages/destinations-manifest/package.json index ffabb76e60..073f3323f1 100644 --- a/packages/destinations-manifest/package.json +++ b/packages/destinations-manifest/package.json @@ -1,6 +1,6 @@ { "name": "@segment/destinations-manifest", - "version": "1.110.0", + "version": "1.111.0", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" @@ -12,45 +12,45 @@ "main": "./dist/index.js", "typings": "./dist/index.d.ts", "dependencies": { - "@segment/analytics-browser-actions-1flow": "^1.62.0", - "@segment/analytics-browser-actions-adobe-target": "^1.81.0", - "@segment/analytics-browser-actions-algolia-plugins": "^1.57.0", - "@segment/analytics-browser-actions-amplitude-plugins": "^1.80.0", - "@segment/analytics-browser-actions-braze": "^1.88.0", - "@segment/analytics-browser-actions-braze-cloud-plugins": "^1.88.0", - "@segment/analytics-browser-actions-bucket": "^1.61.0", - "@segment/analytics-browser-actions-cdpresolution": "^1.67.0", - "@segment/analytics-browser-actions-commandbar": "^1.80.0", - "@segment/analytics-browser-actions-contentstack-web": "^1.23.0", - "@segment/analytics-browser-actions-devrev": "^1.67.0", - "@segment/analytics-browser-actions-friendbuy": "^1.81.0", - "@segment/analytics-browser-actions-fullstory": "^1.82.0", - "@segment/analytics-browser-actions-google-analytics-4": "^1.87.0", - "@segment/analytics-browser-actions-google-campaign-manager": "^1.71.0", - "@segment/analytics-browser-actions-heap": "^1.80.0", - "@segment/analytics-browser-actions-hubspot": "^1.80.0", - "@segment/analytics-browser-actions-intercom": "^1.85.0", - "@segment/analytics-browser-actions-iterate": "^1.80.0", - "@segment/analytics-browser-actions-jimo": "^1.70.0", - "@segment/analytics-browser-actions-koala": "^1.82.0", - "@segment/analytics-browser-actions-logrocket": "^1.81.0", - "@segment/analytics-browser-actions-pendo-web-actions": "^1.70.0", - "@segment/analytics-browser-actions-playerzero": "^1.80.0", - "@segment/analytics-browser-actions-replaybird": "^1.61.0", - "@segment/analytics-browser-actions-ripe": "^1.80.0", + "@segment/analytics-browser-actions-1flow": "^1.63.0", + "@segment/analytics-browser-actions-adobe-target": "^1.82.0", + "@segment/analytics-browser-actions-algolia-plugins": "^1.58.0", + "@segment/analytics-browser-actions-amplitude-plugins": "^1.81.0", + "@segment/analytics-browser-actions-braze": "^1.89.0", + "@segment/analytics-browser-actions-braze-cloud-plugins": "^1.89.0", + "@segment/analytics-browser-actions-bucket": "^1.62.0", + "@segment/analytics-browser-actions-cdpresolution": "^1.68.0", + "@segment/analytics-browser-actions-commandbar": "^1.81.0", + "@segment/analytics-browser-actions-contentstack-web": "^1.24.0", + "@segment/analytics-browser-actions-devrev": "^1.68.0", + "@segment/analytics-browser-actions-friendbuy": "^1.82.0", + "@segment/analytics-browser-actions-fullstory": "^1.83.0", + "@segment/analytics-browser-actions-google-analytics-4": "^1.88.0", + "@segment/analytics-browser-actions-google-campaign-manager": "^1.72.0", + "@segment/analytics-browser-actions-heap": "^1.81.0", + "@segment/analytics-browser-actions-hubspot": "^1.81.0", + "@segment/analytics-browser-actions-intercom": "^1.86.0", + "@segment/analytics-browser-actions-iterate": "^1.81.0", + "@segment/analytics-browser-actions-jimo": "^1.71.0", + "@segment/analytics-browser-actions-koala": "^1.83.0", + "@segment/analytics-browser-actions-logrocket": "^1.82.0", + "@segment/analytics-browser-actions-pendo-web-actions": "^1.71.0", + "@segment/analytics-browser-actions-playerzero": "^1.81.0", + "@segment/analytics-browser-actions-replaybird": "^1.62.0", + "@segment/analytics-browser-actions-ripe": "^1.81.0", "@segment/analytics-browser-actions-sabil": "^1.6.0", - "@segment/analytics-browser-actions-screeb": "^1.81.0", - "@segment/analytics-browser-actions-snap-plugins": "^1.61.0", - "@segment/analytics-browser-actions-sprig": "^1.80.0", - "@segment/analytics-browser-actions-stackadapt": "^1.82.0", - "@segment/analytics-browser-actions-survicate": "^1.56.0", - "@segment/analytics-browser-actions-tiktok-pixel": "^1.81.0", - "@segment/analytics-browser-actions-upollo": "^1.80.0", - "@segment/analytics-browser-actions-userpilot": "^1.80.0", - "@segment/analytics-browser-actions-utils": "^1.80.0", - "@segment/analytics-browser-actions-vwo": "^1.83.0", - "@segment/analytics-browser-actions-wiseops": "^1.81.0", - "@segment/analytics-browser-hubble-web": "^1.66.0", - "@segment/browser-destination-runtime": "^1.79.0" + "@segment/analytics-browser-actions-screeb": "^1.82.0", + "@segment/analytics-browser-actions-snap-plugins": "^1.62.0", + "@segment/analytics-browser-actions-sprig": "^1.81.0", + "@segment/analytics-browser-actions-stackadapt": "^1.83.0", + "@segment/analytics-browser-actions-survicate": "^1.57.0", + "@segment/analytics-browser-actions-tiktok-pixel": "^1.82.0", + "@segment/analytics-browser-actions-upollo": "^1.81.0", + "@segment/analytics-browser-actions-userpilot": "^1.81.0", + "@segment/analytics-browser-actions-utils": "^1.81.0", + "@segment/analytics-browser-actions-vwo": "^1.84.0", + "@segment/analytics-browser-actions-wiseops": "^1.82.0", + "@segment/analytics-browser-hubble-web": "^1.67.0", + "@segment/browser-destination-runtime": "^1.80.0" } } From 7e549a01d54076f727e7473c91a1dda3e289a415 Mon Sep 17 00:00:00 2001 From: maryamsharif <99763167+maryamsharif@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:44:12 -0700 Subject: [PATCH 32/78] [Google Ads] Standardize Phone Number Formatting (#2810) --- .../__tests__/uploadClickConversion.test.ts | 12 +-- .../__tests__/uploadClickConversion2.test.ts | 12 +-- .../uploadConversionAdjustment.test.ts | 86 +++++++++++++++--- .../uploadConversionAdjustment2.test.ts | 89 +++++++++++++++---- .../google-enhanced-conversions/functions.ts | 6 +- .../uploadClickConversion/generated-types.ts | 4 + .../uploadClickConversion/index.ts | 12 ++- .../uploadClickConversion2/generated-types.ts | 4 + .../uploadClickConversion2/index.ts | 12 ++- .../generated-types.ts | 4 + .../uploadConversionAdjustment/index.ts | 16 +++- .../generated-types.ts | 4 + .../uploadConversionAdjustment2/index.ts | 14 ++- 13 files changed, 220 insertions(+), 55 deletions(-) diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadClickConversion.test.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadClickConversion.test.ts index de4c91f981..8720400b43 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadClickConversion.test.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadClickConversion.test.ts @@ -85,7 +85,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]}],\\"partialFailure\\":true}"` + `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -127,7 +127,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]}],\\"partialFailure\\":true}"` + `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -305,7 +305,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]}],\\"partialFailure\\":true}"` + `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -761,7 +761,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]}],\\"partialFailure\\":true}"` + `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -824,7 +824,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]}],\\"partialFailure\\":true}"` + `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -1082,7 +1082,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]}],\\"partialFailure\\":true}"` + `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadClickConversion2.test.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadClickConversion2.test.ts index 6ebad44a1e..87f668ce92 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadClickConversion2.test.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadClickConversion2.test.ts @@ -92,7 +92,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]}],\\"partialFailure\\":true}"` + `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -137,7 +137,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]}],\\"partialFailure\\":true}"` + `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -328,7 +328,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]}],\\"partialFailure\\":true}"` + `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -722,7 +722,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]}],\\"partialFailure\\":true}"` + `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -789,7 +789,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"ef873ef70d75ae19678b2bacbddd956cccda7b619b4ffc7af2b60e570d27b095\\"}]},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]}],\\"partialFailure\\":true}"` + `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"22563905dd330440cb95d11761541dd3bd7f9b704b132392c717a3633582884c\\"}]},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -1060,7 +1060,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"ef873ef70d75ae19678b2bacbddd956cccda7b619b4ffc7af2b60e570d27b095\\"}]},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"e63a1ca2fcb2a9d4c7db0dfae9e63d86c7cdbb7cfdba742848f50f38d460a5ec\\"}]}],\\"partialFailure\\":true}"` + `"{\\"conversions\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"22563905dd330440cb95d11761541dd3bd7f9b704b132392c717a3633582884c\\"}]},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"conversionValue\\":200,\\"currencyCode\\":\\"USD\\",\\"cartData\\":{\\"items\\":[{\\"productId\\":\\"1234\\",\\"quantity\\":3,\\"unitPrice\\":10.99}]},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"76ff44c6428f2fc2750fec01cb3190423adaebb21e797d942f339f3c7c1761dd\\"}]}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadConversionAdjustment.test.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadConversionAdjustment.test.ts index 5c41408339..e4e0b5e0b7 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadConversionAdjustment.test.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadConversionAdjustment.test.ts @@ -60,7 +60,65 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + ) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + }) + + it('should correctly format phone number with country code +1', async () => { + const event = createTestEvent({ + timestamp, + event: 'Test Event', + properties: { + gclid: '54321', + email: 'test@gmail.com', + orderId: '1234', + phone: '234567890', + firstName: 'Jane', + lastName: 'Doe', + currency: 'USD', + value: '123', + address: { + street: '123 Street SW', + city: 'San Diego', + state: 'CA', + postalCode: '982004' + } + } + }) + + nock(`https://googleads.googleapis.com/${API_VERSION}/customers/${customerId}:uploadConversionAdjustments`) + .post('') + .reply(201, { results: [{}] }) + + const responses = await testDestination.testAction('uploadConversionAdjustment', { + event, + mapping: { + gclid: { + '@path': '$.properties.gclid' + }, + conversion_action: '12345', + adjustment_type: 'ENHANCEMENT', + conversion_timestamp: { + '@path': '$.timestamp' + }, + restatement_value: { + '@path': '$.properties.value' + }, + restatement_currency_code: { + '@path': '$.properties.currency' + } + }, + useDefaultMappings: true, + settings: { + customerId + } + }) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -192,7 +250,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -228,7 +286,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -304,7 +362,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -341,7 +399,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -402,7 +460,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -460,7 +518,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -573,7 +631,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -750,7 +808,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -796,7 +854,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -895,7 +953,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -942,7 +1000,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -1025,7 +1083,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -1105,7 +1163,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadConversionAdjustment2.test.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadConversionAdjustment2.test.ts index 391a02b4ab..c7f13a6479 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadConversionAdjustment2.test.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/uploadConversionAdjustment2.test.ts @@ -17,7 +17,7 @@ describe('GoogleEnhancedConversions', () => { gclid: '54321', email: 'test@gmail.com', orderId: '1234', - phone: '1234567890', + phone: '+1234567890', firstName: 'Jane', lastName: 'Doe', currency: 'USD', @@ -61,7 +61,66 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + ) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + }) + + it('should correctly format phone number with country code +1', async () => { + const event = createTestEvent({ + timestamp, + event: 'Test Event', + properties: { + gclid: '54321', + email: 'test@gmail.com', + orderId: '1234', + phone: '234567890', + firstName: 'Jane', + lastName: 'Doe', + currency: 'USD', + value: '123', + address: { + street: '123 Street SW', + city: 'San Diego', + state: 'CA', + postalCode: '982004' + } + } + }) + + nock(`https://googleads.googleapis.com/${API_VERSION}/customers/${customerId}:uploadConversionAdjustments`) + .post('') + .reply(201, { results: [{}] }) + + const responses = await testDestination.testAction('uploadConversionAdjustment2', { + event, + mapping: { + gclid: { + '@path': '$.properties.gclid' + }, + conversion_action: '12345', + adjustment_type: 'ENHANCEMENT', + conversion_timestamp: { + '@path': '$.timestamp' + }, + restatement_value: { + '@path': '$.properties.value' + }, + restatement_currency_code: { + '@path': '$.properties.currency' + }, + __segment_internal_sync_mode: 'add' + }, + useDefaultMappings: true, + settings: { + customerId + } + }) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -233,7 +292,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -270,7 +329,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -352,7 +411,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -390,7 +449,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -452,7 +511,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -511,7 +570,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -630,7 +689,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -858,7 +917,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -905,7 +964,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -1010,7 +1069,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RESTATEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -1058,7 +1117,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"RETRACTION\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"123a\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\"}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -1142,7 +1201,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) @@ -1223,7 +1282,7 @@ describe('GoogleEnhancedConversions', () => { }) expect(responses[0].options.body).toMatchInlineSnapshot( - `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` + `"{\\"conversionAdjustments\\":[{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"a295fa4e457ca8c72751ffb6196f34b2349dcd91443b8c70ad76082d30dbdcd9\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}},{\\"conversionAction\\":\\"customers/1234/conversionActions/12345\\",\\"adjustmentType\\":\\"ENHANCEMENT\\",\\"adjustmentDateTime\\":\\"2021-06-10 18:08:04+00:00\\",\\"orderId\\":\\"1234\\",\\"gclidDateTimePair\\":{\\"gclid\\":\\"54321\\",\\"conversionDateTime\\":\\"2021-06-10 18:08:04+00:00\\"},\\"userIdentifiers\\":[{\\"hashedEmail\\":\\"cc2e166955ec49675e749f9dce21db0cbd2979d4aac4a845bdde35ccb642bc47\\"},{\\"hashedPhoneNumber\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\"},{\\"addressInfo\\":{\\"hashedFirstName\\":\\"4f23798d92708359b734a18172c9c864f1d48044a754115a0d4b843bca3a5332\\",\\"hashedLastName\\":\\"fd53ef835b15485572a6e82cf470dcb41fd218ae5751ab7531c956a2a6bcd3c7\\"}}],\\"userAgent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"restatementValue\\":{\\"adjustedValue\\":123,\\"currencyCode\\":\\"USD\\"}}],\\"partialFailure\\":true}"` ) expect(responses.length).toBe(1) diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/functions.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/functions.ts index 2cd0713e71..819d8e435e 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/functions.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/functions.ts @@ -434,7 +434,11 @@ export function formatToE164(phoneNumber: string, countryCode: string): string { return formattedPhoneNumber } -const formatPhone = (phone: string, countryCode?: string): string => { +export const formatPhone = (phone: string, countryCode?: string): string => { + // Check if phone number is already hashed before doing any formatting + if (isHashedInformation(phone)) { + return phone + } const formattedPhone = formatToE164(phone, countryCode ?? '+1') return formattedPhone } diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/generated-types.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/generated-types.ts index ec9bd8ee13..bca9684f09 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/generated-types.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/generated-types.ts @@ -25,6 +25,10 @@ export interface Payload { * Email address of the individual who triggered the conversion event */ email_address?: string + /** + * The numeric country code to associate with the phone number. If not provided Segment will default to '+1'. If the country code does not start with '+' Segment will add it. + */ + phone_country_code?: string /** * Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000 */ diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/index.ts index f517b19de0..4ea3f707e3 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/index.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion/index.ts @@ -21,7 +21,8 @@ import { convertTimestamp, getApiVersion, commonEmailValidation, - getConversionActionDynamicData + getConversionActionDynamicData, + formatPhone } from '../functions' import { GOOGLE_ENHANCED_CONVERSIONS_BATCH_SIZE } from '../constants' import { processHashing } from '../../../lib/hashing-utils' @@ -77,6 +78,11 @@ const action: ActionDefinition = { }, category: 'hashedPII' }, + phone_country_code: { + label: 'Phone Number Country Code', + description: `The numeric country code to associate with the phone number. If not provided Segment will default to '+1'. If the country code does not start with '+' Segment will add it.`, + type: 'string' + }, phone_number: { label: 'Phone Number', description: @@ -335,7 +341,7 @@ const action: ActionDefinition = { 'hex', features ?? {}, 'actions-google-enhanced-conversions', - (value) => '+' + value.split('+').join('') + (value) => formatPhone(value, payload.phone_country_code) ) } as UserIdentifierInterface) } @@ -454,7 +460,7 @@ const action: ActionDefinition = { 'hex', features ?? {}, 'actions-google-enhanced-conversions', - (value) => '+' + value.split('+').join('') + (value) => formatPhone(value, payload.phone_country_code) ) } as UserIdentifierInterface) } diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/generated-types.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/generated-types.ts index ec9bd8ee13..bca9684f09 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/generated-types.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/generated-types.ts @@ -25,6 +25,10 @@ export interface Payload { * Email address of the individual who triggered the conversion event */ email_address?: string + /** + * The numeric country code to associate with the phone number. If not provided Segment will default to '+1'. If the country code does not start with '+' Segment will add it. + */ + phone_country_code?: string /** * Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000 */ diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/index.ts index f66b5c8110..a254247036 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/index.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadClickConversion2/index.ts @@ -22,7 +22,8 @@ import { getApiVersion, commonEmailValidation, getConversionActionDynamicData, - memoizedGetCustomVariables + memoizedGetCustomVariables, + formatPhone } from '../functions' import { GOOGLE_ENHANCED_CONVERSIONS_BATCH_SIZE } from '../constants' import { processHashing } from '../../../lib/hashing-utils' @@ -84,6 +85,11 @@ const action: ActionDefinition = { }, category: 'hashedPII' }, + phone_country_code: { + label: 'Phone Number Country Code', + description: `The numeric country code to associate with the phone number. If not provided Segment will default to '+1'. If the country code does not start with '+' Segment will add it.`, + type: 'string' + }, phone_number: { label: 'Phone Number', description: @@ -343,7 +349,7 @@ const action: ActionDefinition = { 'hex', features ?? {}, 'actions-google-enhanced-conversions', - (value) => '+' + value.split('+').join('') + (value) => formatPhone(value, payload.phone_country_code) ) } as UserIdentifierInterface) } @@ -467,7 +473,7 @@ const action: ActionDefinition = { 'hex', features ?? {}, 'actions-google-enhanced-conversions', - (value) => '+' + value.split('+').join('') + (value) => formatPhone(value, payloadItem.phone_country_code) ) } as UserIdentifierInterface) } diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/generated-types.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/generated-types.ts index a24c2683ae..b8ef568db6 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/generated-types.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/generated-types.ts @@ -37,6 +37,10 @@ export interface Payload { * Email address of the individual who triggered the conversion event. */ email_address?: string + /** + * The numeric country code to associate with the phone number. If not provided Segment will default to '+1'. If the country code does not start with '+' Segment will add it. + */ + phone_country_code?: string /** * Phone number of the individual who triggered the conversion event, in E.164 standard format, e.g. +14150000000 */ diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/index.ts index 911a5c937d..3949c1dec8 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/index.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment/index.ts @@ -3,8 +3,9 @@ import { handleGoogleErrors, convertTimestamp, getApiVersion, - commonEmailValidation, - getConversionActionDynamicData + getConversionActionDynamicData, + formatPhone, + commonEmailValidation } from '../functions' import type { Settings } from '../generated-types' import type { Payload } from './generated-types' @@ -115,6 +116,11 @@ const action: ActionDefinition = { }, category: 'hashedPII' }, + phone_country_code: { + label: 'Phone Number Country Code', + description: `The numeric country code to associate with the phone number. If not provided Segment will default to '+1'. If the country code does not start with '+' Segment will add it.`, + type: 'string' + }, phone_number: { label: 'Phone Number', description: @@ -305,7 +311,8 @@ const action: ActionDefinition = { 'sha256', 'hex', features ?? {}, - 'actions-google-enhanced-conversions' + 'actions-google-enhanced-conversions', + (value) => formatPhone(value, payload.phone_country_code) ) } as UserIdentifierInterface) } @@ -433,7 +440,8 @@ const action: ActionDefinition = { 'sha256', 'hex', features ?? {}, - 'actions-google-enhanced-conversions' + 'actions-google-enhanced-conversions', + (value) => formatPhone(value, payloadItem.phone_country_code) ) } as UserIdentifierInterface) } diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/generated-types.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/generated-types.ts index 7079be7dd0..d92888d3f2 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/generated-types.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/generated-types.ts @@ -33,6 +33,10 @@ export interface Payload { * The currency of the restated value. If not provided, then the default currency from the conversion action is used, and if that is not set then the account currency is used. This is the ISO 4217 3-character currency code, e.g. USD or EUR. */ restatement_currency_code?: string + /** + * The numeric country code to associate with the phone number. If not provided Segment will default to '+1'. If the country code does not start with '+' Segment will add it. + */ + phone_country_code?: string /** * Email address of the individual who triggered the conversion event. */ diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/index.ts index 1902b86118..cc2d8ce746 100644 --- a/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/index.ts +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/uploadConversionAdjustment2/index.ts @@ -10,7 +10,8 @@ import { convertTimestamp, getApiVersion, commonEmailValidation, - getConversionActionDynamicData + getConversionActionDynamicData, + formatPhone } from '../functions' import type { Settings } from '../generated-types' import type { Payload } from './generated-types' @@ -114,6 +115,11 @@ const action: ActionDefinition = { ] } }, + phone_country_code: { + label: 'Phone Number Country Code', + description: `The numeric country code to associate with the phone number. If not provided Segment will default to '+1'. If the country code does not start with '+' Segment will add it.`, + type: 'string' + }, email_address: { label: 'Email Address', description: 'Email address of the individual who triggered the conversion event.', @@ -319,7 +325,8 @@ const action: ActionDefinition = { 'sha256', 'hex', features ?? {}, - 'actions-google-enhanced-conversions' + 'actions-google-enhanced-conversions', + (value) => formatPhone(value, payload.phone_country_code) ) } as UserIdentifierInterface) } @@ -457,7 +464,8 @@ const action: ActionDefinition = { 'sha256', 'hex', features ?? {}, - 'actions-google-enhanced-conversions' + 'actions-google-enhanced-conversions', + (value) => formatPhone(value, payloadItem.phone_country_code) ) } as UserIdentifierInterface) } From c29b7c34125a3de96c758b8943b0d32a073449c1 Mon Sep 17 00:00:00 2001 From: Varadarajan V <109586712+varadarajan-tw@users.noreply.github.com> Date: Wed, 9 Apr 2025 04:14:28 +0530 Subject: [PATCH 33/78] [STRATCONN-5676] - Adds support braze web sdk - 5.8.1 (#2833) --- .../__snapshots__/integration.test.ts.snap | 2 +- .../destinations/braze/src/index.ts | 6 ++- yarn.lock | 37 ++++++++++++++++--- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/packages/browser-destinations/destinations/braze/src/__tests__/__snapshots__/integration.test.ts.snap b/packages/browser-destinations/destinations/braze/src/__tests__/__snapshots__/integration.test.ts.snap index 8e13ff3164..02e85e2fa6 100644 --- a/packages/browser-destinations/destinations/braze/src/__tests__/__snapshots__/integration.test.ts.snap +++ b/packages/browser-destinations/destinations/braze/src/__tests__/__snapshots__/integration.test.ts.snap @@ -85,7 +85,7 @@ exports[`loads different versions from CDN undefined version: 1`] = ` NodeList [