From abbdca07374773952331c1674489db0c9c53eca4 Mon Sep 17 00:00:00 2001 From: ItsSudip Date: Sat, 24 Feb 2024 23:49:31 +0530 Subject: [PATCH 001/240] chore: updated test cases according to new flow for clevertap --- .../clevertap/dataDelivery/business.ts | 219 ++++++++++++++++++ .../clevertap/dataDelivery/data.ts | 5 +- 2 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 test/integrations/destinations/clevertap/dataDelivery/business.ts diff --git a/test/integrations/destinations/clevertap/dataDelivery/business.ts b/test/integrations/destinations/clevertap/dataDelivery/business.ts new file mode 100644 index 0000000000..edab4ee6d7 --- /dev/null +++ b/test/integrations/destinations/clevertap/dataDelivery/business.ts @@ -0,0 +1,219 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; + +const params = { + destination: 'clevertap', +}; +const headers = { + 'X-CleverTap-Account-Id': '476550467', + 'X-CleverTap-Passcode': + 'fbee74a147828e2932c701d19dc1f2dcfa4ac0048be3aa3a88d427090a59dc1c0fa002f1', + 'Content-Type': 'application/json', +}; +export const V1BusinessTestScenarion: ProxyV1TestData[] = [ + { + id: 'clevertap_business_0', + scenario: 'business', + successCriteria: 'should return 200 status code with success message', + name: 'clevertap', + description: '[business]:: create an user through identify call', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + params, + headers, + JSON: { + d: [ + { + type: 'profile', + profileData: { + Email: 'jamesDoe@gmail.com', + Name: 'James Doe', + Phone: '92374162212', + Gender: 'M', + Employed: true, + DOB: '1614775793', + Education: 'Science', + Married: 'Y', + 'Customer Type': 'Prime', + graduate: true, + msg_push: true, + msgSms: true, + msgemail: true, + msgwhatsapp: false, + custom_tags: '["Test_User","Interested_User","DIY_Hobby"]', + custom_mappings: '{"Office":"Trastkiv","Country":"Russia"}', + address: + '{"city":"kolkata","country":"India","postalCode":789223,"state":"WB","street":""}', + }, + identity: 'anon_id', + }, + ], + }, + endpoint: 'https://api.clevertap.com/1/upload/test1', + }, + [generateMetadata(123)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + metadata: generateMetadata(123), + error: '{"status":"success","processed":1,"unprocessed":[]}', + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'clevertap_business_1', + scenario: 'business', + successCriteria: 'should return 401 status code with error message', + name: 'clevertap', + description: '[business]:: event failed due to invalid credentials', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + params, + headers: { + 'X-CleverTap-Account-Id': 'fakeId123', + 'X-CleverTap-Passcode': 'fakePasscode123', + 'Content-Type': 'application/json', + }, + JSON: { + d: [ + { + identity: 'anon-id-new', + type: 'event', + evtName: 'Web Page Viewed: Rudder', + evtData: { + title: 'Home', + path: '/', + }, + }, + ], + }, + endpoint: 'https://api.clevertap.com/1/upload/test2', + }, + [generateMetadata(123)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 401, + message: 'Request failed with status: 401', + response: [ + { + metadata: generateMetadata(123), + error: '{"status":"fail","error":"Invalid Credentials","code":401}', + statusCode: 401, + }, + ], + statTags: { + destType: 'CLEVERTAP', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'clevertap_business_2', + scenario: 'business', + successCriteria: 'should return 401 status code with error message', + name: 'clevertap', + description: '[business]:: event failed due to invalid credentials', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + params, + headers: { + 'X-CleverTap-Account-Id': '476550467', + 'X-CleverTap-Passcode': + 'fbee74a147828e2932c701d19dc1f2dcfa4ac0048be3aa3a88d427090a59dc1c0fa002f1', + 'Content-Type': 'application/json', + }, + JSON: { + d: [ + { + identity: 'anon-id-new', + type: 'event', + evtData: { + title: 'Home', + path: '/', + }, + }, + ], + }, + endpoint: 'https://api.clevertap.com/1/upload/test3', + }, + [generateMetadata(123)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + message: 'Request failed with status: 200', + response: [ + { + metadata: generateMetadata(123), + error: '{"status":"fail","processed":0,"unprocessed":[]}', + statusCode: 400, + }, + ], + statTags: { + destType: 'CLEVERTAP', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/clevertap/dataDelivery/data.ts b/test/integrations/destinations/clevertap/dataDelivery/data.ts index 8032dd50c8..57e0d0ceea 100644 --- a/test/integrations/destinations/clevertap/dataDelivery/data.ts +++ b/test/integrations/destinations/clevertap/dataDelivery/data.ts @@ -1,4 +1,5 @@ -export const data = [ +import { V1BusinessTestScenarion } from './business'; +const oldV0TestCases = [ { name: 'clevertap', description: 'Test 0', @@ -228,3 +229,5 @@ export const data = [ }, }, ]; + +export const data = [...oldV0TestCases, ...V1BusinessTestScenarion]; From 9558e3bf848213bc75a58297d234d823b72a274a Mon Sep 17 00:00:00 2001 From: gitcommitshow Date: Fri, 1 Mar 2024 00:46:09 +0530 Subject: [PATCH 002/240] feat: add slack source --- src/v0/sources/slack/mapping.json | 10 +++++ src/v0/sources/slack/transform.js | 72 +++++++++++++++++++++++++++++++ src/v0/sources/slack/util.js | 15 +++++++ 3 files changed, 97 insertions(+) create mode 100644 src/v0/sources/slack/mapping.json create mode 100644 src/v0/sources/slack/transform.js create mode 100644 src/v0/sources/slack/util.js diff --git a/src/v0/sources/slack/mapping.json b/src/v0/sources/slack/mapping.json new file mode 100644 index 0000000000..c6c162c7c6 --- /dev/null +++ b/src/v0/sources/slack/mapping.json @@ -0,0 +1,10 @@ +[ + { + "sourceKeys": "ts", + "destKeys": ["timestamp", "originalTimestamp"] + }, + { + "sourceKeys": "type", + "destKeys": "event" + } +] diff --git a/src/v0/sources/slack/transform.js b/src/v0/sources/slack/transform.js new file mode 100644 index 0000000000..296c44d5f2 --- /dev/null +++ b/src/v0/sources/slack/transform.js @@ -0,0 +1,72 @@ +const sha256 = require('sha256'); +const { TransformationError } = require('@rudderstack/integrations-lib'); +const Message = require('../message'); +const { mapping, formEventName } = require('./util'); +const { generateUUID, removeUndefinedAndNullValues } = require('../../util'); +const { JSON_MIME_TYPE } = require('../../util/constant'); + +function processNormalEvent(slackPayload) { + const message = new Message(`SLACK`); + // we are setting event type as track always + message.setEventType('track'); + message.setEventName(formEventName(slackPayload.type)); + if (slackPayload.user) { + const stringifiedUserId = + typeof slackPayload.user === 'string' ? slackPayload.user : slackPayload.user.id; + message.setProperty( + 'anonymousId', + stringifiedUserId ? sha256(stringifiedUserId).toString().substring(0, 36) : generateUUID(), + ); + // setting the userId got from Monday into externalId + message.context.externalId = [ + { + type: 'slackUserId', + id: stringifiedUserId, + }, + ]; + } else { + throw new TransformationError('UserId not found'); + } + + message.setPropertiesV2(slackPayload, mapping); + /* deleting properties already mapped in + the payload's root */ + delete message.properties.triggerTime; + delete message.properties.userId; + return message; +} + +// the payload for the challenge event will be as follows: +// { +// challenge : "some_key" +// } +// this will be sent when the webhook is added to an item in monday. + +function isChallengeEvent(event) { + return !!event?.challenge; +} + +// sending challenge event object back to Monday +function processChallengeEvent(event) { + return { + outputToSource: { + body: Buffer.from(JSON.stringify(event)).toString('base64'), + contentType: JSON_MIME_TYPE, + }, + statusCode: 200, + }; +} + +// we will check here if the event is a challenge event or not +// and process accordingly. +// For challenge event the recieved challenge object is sent back +// to Monday to verify the webhook url. +// Ref: https://developer.monday.com/api-reference/docs/webhooks-1#how-to-verify-a-webhook-url +function process(event) { + const response = isChallengeEvent(event) + ? processChallengeEvent(event) + : processNormalEvent(event); + return removeUndefinedAndNullValues(response); +} + +exports.process = process; diff --git a/src/v0/sources/slack/util.js b/src/v0/sources/slack/util.js new file mode 100644 index 0000000000..45978d0a68 --- /dev/null +++ b/src/v0/sources/slack/util.js @@ -0,0 +1,15 @@ +/* eslint-disable no-restricted-syntax */ +const path = require('path'); +const fs = require('fs'); + +const mapping = JSON.parse(fs.readFileSync(path.resolve(__dirname, './mapping.json'), 'utf-8')); + +// turning underscore-seperated Monday events into Rudder format +function formEventName(evtName) { + return evtName + .split('_') + .map((s) => s.charAt(0).toUpperCase() + s.slice(1)) + .join(' '); +} + +module.exports = { mapping, formEventName }; From 42d6010d93b79ccb996c9548f4c0eeba0e3dacc2 Mon Sep 17 00:00:00 2001 From: gitcommitshow Date: Fri, 1 Mar 2024 14:40:19 +0530 Subject: [PATCH 003/240] fix: filtering url verification event via evvent type --- src/v0/sources/slack/mapping.json | 4 ++++ src/v0/sources/slack/transform.js | 9 +++++---- src/v0/sources/slack/util.js | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/v0/sources/slack/mapping.json b/src/v0/sources/slack/mapping.json index c6c162c7c6..304473180e 100644 --- a/src/v0/sources/slack/mapping.json +++ b/src/v0/sources/slack/mapping.json @@ -6,5 +6,9 @@ { "sourceKeys": "type", "destKeys": "event" + }, + { + "sourceKeys": "channel", + "destKeys": "properties.slackChannel" } ] diff --git a/src/v0/sources/slack/transform.js b/src/v0/sources/slack/transform.js index 296c44d5f2..d9460b7d23 100644 --- a/src/v0/sources/slack/transform.js +++ b/src/v0/sources/slack/transform.js @@ -31,8 +31,8 @@ function processNormalEvent(slackPayload) { message.setPropertiesV2(slackPayload, mapping); /* deleting properties already mapped in the payload's root */ - delete message.properties.triggerTime; - delete message.properties.userId; + delete message.properties?.ts; + delete message.properties?.type; return message; } @@ -43,14 +43,15 @@ function processNormalEvent(slackPayload) { // this will be sent when the webhook is added to an item in monday. function isChallengeEvent(event) { - return !!event?.challenge; + return event?.type === 'url_verification' && !!event?.challenge; } // sending challenge event object back to Monday function processChallengeEvent(event) { + const response = { challenge: event?.challenge }; return { outputToSource: { - body: Buffer.from(JSON.stringify(event)).toString('base64'), + body: Buffer.from(JSON.stringify(response)).toString('base64'), contentType: JSON_MIME_TYPE, }, statusCode: 200, diff --git a/src/v0/sources/slack/util.js b/src/v0/sources/slack/util.js index 45978d0a68..6f8270cd8d 100644 --- a/src/v0/sources/slack/util.js +++ b/src/v0/sources/slack/util.js @@ -4,7 +4,7 @@ const fs = require('fs'); const mapping = JSON.parse(fs.readFileSync(path.resolve(__dirname, './mapping.json'), 'utf-8')); -// turning underscore-seperated Monday events into Rudder format +// turning underscore-seperated Slack events into Rudder format function formEventName(evtName) { return evtName .split('_') From b19eb8c699b60e90e02b39e3a448e6aac721e37f Mon Sep 17 00:00:00 2001 From: gitcommitshow Date: Sat, 2 Mar 2024 19:08:27 +0530 Subject: [PATCH 004/240] fix: add originalTimestamp from slack ts property --- src/v0/sources/slack/mapping.json | 8 ------- src/v0/sources/slack/transform.js | 13 ++++++------ src/v0/sources/slack/util.js | 35 ++++++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/v0/sources/slack/mapping.json b/src/v0/sources/slack/mapping.json index 304473180e..02f31482d4 100644 --- a/src/v0/sources/slack/mapping.json +++ b/src/v0/sources/slack/mapping.json @@ -1,14 +1,6 @@ [ - { - "sourceKeys": "ts", - "destKeys": ["timestamp", "originalTimestamp"] - }, { "sourceKeys": "type", "destKeys": "event" - }, - { - "sourceKeys": "channel", - "destKeys": "properties.slackChannel" } ] diff --git a/src/v0/sources/slack/transform.js b/src/v0/sources/slack/transform.js index d9460b7d23..6c244a1d78 100644 --- a/src/v0/sources/slack/transform.js +++ b/src/v0/sources/slack/transform.js @@ -1,7 +1,7 @@ const sha256 = require('sha256'); const { TransformationError } = require('@rudderstack/integrations-lib'); const Message = require('../message'); -const { mapping, formEventName } = require('./util'); +const { mapping, tsToISODate, formEventName } = require('./util'); const { generateUUID, removeUndefinedAndNullValues } = require('../../util'); const { JSON_MIME_TYPE } = require('../../util/constant'); @@ -27,12 +27,13 @@ function processNormalEvent(slackPayload) { } else { throw new TransformationError('UserId not found'); } - + // Set the standard event property originalTimestamp + message.setProperty('originalTimestamp', tsToISODate(slackPayload.ts || slackPayload.event_ts)); + // Map the remaining standard event properties according to mappings for the payload properties message.setPropertiesV2(slackPayload, mapping); - /* deleting properties already mapped in - the payload's root */ - delete message.properties?.ts; - delete message.properties?.type; + // Copy the complete Slack payload to message.properties + if (!message.properties) message.properties = {}; + Object.assign(message.properties, slackPayload); return message; } diff --git a/src/v0/sources/slack/util.js b/src/v0/sources/slack/util.js index 6f8270cd8d..712a75bb6c 100644 --- a/src/v0/sources/slack/util.js +++ b/src/v0/sources/slack/util.js @@ -4,6 +4,39 @@ const fs = require('fs'); const mapping = JSON.parse(fs.readFileSync(path.resolve(__dirname, './mapping.json'), 'utf-8')); +/** + * Converts a Slack timestamp to RudderStack's standard timestamp format - ISO 8601 date string. + * The Slack timestamp is a string that represents unix timestamp (seconds since the Unix Epoch) + * with fractional seconds for millisecond precision. + * If the timestamp is not provided, the function returns the current date and time in ISO 8601 format. + * + * @param {string} [slackTs] - The Slack timestamp to be converted. + * @returns {string} The ISO 8601 formatted date string corresponding to the given Slack timestamp + * or the current date and time if no timestamp is provided. + * + * @example + * // Convert a Slack timestamp to an ISO 8601 date string + * const slackTimestamp = "1609459200.123000"; + * const isoDate = tsToISODate(slackTimestamp); + * console.log(isoDate); // Output: "2021-01-01T00:00:00.123Z" (depending on your timezone) + */ +function tsToISODate(slackTs) { + // Default to current date if slackTs is not provided + if (!slackTs) return new Date().toISOString(); + + // Convert slackTs string into unix timestamp in milliseconds + const msTimestamp = parseFloat(slackTs) * 1000; + // Convert to a date object + if (Number.isNaN(msTimestamp)) { + // If timestamp was not a valid float, the parser will return NaN, stop processing the timestamp further and return null + return null; + } + const date = new Date(msTimestamp); + + // Return the date in ISO 8601 format + return date.toISOString(); +} + // turning underscore-seperated Slack events into Rudder format function formEventName(evtName) { return evtName @@ -12,4 +45,4 @@ function formEventName(evtName) { .join(' '); } -module.exports = { mapping, formEventName }; +module.exports = { mapping, tsToISODate, formEventName }; From d0c5942e6255dab97b493fff311a4af71a142fd8 Mon Sep 17 00:00:00 2001 From: gitcommitshow Date: Sun, 3 Mar 2024 14:51:38 +0530 Subject: [PATCH 005/240] feat: add identify event and the latest slack event schema --- src/v0/sources/slack/mapping.json | 46 ++++++++++++++- src/v0/sources/slack/transform.js | 97 +++++++++++++++++++++---------- src/v0/sources/slack/util.js | 16 ++++- 3 files changed, 125 insertions(+), 34 deletions(-) diff --git a/src/v0/sources/slack/mapping.json b/src/v0/sources/slack/mapping.json index 02f31482d4..f7825bd88d 100644 --- a/src/v0/sources/slack/mapping.json +++ b/src/v0/sources/slack/mapping.json @@ -1,6 +1,50 @@ [ { - "sourceKeys": "type", + "sourceKeys": "event.type", "destKeys": "event" + }, + { + "sourceKeys": "event.user.tz", + "destKeys": "timezone" + }, + { + "sourceKeys": "event.user.profile.email", + "destKeys": "context.traits.email" + }, + { + "sourceKeys": "event.user.profile.phone", + "destKeys": "context.traits.phone" + }, + { + "sourceKeys": "event.user.profile.real_name_normalized", + "destKeys": "context.traits.name" + }, + { + "sourceKeys": "event.user.profile.real_name", + "destKeys": "context.traits.name" + }, + { + "sourceKeys": "event.user.profile.display_name_normalized", + "destKeys": "context.traits.name" + }, + { + "sourceKeys": "event.user.profile.display_name", + "destKeys": "context.traits.name" + }, + { + "sourceKeys": "event.user.profile.first_name", + "destKeys": "context.traits.firstName" + }, + { + "sourceKeys": "event.user.profile.last_name", + "destKeys": "context.traits.lastName" + }, + { + "sourceKeys": "event.user.profile.image_original", + "destKeys": "context.traits.avatar" + }, + { + "sourceKeys": "event.user.profile.title", + "destKeys": "context.traits.title" } ] diff --git a/src/v0/sources/slack/transform.js b/src/v0/sources/slack/transform.js index 6c244a1d78..12c60df2ba 100644 --- a/src/v0/sources/slack/transform.js +++ b/src/v0/sources/slack/transform.js @@ -1,23 +1,44 @@ const sha256 = require('sha256'); const { TransformationError } = require('@rudderstack/integrations-lib'); const Message = require('../message'); -const { mapping, tsToISODate, formEventName } = require('./util'); +const { mapping, tsToISODate, normalizeEventName } = require('./util'); const { generateUUID, removeUndefinedAndNullValues } = require('../../util'); const { JSON_MIME_TYPE } = require('../../util/constant'); +const { EventType } = require('../../../constants'); +/** + * Transform event data to RudderStack supported standard event schema + * @param {Object} slackPayload - The complete data received on the webhook from Slack + * @param {Object} slackPayload.event - The data object specific to the Slack event received. Has different schema for different event types. + * @returns {Object} Event data transformed to RudderStack supported standard event schema + */ function processNormalEvent(slackPayload) { const message = new Message(`SLACK`); - // we are setting event type as track always - message.setEventType('track'); - message.setEventName(formEventName(slackPayload.type)); - if (slackPayload.user) { + if (!slackPayload || !slackPayload.event) { + throw new TransformationError('Missing the required event data'); + } + switch (slackPayload.event.type) { + case 'team_join': + message.setEventType(EventType.IDENTIFY); + break; + case 'user_change': + message.setEventType(EventType.IDENTIFY); + break; + default: + message.setEventType(EventType.TRACK); + break; + } + message.setEventName(normalizeEventName(slackPayload.event.type)); + if (slackPayload.event.user) { const stringifiedUserId = - typeof slackPayload.user === 'string' ? slackPayload.user : slackPayload.user.id; + typeof slackPayload.event.user === 'object' + ? slackPayload.event.user.id + : slackPayload.event.user; message.setProperty( 'anonymousId', stringifiedUserId ? sha256(stringifiedUserId).toString().substring(0, 36) : generateUUID(), ); - // setting the userId got from Monday into externalId + // Set the user id received from Slack into externalId message.context.externalId = [ { type: 'slackUserId', @@ -27,28 +48,31 @@ function processNormalEvent(slackPayload) { } else { throw new TransformationError('UserId not found'); } - // Set the standard event property originalTimestamp - message.setProperty('originalTimestamp', tsToISODate(slackPayload.ts || slackPayload.event_ts)); + // Set the standard common event fields. More info at https://www.rudderstack.com/docs/event-spec/standard-events/common-fields/ + // originalTimestamp - The actual time (in UTC) when the event occurred + message.setProperty( + 'originalTimestamp', + tsToISODate(slackPayload.event.ts || slackPayload.event.event_ts || slackPayload.event_time), + ); + // sentAt - Time, client-side, when the event was sent from the client to RudderStack + message.setProperty('sentAt', tsToISODate(slackPayload.event_time)); // Map the remaining standard event properties according to mappings for the payload properties message.setPropertiesV2(slackPayload, mapping); - // Copy the complete Slack payload to message.properties + // Copy the complete Slack event payload to message.properties if (!message.properties) message.properties = {}; - Object.assign(message.properties, slackPayload); + Object.assign(message.properties, slackPayload.event); return message; } -// the payload for the challenge event will be as follows: -// { -// challenge : "some_key" -// } -// this will be sent when the webhook is added to an item in monday. - -function isChallengeEvent(event) { - return event?.type === 'url_verification' && !!event?.challenge; -} - -// sending challenge event object back to Monday -function processChallengeEvent(event) { +/** + * Handles a special event for webhook url verification. + * Responds back with the challenge key received in the request. + * Reference - https://api.slack.com/apis/connections/events-api#subscribing + * @param {Object} event - Event data received from Slack + * @param {string} event.challenge - The challenge key received in the request + * @returns response that needs to be sent back to the source, alongwith the same challenge key received int the request + */ +function processUrlVerificationEvent(event) { const response = { challenge: event?.challenge }; return { outputToSource: { @@ -59,14 +83,27 @@ function processChallengeEvent(event) { }; } -// we will check here if the event is a challenge event or not -// and process accordingly. -// For challenge event the recieved challenge object is sent back -// to Monday to verify the webhook url. -// Ref: https://developer.monday.com/api-reference/docs/webhooks-1#how-to-verify-a-webhook-url +/** + * Checks if the event is a special url verification event or not. + * Slack sends this event at the time of webhook setup to verify webhook url ownership for the security purpose. + * Reference - https://api.slack.com/apis/connections/events-api#subscribing + * @param {Object} event - Event data received from Slack + * @param {string} event.challenge - The challenge key received in the request + * @param {string} event.type - The type of Slack event. `url_verification` when it is a special webhook url verification event. + * @returns {boolean} true if it is a valid challenge event for url verification event + */ +function isWebhookUrlVerificationEvent(event) { + return event?.type === 'url_verification' && !!event?.challenge; +} + +/** + * Processes the event with needed transformation and sends back the response + * Reference - https://api.slack.com/apis/connections/events-api + * @param {Object} event + */ function process(event) { - const response = isChallengeEvent(event) - ? processChallengeEvent(event) + const response = isWebhookUrlVerificationEvent(event) + ? processUrlVerificationEvent(event) : processNormalEvent(event); return removeUndefinedAndNullValues(response); } diff --git a/src/v0/sources/slack/util.js b/src/v0/sources/slack/util.js index 712a75bb6c..c4b3bdfa4e 100644 --- a/src/v0/sources/slack/util.js +++ b/src/v0/sources/slack/util.js @@ -37,12 +37,22 @@ function tsToISODate(slackTs) { return date.toISOString(); } -// turning underscore-seperated Slack events into Rudder format -function formEventName(evtName) { +/** + * Converts an event name from snake_case to a RudderStack format - space-separated string with each word capitalized. + * @param {string} evtName - The event name in snake_case format to be normalized. + * @returns {string} The normalized event name with spaces between words and each word capitalized. + * + * @example + * // Convert a slack event name to RudderStack format + * const eventName = "member_joined_channel"; + * const normalizedEventName = normalizeEventName(eventName); + * console.log(normalizedEventName); // Output: "Member Joined Channel" + */ +function normalizeEventName(evtName) { return evtName .split('_') .map((s) => s.charAt(0).toUpperCase() + s.slice(1)) .join(' '); } -module.exports = { mapping, tsToISODate, formEventName }; +module.exports = { mapping, tsToISODate, normalizeEventName }; From e08b826e8547e44284927dd542b822f5578d0959 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 4 Mar 2024 13:13:50 +0000 Subject: [PATCH 006/240] chore(release): 1.58.0 --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a043cfb6e3..c143851091 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,32 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.58.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.58.0) (2024-03-04) + + +### Features + +* add support for interaction events in sfmc ([#3109](https://github.com/rudderlabs/rudder-transformer/issues/3109)) ([0486049](https://github.com/rudderlabs/rudder-transformer/commit/0486049ba2ad96b50d8f29e96b46b96a8a5c9f76)) +* add support of custom page/screen event name in mixpanel ([#3098](https://github.com/rudderlabs/rudder-transformer/issues/3098)) ([0eb2393](https://github.com/rudderlabs/rudder-transformer/commit/0eb2393939fba2452ef7f07a1d149d87f18290c3)) +* consent mode support for google adwords remarketing list ([#3143](https://github.com/rudderlabs/rudder-transformer/issues/3143)) ([7532c90](https://github.com/rudderlabs/rudder-transformer/commit/7532c90d7e1feac00f12961c56da18757010f44a)) +* **facebook:** update content_type mapping logic for fb pixel and fb conversions ([#3113](https://github.com/rudderlabs/rudder-transformer/issues/3113)) ([aea417c](https://github.com/rudderlabs/rudder-transformer/commit/aea417cd2691547399010c034cadbc5db6b0c6ee)) +* klaviyo profile mapping ([#3105](https://github.com/rudderlabs/rudder-transformer/issues/3105)) ([2761786](https://github.com/rudderlabs/rudder-transformer/commit/2761786ff3fc99ed6d4d3b7a6c2400226b1cfb12)) +* onboard new destination ninetailed ([#3106](https://github.com/rudderlabs/rudder-transformer/issues/3106)) ([0e2588e](https://github.com/rudderlabs/rudder-transformer/commit/0e2588ecd87f3b2c6877a099aa1cbf2d5325966c)) + + +### Bug Fixes + +* add error handling for tiktok ads ([#3144](https://github.com/rudderlabs/rudder-transformer/issues/3144)) ([e93e47f](https://github.com/rudderlabs/rudder-transformer/commit/e93e47f33e098104fb532916932fe38bbfeaa4a1)) +* **algolia:** added check for objectIds or filters to be non empty ([#3126](https://github.com/rudderlabs/rudder-transformer/issues/3126)) ([d619c97](https://github.com/rudderlabs/rudder-transformer/commit/d619c9769cd270cb2d16dad0865683ff4beb2d19)) +* clevertap remove stringification of array object properties ([#3048](https://github.com/rudderlabs/rudder-transformer/issues/3048)) ([69e43b6](https://github.com/rudderlabs/rudder-transformer/commit/69e43b6ffadeaec87b7440da34a341890ceba252)) +* convert to string from null in hs ([#3136](https://github.com/rudderlabs/rudder-transformer/issues/3136)) ([75e9f46](https://github.com/rudderlabs/rudder-transformer/commit/75e9f462b0ff9b9a8abab3c78dc7d147926e9e5e)) +* event fix and added utility ([#3142](https://github.com/rudderlabs/rudder-transformer/issues/3142)) ([9b705b7](https://github.com/rudderlabs/rudder-transformer/commit/9b705b71a9d3a595ea0fbf532602c3941b0a18db)) +* metadata structure correction ([#3119](https://github.com/rudderlabs/rudder-transformer/issues/3119)) ([8351b5c](https://github.com/rudderlabs/rudder-transformer/commit/8351b5cbbf81bbc14b2f884feaae4ad3ca59a39a)) +* one_signal: Encode external_id in endpoint ([#3140](https://github.com/rudderlabs/rudder-transformer/issues/3140)) ([8a20886](https://github.com/rudderlabs/rudder-transformer/commit/8a2088608d6da4b35bbb506db2fc3df1e4d41f3b)) +* rakuten: sync property mapping sourcekeys to rudderstack standard spec ([#3129](https://github.com/rudderlabs/rudder-transformer/issues/3129)) ([2ebff95](https://github.com/rudderlabs/rudder-transformer/commit/2ebff956ff2aa74b008a8de832a31d8774d2d47e)) +* reddit revenue mapping for floating point values ([#3118](https://github.com/rudderlabs/rudder-transformer/issues/3118)) ([41f4078](https://github.com/rudderlabs/rudder-transformer/commit/41f4078011ef54334bb9ecc11a7b2ccc8831a4aa)) +* version deprecation failure false positive ([#3104](https://github.com/rudderlabs/rudder-transformer/issues/3104)) ([657b780](https://github.com/rudderlabs/rudder-transformer/commit/657b7805eb01da25a007d978198d5debf03917fd)) + ### [1.57.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.0...v1.57.1) (2024-03-04) diff --git a/package-lock.json b/package-lock.json index a629744cf4..700207e021 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.57.1", + "version": "1.58.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.57.1", + "version": "1.58.0", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index f5908bc7ff..46e7ab98ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.57.1", + "version": "1.58.0", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From 4653b74522cc917230c211ce1df1b57e8a607ad7 Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Mon, 4 Mar 2024 19:10:23 +0530 Subject: [PATCH 007/240] fix: am formatting issues --- src/v0/destinations/am/transform.js | 1 - src/v0/destinations/am/utils.js | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/v0/destinations/am/transform.js b/src/v0/destinations/am/transform.js index bc08315fa8..2d78479ced 100644 --- a/src/v0/destinations/am/transform.js +++ b/src/v0/destinations/am/transform.js @@ -336,7 +336,6 @@ const getDefaultResponseData = (message, rawPayload, evType, groupInfo) => { return { groups, rawPayload }; }; - const getResponseData = (evType, destination, rawPayload, message, groupInfo) => { let groups; diff --git a/src/v0/destinations/am/utils.js b/src/v0/destinations/am/utils.js index 4d4fd5dc37..190a5c1bae 100644 --- a/src/v0/destinations/am/utils.js +++ b/src/v0/destinations/am/utils.js @@ -123,7 +123,6 @@ const validateEventType = (evType) => { } }; - const userPropertiesPostProcess = (rawPayload) => { const operationList = [ '$setOnce', @@ -187,5 +186,5 @@ module.exports = { getEventId, getUnsetObj, validateEventType, - userPropertiesPostProcess + userPropertiesPostProcess, }; From 05ffe820e5c5a3b346f39c268dd49fca47568461 Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Mon, 4 Mar 2024 19:41:08 +0530 Subject: [PATCH 008/240] fix: prepare-for-staging-deploy.yml --- .github/workflows/prepare-for-staging-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prepare-for-staging-deploy.yml b/.github/workflows/prepare-for-staging-deploy.yml index e7df8c43a5..a69cf90c8c 100644 --- a/.github/workflows/prepare-for-staging-deploy.yml +++ b/.github/workflows/prepare-for-staging-deploy.yml @@ -101,7 +101,7 @@ jobs: cd rudder-devops BRANCH_NAME="shared-transformer-$TAG_NAME" echo $BRANCH_NAME - if [ `git ls-remote --heads origin $BRANCH_NAME 2>/dev/null` ] + if [ -n `git ls-remote --heads origin $BRANCH_NAME 2>/dev/null` ] then echo "Staging deployment branch already exists!" else From afb2f450ddee0522e802327dce68ac33a04c9639 Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Mon, 4 Mar 2024 22:34:03 +0530 Subject: [PATCH 009/240] fix: prepare-for-staging-deploy.yml --- .github/workflows/prepare-for-staging-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prepare-for-staging-deploy.yml b/.github/workflows/prepare-for-staging-deploy.yml index a69cf90c8c..1bd7e276f4 100644 --- a/.github/workflows/prepare-for-staging-deploy.yml +++ b/.github/workflows/prepare-for-staging-deploy.yml @@ -101,7 +101,7 @@ jobs: cd rudder-devops BRANCH_NAME="shared-transformer-$TAG_NAME" echo $BRANCH_NAME - if [ -n `git ls-remote --heads origin $BRANCH_NAME 2>/dev/null` ] + if [ -n "$(git ls-remote --heads origin $BRANCH_NAME 2>/dev/null)" ] then echo "Staging deployment branch already exists!" else From 17da0a9cd2efb7b3ae061db081c737cb38d30df2 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Tue, 5 Mar 2024 11:23:15 +0530 Subject: [PATCH 010/240] fix: release fix feat, bug order (#3165) --- github-release.config.js | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 github-release.config.js diff --git a/github-release.config.js b/github-release.config.js new file mode 100644 index 0000000000..4194af4530 --- /dev/null +++ b/github-release.config.js @@ -0,0 +1,5 @@ +module.exports = { + gitRawCommitsOpts: { + merges: null, + }, + }; \ No newline at end of file diff --git a/package.json b/package.json index 46e7ab98ac..070510029b 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "commit-msg": "commitlint --edit", "prepare": "node ./scripts/skipPrepareScript.js || husky install", "release": "npx standard-version", - "release:github": "DEBUG=conventional-github-releaser npx conventional-github-releaser -p angular -v", + "release:github": "DEBUG=conventional-github-releaser npx conventional-github-releaser -p angular --config github-release.config.js", "clean:node": "modclean", "check:lint": "eslint . -f json -o reports/eslint.json || exit 0" }, From dff7eb9b8072016a16e7083c60507a9d03302f17 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Tue, 5 Mar 2024 11:32:26 +0530 Subject: [PATCH 011/240] fix: release action git (#3166) --- github-release.config.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/github-release.config.js b/github-release.config.js index 4194af4530..df269d8a02 100644 --- a/github-release.config.js +++ b/github-release.config.js @@ -1,5 +1,5 @@ module.exports = { - gitRawCommitsOpts: { - merges: null, - }, - }; \ No newline at end of file + gitRawCommitsOpts: { + merges: null, + }, +}; From 0771e87ff73181c412636f9bde40b8947a3e3080 Mon Sep 17 00:00:00 2001 From: gitcommitshow Date: Tue, 5 Mar 2024 23:48:49 +0530 Subject: [PATCH 012/240] fix: handle error-first --- src/v0/sources/slack/transform.js | 33 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/v0/sources/slack/transform.js b/src/v0/sources/slack/transform.js index 12c60df2ba..d67d1bb017 100644 --- a/src/v0/sources/slack/transform.js +++ b/src/v0/sources/slack/transform.js @@ -29,25 +29,24 @@ function processNormalEvent(slackPayload) { break; } message.setEventName(normalizeEventName(slackPayload.event.type)); - if (slackPayload.event.user) { - const stringifiedUserId = - typeof slackPayload.event.user === 'object' - ? slackPayload.event.user.id - : slackPayload.event.user; - message.setProperty( - 'anonymousId', - stringifiedUserId ? sha256(stringifiedUserId).toString().substring(0, 36) : generateUUID(), - ); - // Set the user id received from Slack into externalId - message.context.externalId = [ - { - type: 'slackUserId', - id: stringifiedUserId, - }, - ]; - } else { + if (!slackPayload.event.user) { throw new TransformationError('UserId not found'); } + const stringifiedUserId = + typeof slackPayload.event.user === 'object' + ? slackPayload.event.user.id + : slackPayload.event.user; + message.setProperty( + 'anonymousId', + stringifiedUserId ? sha256(stringifiedUserId).toString().substring(0, 36) : generateUUID(), + ); + // Set the user id received from Slack into externalId + message.context.externalId = [ + { + type: 'slackUserId', + id: stringifiedUserId, + }, + ]; // Set the standard common event fields. More info at https://www.rudderstack.com/docs/event-spec/standard-events/common-fields/ // originalTimestamp - The actual time (in UTC) when the event occurred message.setProperty( From c106590214129596b9d24b9c741d799199139ba6 Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Wed, 6 Mar 2024 10:30:01 +0530 Subject: [PATCH 013/240] chore: add v1 proxy tests for salesforce (#3074) * feat: update proxy data type for response handler input * feat: update proxy v1 test cases * feat: update proxy tests for cm360 Added new structure for proxy test scnearios for cm360 also added zod validations as part of tests * fix: typo * Update test/integrations/destinations/campaign_manager/dataDelivery/business.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update test/integrations/destinations/campaign_manager/dataDelivery/business.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix: api contract for v1 proxy * chore: clean up zod type * chore: update testutils * chore: update V0 proxy request type and zod schema * feat: adding zod validations (#3066) * feat: add type definitions for test cases * fix: update networkHandler for rakuten --------- Co-authored-by: Utsab Chowdhury * chore: update delivery test cases for criteo audience * Revert "chore: update delivery test cases for criteo audience" This reverts commit 689b0cda0aeace910e82167375045e123e365300. * chore: add initial business tests * chore: add type def for proxy v1 test * chore: fix generateMetdata func * chore: cleanup * chore: add other scenario test, refactor * chore: address commentsx1 * chore: move test to other * chore: address commentsx2 --------- Co-authored-by: Utsab Chowdhury Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Utsab Chowdhury Co-authored-by: ItsSudip --- .../salesforce/dataDelivery/business.ts | 380 ++++++++++++++++++ .../salesforce/dataDelivery/data.ts | 77 +--- .../salesforce/dataDelivery/other.ts | 106 +++++ .../destinations/salesforce/network.ts | 264 +++++++++--- 4 files changed, 710 insertions(+), 117 deletions(-) create mode 100644 test/integrations/destinations/salesforce/dataDelivery/business.ts create mode 100644 test/integrations/destinations/salesforce/dataDelivery/other.ts diff --git a/test/integrations/destinations/salesforce/dataDelivery/business.ts b/test/integrations/destinations/salesforce/dataDelivery/business.ts new file mode 100644 index 0000000000..4e98a3fc1a --- /dev/null +++ b/test/integrations/destinations/salesforce/dataDelivery/business.ts @@ -0,0 +1,380 @@ +import { ProxyMetdata } from '../../../../../src/types'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const commonHeaders = { + Authorization: 'Bearer token', + 'Content-Type': 'application/json', +}; +const params = { destination: 'salesforce' }; + +const users = [ + { + Email: 'danis.archurav@sbermarket.ru', + Company: 'itus.ru', + LastName: 'Danis', + FirstName: 'Archurav', + LeadSource: 'App Signup', + account_type__c: 'free_trial', + }, +]; + +const statTags = { + aborted: { + destType: 'SALESFORCE', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, + retryable: { + destType: 'SALESFORCE', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, + throttled: { + destType: 'SALESFORCE', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'throttled', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, +}; + +export const proxyMetdata: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; + +export const reqMetadataArray = [proxyMetdata]; + +const commonRequestParameters = { + headers: commonHeaders, + JSON: users[0], + params, +}; + +const externalIdSearchData = { Planning_Categories__c: 'pc', External_ID__c: 123 }; +export const externalIDSearchedData = { + headers: commonHeaders, + JSON: externalIdSearchData, + params, +}; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'salesforce_v1_scenario_1', + name: 'salesforce', + description: + '[Proxy v1 API] :: Test for a valid request - Lead creation with existing unchanged leadId and unchanged data', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: + 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/existing_unchanged_leadId', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request for destination: salesforce Processed Successfully', + response: [ + { + error: '{"statusText":"No Content"}', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_scenario_2', + name: 'salesforce', + description: '[Proxy v1 API] :: Test with session expired scenario', + successCriteria: 'Should return 5XX with error Session expired or invalid, INVALID_SESSION_ID', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: + 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/invalid_session_id', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + message: + 'Salesforce Request Failed - due to "Session expired or invalid", (Retryable) during Salesforce Response Handling', + response: [ + { + error: + '[{"message":"Session expired or invalid","errorCode":"INVALID_SESSION_ID"}]', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + statTags: statTags.retryable, + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_scenario_3', + name: 'salesforce', + description: '[Proxy v1 API] :: Test for Invalid Auth token passed in header', + successCriteria: 'Should return 401 INVALID_AUTH_HEADER', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/2', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + message: + 'Salesforce Request Failed: "401" due to "INVALID_HEADER_TYPE", (Aborted) during Salesforce Response Handling', + response: [ + { + error: '[{"message":"INVALID_HEADER_TYPE","errorCode":"INVALID_AUTH_HEADER"}]', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + statTags: statTags.aborted, + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_scenario_4', + name: 'salesforce', + description: '[Proxy v1 API] :: Test for rate limit exceeded scenario', + successCriteria: 'Should return 429 with error message "Request limit exceeded"', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/4', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + 'Salesforce Request Failed - due to "REQUEST_LIMIT_EXCEEDED", (Throttled) during Salesforce Response Handling', + response: [ + { + error: + '[{"message":"Request limit exceeded","errorCode":"REQUEST_LIMIT_EXCEEDED"}]', + metadata: proxyMetdata, + statusCode: 429, + }, + ], + statTags: statTags.throttled, + status: 429, + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_scenario_5', + name: 'salesforce', + description: '[Proxy v1 API] :: Test for server unavailable scenario', + successCriteria: 'Should return 500 with error message "Server Unavailable"', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/5', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + 'Salesforce Request Failed - due to "Server Unavailable", (Retryable) during Salesforce Response Handling', + response: [ + { + error: '[{"message":"Server Unavailable","errorCode":"SERVER_UNAVAILABLE"}]', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + statTags: statTags.retryable, + status: 500, + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_scenario_6', + name: 'salesforce', + description: '[Proxy v1 API] :: Test for invalid grant scenario due to authentication failure', + successCriteria: + 'Should return 400 with error message "invalid_grant" due to "authentication failure"', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/6', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + 'Salesforce Request Failed: "400" due to "{"error":"invalid_grant","error_description":"authentication failure"}", (Aborted) during Salesforce Response Handling', + response: [ + { + error: '{"error":"invalid_grant","error_description":"authentication failure"}', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + statTags: statTags.aborted, + status: 400, + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_scenario_7', + name: 'salesforce', + description: '[Proxy v1 API] :: Test for a valid request - External ID search', + successCriteria: 'Should return 200 with list of matching records with External ID', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...externalIDSearchedData, + endpoint: + 'https://rudderstack.my.salesforce.com/services/data/v50.0/parameterizedSearch/?q=123&sobject=object_name&in=External_ID__c&object_name.fields=id,External_ID__c', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request for destination: salesforce Processed Successfully', + response: [ + { + error: + '{"searchRecords":[{"attributes":{"type":"object_name","url":"/services/data/v50.0/sobjects/object_name/a0J75100002w97gEAA"},"Id":"a0J75100002w97gEAA","External_ID__c":"external_id"},{"attributes":{"type":"object_name","url":"/services/data/v50.0/sobjects/object_name/a0J75200002w9ZsEAI"},"Id":"a0J75200002w9ZsEAI","External_ID__c":"external_id TEST"}]}', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/salesforce/dataDelivery/data.ts b/test/integrations/destinations/salesforce/dataDelivery/data.ts index cfaa75e23e..d376289d97 100644 --- a/test/integrations/destinations/salesforce/dataDelivery/data.ts +++ b/test/integrations/destinations/salesforce/dataDelivery/data.ts @@ -1,7 +1,18 @@ import { AxiosError } from 'axios'; import MockAdapter from 'axios-mock-adapter'; +import { testScenariosForV1API } from './business'; +import { otherSalesforceScenariosV1 } from './other'; -export const data = [ +const legacyDataValue = { + Email: 'danis.archurav@sbermarket.ru', + Company: 'itus.ru', + LastName: 'Danis', + FirstName: 'Archurav', + LeadSource: 'App Signup', + account_type__c: 'free_trial', +}; + +const legacyTests = [ { name: 'salesforce', description: 'Test 0', @@ -24,14 +35,7 @@ export const data = [ body: { XML: {}, FORM: {}, - JSON: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + JSON: legacyDataValue, JSON_ARRAY: {}, }, metadata: { @@ -86,14 +90,7 @@ export const data = [ body: { XML: {}, FORM: {}, - JSON: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + JSON: legacyDataValue, JSON_ARRAY: {}, }, metadata: { @@ -162,14 +159,7 @@ export const data = [ body: { XML: {}, FORM: {}, - JSON: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + JSON: legacyDataValue, JSON_ARRAY: {}, }, metadata: { @@ -238,14 +228,7 @@ export const data = [ body: { XML: {}, FORM: {}, - JSON: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + JSON: legacyDataValue, JSON_ARRAY: {}, }, metadata: { @@ -314,14 +297,7 @@ export const data = [ body: { XML: {}, FORM: {}, - JSON: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + JSON: legacyDataValue, JSON_ARRAY: {}, }, metadata: { @@ -390,14 +366,7 @@ export const data = [ body: { XML: {}, FORM: {}, - JSON: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + JSON: legacyDataValue, JSON_ARRAY: {}, }, metadata: { @@ -464,14 +433,7 @@ export const data = [ body: { XML: {}, FORM: {}, - JSON: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + JSON: legacyDataValue, JSON_ARRAY: {}, }, metadata: { @@ -781,3 +743,4 @@ export const data = [ }, }, ]; +export const data = [...legacyTests, ...testScenariosForV1API, ...otherSalesforceScenariosV1]; diff --git a/test/integrations/destinations/salesforce/dataDelivery/other.ts b/test/integrations/destinations/salesforce/dataDelivery/other.ts new file mode 100644 index 0000000000..b3361caba7 --- /dev/null +++ b/test/integrations/destinations/salesforce/dataDelivery/other.ts @@ -0,0 +1,106 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const statTags = { + errorCategory: 'network', + errorType: 'retryable', + destType: 'SALESFORCE', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; +const metadata = { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: false, +}; + +export const otherSalesforceScenariosV1: ProxyV1TestData[] = [ + { + id: 'salesforce_v1_other_scenario_1', + name: 'salesforce', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://sf_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 500, + metadata, + }, + ], + statTags, + message: + 'Salesforce Request Failed - due to "{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}", (Retryable) during Salesforce Response Handling', + status: 500, + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_other_scenario_2', + name: 'salesforce', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata, + }, + ], + statTags, + message: + 'Salesforce Request Failed - due to ""Internal Server Error"", (Retryable) during Salesforce Response Handling', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/salesforce/network.ts b/test/integrations/destinations/salesforce/network.ts index 396fad9d69..93013cd8db 100644 --- a/test/integrations/destinations/salesforce/network.ts +++ b/test/integrations/destinations/salesforce/network.ts @@ -1,15 +1,22 @@ +const commonHeaders = { + Authorization: 'Bearer token', + 'Content-Type': 'application/json', +}; + +const dataValue = { + Email: 'danis.archurav@sbermarket.ru', + Company: 'itus.ru', + LastName: 'Danis', + FirstName: 'Archurav', + LeadSource: 'App Signup', + account_type__c: 'free_trial', +}; + const tfProxyMocksData = [ { httpReq: { url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/1', - data: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + data: dataValue, params: { destination: 'salesforce' }, headers: { 'Content-Type': 'application/json', @@ -26,14 +33,7 @@ const tfProxyMocksData = [ { httpReq: { url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/3', - data: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + data: dataValue, params: { destination: 'salesforce' }, headers: { 'Content-Type': 'application/json', @@ -50,19 +50,11 @@ const tfProxyMocksData = [ { httpReq: { url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/2', - data: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + data: dataValue, params: { destination: 'salesforce' }, headers: { 'Content-Type': 'application/json', - Authorization: 'Bearer Incorrect_token', - 'User-Agent': 'RudderLabs', + Authorization: 'Bearer token', }, method: 'POST', }, @@ -74,14 +66,7 @@ const tfProxyMocksData = [ { httpReq: { url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/4', - data: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + data: dataValue, params: { destination: 'salesforce' }, headers: { 'Content-Type': 'application/json', @@ -98,14 +83,7 @@ const tfProxyMocksData = [ { httpReq: { url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/5', - data: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + data: dataValue, params: { destination: 'salesforce' }, headers: { 'Content-Type': 'application/json', @@ -122,14 +100,7 @@ const tfProxyMocksData = [ { httpReq: { url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/6', - data: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + data: dataValue, params: { destination: 'salesforce' }, headers: { 'Content-Type': 'application/json', @@ -146,19 +117,11 @@ const tfProxyMocksData = [ { httpReq: { url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/7', - data: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + data: dataValue, params: { destination: 'salesforce' }, headers: { 'Content-Type': 'application/json', Authorization: 'Bearer token', - 'User-Agent': 'RudderLabs', }, method: 'POST', }, @@ -323,4 +286,185 @@ const transformationMocksData = [ }, }, ]; -export const networkCallsData = [...tfProxyMocksData, ...transformationMocksData]; + +const businessMockData = [ + { + description: + 'Mock response from destination depicting a valid lead request, with no changed data', + httpReq: { + method: 'post', + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/existing_unchanged_leadId', + data: dataValue, + headers: commonHeaders, + }, + httpRes: { + data: { statusText: 'No Content' }, + status: 204, + }, + }, + { + description: 'Mock response from destination depicting a invalid session id', + httpReq: { + method: 'post', + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/invalid_session_id', + data: dataValue, + headers: commonHeaders, + }, + httpRes: { + data: [{ message: 'Session expired or invalid', errorCode: 'INVALID_SESSION_ID' }], + status: 500, + }, + }, + { + httpReq: { + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/2', + data: dataValue, + params: { destination: 'salesforce' }, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer Incorrect_token', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: [{ message: 'INVALID_HEADER_TYPE', errorCode: 'INVALID_AUTH_HEADER' }], + status: 401, + }, + }, + { + httpReq: { + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/4', + data: dataValue, + params: { destination: 'salesforce' }, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer token', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: [{ message: 'Request limit exceeded', errorCode: 'REQUEST_LIMIT_EXCEEDED' }], + status: 403, + }, + }, + { + httpReq: { + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/5', + data: dataValue, + params: { destination: 'salesforce' }, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer token', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: [{ message: 'Server Unavailable', errorCode: 'SERVER_UNAVAILABLE' }], + status: 503, + }, + }, + { + httpReq: { + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/6', + data: dataValue, + params: { destination: 'salesforce' }, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer token', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { error: 'invalid_grant', error_description: 'authentication failure' }, + status: 400, + }, + }, + { + httpReq: { + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/7', + data: dataValue, + params: { destination: 'salesforce' }, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer token', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + message: 'Server Unavailable', + errorCode: 'SERVER_UNAVAILABLE', + }, + status: 503, + }, + }, + { + httpReq: { + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/parameterizedSearch/?q=123&sobject=object_name&in=External_ID__c&object_name.fields=id,External_ID__c', + data: { Planning_Categories__c: 'pc', External_ID__c: 123 }, + params: { destination: 'salesforce' }, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer token', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + searchRecords: [ + { + attributes: { + type: 'object_name', + url: '/services/data/v50.0/sobjects/object_name/a0J75100002w97gEAA', + }, + Id: 'a0J75100002w97gEAA', + External_ID__c: 'external_id', + }, + { + attributes: { + type: 'object_name', + url: '/services/data/v50.0/sobjects/object_name/a0J75200002w9ZsEAI', + }, + Id: 'a0J75200002w9ZsEAI', + External_ID__c: 'external_id TEST', + }, + ], + }, + status: 200, + }, + }, +]; + +const otherMocksData = [ + { + description: + 'Mock response from destination depicting a valid lead request, with no changed data', + httpReq: { + method: 'post', + url: 'https://sf_test_url/test_for_service_not_available', + }, + httpRes: { + data: { + error: { + message: 'Service Unavailable', + description: + 'The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later.', + }, + }, + status: 503, + }, + }, +]; + +export const networkCallsData = [ + ...tfProxyMocksData, + ...transformationMocksData, + ...businessMockData, + ...otherMocksData +]; From a5d20ad7f6a71a176289f1a462e6853cfa67ec13 Mon Sep 17 00:00:00 2001 From: Utsab Chowdhury Date: Thu, 7 Mar 2024 18:30:20 +0530 Subject: [PATCH 014/240] chore: onboard script to generate testdata and test integration (#3112) --- .gitignore | 3 +- .../destinations/salesforce/network.ts | 2 +- test/integrations/testTypes.ts | 1 + test/integrations/testUtils.ts | 56 +++++++++--- test/scripts/testDataGenerator.ts | 88 +++++++++++++++++++ 5 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 test/scripts/testDataGenerator.ts diff --git a/.gitignore b/.gitignore index 956605f139..09c536ebb8 100644 --- a/.gitignore +++ b/.gitignore @@ -137,4 +137,5 @@ dist .idea # component test report -test_reports/ \ No newline at end of file +test_reports/ +temp/ diff --git a/test/integrations/destinations/salesforce/network.ts b/test/integrations/destinations/salesforce/network.ts index 93013cd8db..b422271d36 100644 --- a/test/integrations/destinations/salesforce/network.ts +++ b/test/integrations/destinations/salesforce/network.ts @@ -466,5 +466,5 @@ export const networkCallsData = [ ...tfProxyMocksData, ...transformationMocksData, ...businessMockData, - ...otherMocksData + ...otherMocksData, ]; diff --git a/test/integrations/testTypes.ts b/test/integrations/testTypes.ts index a46277d552..1c5a989f44 100644 --- a/test/integrations/testTypes.ts +++ b/test/integrations/testTypes.ts @@ -37,6 +37,7 @@ export interface mockType { } export interface TestCaseData { + id?: string; name: string; description: string; scenario?: string; diff --git a/test/integrations/testUtils.ts b/test/integrations/testUtils.ts index 2abe4c6d9a..7aede97cf7 100644 --- a/test/integrations/testUtils.ts +++ b/test/integrations/testUtils.ts @@ -1,4 +1,3 @@ -import { z } from 'zod'; import { globSync } from 'glob'; import { join } from 'path'; import { MockHttpCallsData, TestCaseData } from './testTypes'; @@ -6,24 +5,18 @@ import MockAdapter from 'axios-mock-adapter'; import isMatch from 'lodash/isMatch'; import { OptionValues } from 'commander'; import { removeUndefinedAndNullValues } from '@rudderstack/integrations-lib'; -import { - Destination, - Metadata, - ProxyMetdata, - ProxyV0Request, - ProxyV1Request, -} from '../../src/types'; +import tags from '../../src/v0/util/tags'; +import { existsSync, mkdirSync, writeFileSync } from 'fs'; +import { Destination, ProxyMetdata, ProxyV0Request, ProxyV1Request } from '../../src/types'; import { DeliveryV0ResponseSchema, DeliveryV0ResponseSchemaForOauth, DeliveryV1ResponseSchema, DeliveryV1ResponseSchemaForOauth, ProcessorTransformationResponseListSchema, - ProcessorTransformationResponseSchema, ProxyV0RequestSchema, ProxyV1RequestSchema, RouterTransformationResponseListSchema, - RouterTransformationResponseSchema, } from '../../src/types/zodTypes'; const generateAlphanumericId = (size = 36) => @@ -104,6 +97,49 @@ export const overrideDestination = (destination: Destination, overrideConfigValu }); }; +export const produceTestData = (testData: TestCaseData[], filterKeys = []) => { + const result: any = []; + testData.forEach((tcData) => { + let events; + try { + switch (tcData.feature) { + case tags.FEATURES.PROCESSOR: + events = tcData.input.request.body; + break; + case tags.FEATURES.BATCH: + events = tcData.input.request.body.input; + break; + case tags.FEATURES.ROUTER: + events = tcData.input.request.body.input; + break; + } + } catch (e) { + throw new Error( + `Error in producing test data for destination:${tcData.name}, id:${tcData.id}: ${e}`, + ); + } + + events.forEach((event) => { + const { message } = event; + // remove unwanted keys + filterKeys.forEach((key) => { + delete message[key]; + }); + result.push(message); + }); + }); + + // write the data to a file + + // create directory if not exists + const dir = join(__dirname, '../../temp'); + if (!existsSync(dir)) { + mkdirSync(dir); + } + writeFileSync(join(__dirname, '../../temp/test_data.json'), JSON.stringify(result, null, 2)); + console.log('Data generated successfully at temp/test_data.json'); +}; + export const generateIndentifyPayload: any = (parametersOverride: any) => { const payload = { type: 'identify', diff --git a/test/scripts/testDataGenerator.ts b/test/scripts/testDataGenerator.ts new file mode 100644 index 0000000000..a00e9fce32 --- /dev/null +++ b/test/scripts/testDataGenerator.ts @@ -0,0 +1,88 @@ +import path from 'path'; +import { TestCaseData } from '../integrations/testTypes'; +import { getTestData, getTestDataFilePaths, produceTestData } from '../integrations/testUtils'; +import { Command } from 'commander'; +import axios from 'axios'; +import * as fs from 'fs'; + +// Produces test data for a given destination +// Example usage +// npx ts-node test/scripts/testDataGenerator.ts --destination=klaviyo --feature=processor + +const command = new Command(); +command + .allowUnknownOption() + .option('-d, --destination ', 'Enter Destination Name') + .option('-f, --feature ', 'Enter Feature Name(processor, router)') + .option('-i, --index ', 'Enter Test index') + .option('-id, --id ', 'Enter unique "Id" of the test case you want to run') + .option('-dp, --dataPlane ', 'Enter Data Plane URL') + .option('-wk, --writeKey ', 'Enter Write Key') + .option( + '-fk, --filterKeys ', + 'Enter Keys to filter from the test data(originalTimestamp, timestamp, messageId etc)', + ) + .parse(); + +const opts = command.opts(); + +if (opts.destination === undefined) { + throw new Error('Destination is not provided'); +} + +const filterKeys = opts.filterKeys ? opts.filterKeys.split(',') : []; + +const rootDir = __dirname; +const resolvedpath = path.resolve(rootDir, '../integrations'); +const destinationTestDataPaths = getTestDataFilePaths(resolvedpath, opts); + +destinationTestDataPaths.forEach((testDataPath) => { + let testData: TestCaseData[] = getTestData(testDataPath); + if (opts.index !== undefined) { + testData = [testData[parseInt(opts.index)]]; + } + if (opts.id) { + testData = testData.filter((data) => { + if (data['id'] === opts.id) { + return true; + } + return false; + }); + } + console.log('Writing test data to ../../temp/test_data.json'); + produceTestData(testData, filterKeys); + + if (opts.dataPlane && opts.writeKey) { + // read file ../../temp/test_data.json + console.log('Sending data to data plane URL: ', opts.dataPlane); + + const resolvedpathForData = path.resolve(rootDir, '../../temp/test_data.json'); + + fs.readFile(resolvedpathForData, 'utf8', function (err, data) { + if (err) { + console.log(err); + } else { + const parsedData = JSON.parse(data); + axios + .post( + `${opts.dataPlane}/v1/batch`, + { + batch: parsedData, + }, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Basic ${Buffer.from(opts.writeKey + ':').toString('base64')}`, + }, + }, + ) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); + } + }); + } +}); From c1b3736ab60c9582bdf1c4b07a761976de0da16f Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Fri, 8 Mar 2024 11:46:43 +0530 Subject: [PATCH 015/240] fix: email mapping for clevertap --- .../clevertap/data/CleverTapIdentify.json | 2 +- .../destinations/clevertap/processor/data.ts | 124 ++++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/src/v0/destinations/clevertap/data/CleverTapIdentify.json b/src/v0/destinations/clevertap/data/CleverTapIdentify.json index 577e13c339..cdc4b28d93 100644 --- a/src/v0/destinations/clevertap/data/CleverTapIdentify.json +++ b/src/v0/destinations/clevertap/data/CleverTapIdentify.json @@ -1,7 +1,7 @@ [ { "destKey": "Email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/test/integrations/destinations/clevertap/processor/data.ts b/test/integrations/destinations/clevertap/processor/data.ts index 6309c5ec8a..1d7bdd7e78 100644 --- a/test/integrations/destinations/clevertap/processor/data.ts +++ b/test/integrations/destinations/clevertap/processor/data.ts @@ -122,6 +122,130 @@ export const data = [ }, }, }, + { + name: 'clevertap', + description: 'Should not load email from externalId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + passcode: 'sample_passcode', + accountId: '476550467', + trackAnonymous: true, + enableObjectIdMapping: false, + }, + }, + message: { + channel: 'web', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: 'anon_id', + type: 'identify', + traits: { + anonymousId: 'anon_id', + name: 'James Doe', + phone: '92374162212', + gender: 'M', + employed: true, + birthday: '1614775793', + education: 'Science', + graduate: true, + married: true, + customerType: 'Prime', + msg_push: true, + msgSms: true, + msgemail: true, + msgwhatsapp: false, + custom_tags: ['Test_User', 'Interested_User', 'DIY_Hobby'], + custom_mappings: { + Office: 'Trastkiv', + Country: 'Russia', + }, + address: { + city: 'kolkata', + country: 'India', + postalCode: 789223, + state: 'WB', + street: '', + }, + 'category-unsubscribe': { email: ['Marketing', 'Transactional'] }, + }, + context: { + externalId: [{ type: 'someId', id: 'someID' }], + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.clevertap.com/1/upload', + headers: { + 'X-CleverTap-Account-Id': '476550467', + 'X-CleverTap-Passcode': 'sample_passcode', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + d: [ + { + type: 'profile', + profileData: { + Name: 'James Doe', + Phone: '92374162212', + Gender: 'M', + Employed: true, + DOB: '1614775793', + Education: 'Science', + Married: true, + 'Customer Type': 'Prime', + graduate: true, + msg_push: true, + msgSms: true, + msgemail: true, + msgwhatsapp: false, + custom_mappings: '{"Office":"Trastkiv","Country":"Russia"}', + custom_tags: '["Test_User","Interested_User","DIY_Hobby"]', + address: + '{"city":"kolkata","country":"India","postalCode":789223,"state":"WB","street":""}', + 'category-unsubscribe': { email: ['Marketing', 'Transactional'] }, + }, + identity: 'anon_id', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, { name: 'clevertap', description: 'Test 1', From 01d460c3edaf39b35c4686516c9e9140be46aa5e Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Mon, 11 Mar 2024 12:48:09 +0530 Subject: [PATCH 016/240] fix: label not present in prometheus metrics (#3176) * fix: label not present in prometheus metrics Signed-off-by: Sai Sankeerth * fix: remove error logging Signed-off-by: Sai Sankeerth --- src/util/prometheus.js | 2 +- src/util/redis/redisConnector.test.js | 2 +- src/util/redis/testData/shopify_source.json | 15 ++++++++---- .../shopify/shopify_redis.util.test.js | 14 +++++++---- src/v0/sources/shopify/transform.js | 14 +++++++---- src/v0/sources/shopify/util.js | 23 +++++++++++++------ 6 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 0fa17dc9bd..89e5424c0c 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -710,7 +710,7 @@ class Prometheus { name: 'get_libraries_code_time', help: 'get_libraries_code_time', type: 'histogram', - labelNames: ['libraryVersionId', 'versionId', 'type'], + labelNames: ['libraryVersionId', 'versionId', 'type', 'version'], }, { name: 'isolate_cpu_time', diff --git a/src/util/redis/redisConnector.test.js b/src/util/redis/redisConnector.test.js index 840f222e37..e0491132ff 100644 --- a/src/util/redis/redisConnector.test.js +++ b/src/util/redis/redisConnector.test.js @@ -70,7 +70,7 @@ describe(`Redis Class Get Tests`, () => { data.forEach((dataPoint, index) => { it(`${index}. Redis Get- ${dataPoint.description}`, async () => { try { - const output = await RedisDB.getVal(dataPoint.input.value, (isObjExpected = false)); + const output = await RedisDB.getVal(dataPoint.input.value, false); expect(output).toEqual(dataPoint.output); } catch (error) { expect(error.message).toEqual(dataPoint.output.error); diff --git a/src/util/redis/testData/shopify_source.json b/src/util/redis/testData/shopify_source.json index 53c6047298..04b80b8fc9 100644 --- a/src/util/redis/testData/shopify_source.json +++ b/src/util/redis/testData/shopify_source.json @@ -5,7 +5,8 @@ "user_id": "rudder01", "id": "shopify_test_get_items_fail", "query_parameters": { - "topic": ["carts_update"] + "topic": ["carts_update"], + "writeKey": ["wr"] }, "token": "shopify_test_get_items_fail", "email": "test@rudderstack.com", @@ -115,7 +116,8 @@ "input": { "cart_token": "shopifyGetAnonymousId", "query_parameters": { - "topic": ["checkouts_delete"] + "topic": ["checkouts_delete"], + "writeKey": ["wr"] }, "line_items": [], "note": null, @@ -154,7 +156,8 @@ "input": { "id": "shopify_test3", "query_parameters": { - "topic": ["carts_update"] + "topic": ["carts_update"], + "writeKey": ["wr"] }, "token": "shopify_test3", "line_items": [], @@ -252,7 +255,8 @@ "user_id": "rudder01", "id": "shopify_test_cart", "query_parameters": { - "topic": ["carts_update"] + "topic": ["carts_update"], + "writeKey": ["wr"] }, "token": "shopify_test_cart", "email": "test@rudderstack.com", @@ -1256,7 +1260,8 @@ "input": { "id": "shopify_test4", "query_parameters": { - "topic": ["carts_update"] + "topic": ["carts_update"], + "writeKey": ["wr"] }, "token": "shopify_test4", "line_items": [], diff --git a/src/v0/sources/shopify/shopify_redis.util.test.js b/src/v0/sources/shopify/shopify_redis.util.test.js index db596e1dfb..fb99837932 100644 --- a/src/v0/sources/shopify/shopify_redis.util.test.js +++ b/src/v0/sources/shopify/shopify_redis.util.test.js @@ -1,5 +1,9 @@ const { getAnonymousIdAndSessionId, checkAndUpdateCartItems } = require('./util'); jest.mock('ioredis', () => require('../../../../test/__mocks__/redis')); +const metricMetadata = { + writeKey: 'dummyKey', + source: 'src', +}; describe('Shopify Utils Test', () => { describe('Check for valid cart update event test cases', () => { it('Event containing token and nothing is retreived from redis and less than req. time difference between created_at and uadated_at', async () => { @@ -14,7 +18,7 @@ describe('Shopify Utils Test', () => { created_at: '2023-02-10T12:05:04.402Z', }; const expectedOutput = false; - const output = await checkAndUpdateCartItems(input); + const output = await checkAndUpdateCartItems(input, null, metricMetadata); expect(output).toEqual(expectedOutput); }); it('Event containing token and nothing is retreived from redis', async () => { @@ -28,7 +32,7 @@ describe('Shopify Utils Test', () => { ], }; const expectedOutput = true; - const output = await checkAndUpdateCartItems(input); + const output = await checkAndUpdateCartItems(input, null, metricMetadata); expect(output).toEqual(expectedOutput); }); @@ -44,7 +48,7 @@ describe('Shopify Utils Test', () => { }; const expectedOutput = true; - const output = await checkAndUpdateCartItems(input); + const output = await checkAndUpdateCartItems(input, null, metricMetadata); expect(output).toEqual(expectedOutput); }); @@ -60,7 +64,7 @@ describe('Shopify Utils Test', () => { }; const expectedOutput = false; - const output = await checkAndUpdateCartItems(input); + const output = await checkAndUpdateCartItems(input, null, metricMetadata); expect(output).toEqual(expectedOutput); }); @@ -76,7 +80,7 @@ describe('Shopify Utils Test', () => { }; const expectedOutput = true; - const output = await checkAndUpdateCartItems(input); + const output = await checkAndUpdateCartItems(input, null, metricMetadata); expect(output).toEqual(expectedOutput); }); }); diff --git a/src/v0/sources/shopify/transform.js b/src/v0/sources/shopify/transform.js index 013580d7a3..4f09984054 100644 --- a/src/v0/sources/shopify/transform.js +++ b/src/v0/sources/shopify/transform.js @@ -143,7 +143,7 @@ const processEvent = async (inputEvent, metricMetadata) => { break; case 'carts_update': if (useRedisDatabase) { - redisData = await getDataFromRedis(event.id || event.token); + redisData = await getDataFromRedis(event.id || event.token, metricMetadata); const isValidEvent = await checkAndUpdateCartItems(inputEvent, redisData, metricMetadata); if (!isValidEvent) { return NO_OPERATION_SUCCESS; @@ -155,7 +155,8 @@ const processEvent = async (inputEvent, metricMetadata) => { if (!SUPPORTED_TRACK_EVENTS.includes(shopifyTopic)) { stats.increment('invalid_shopify_event', { event: shopifyTopic, - ...metricMetadata, + source: metricMetadata.source, + shopifyTopic: metricMetadata.shopifyTopic, }); return NO_OPERATION_SUCCESS; } @@ -215,7 +216,8 @@ const processIdentifierEvent = async (event, metricMetadata) => { stats.increment('shopify_redis_calls', { type: 'set', field: 'itemsHash', - ...metricMetadata, + source: metricMetadata.source, + writeKey: metricMetadata.writeKey, }); /* cart_token: { anonymousId: 'anon_id1', @@ -236,14 +238,16 @@ const processIdentifierEvent = async (event, metricMetadata) => { stats.increment('shopify_redis_calls', { type: 'set', field, - ...metricMetadata, + source: metricMetadata.source, + writeKey: metricMetadata.writeKey, }); await RedisDB.setVal(`${event.cartToken}`, value); } catch (e) { logger.debug(`{{SHOPIFY::}} cartToken map set call Failed due redis error ${e}`); stats.increment('shopify_redis_failures', { type: 'set', - ...metricMetadata, + source: metricMetadata.source, + writeKey: metricMetadata.writeKey, }); } } diff --git a/src/v0/sources/shopify/util.js b/src/v0/sources/shopify/util.js index 6f31ade4a7..c4bbb61b9c 100644 --- a/src/v0/sources/shopify/util.js +++ b/src/v0/sources/shopify/util.js @@ -29,7 +29,8 @@ const getDataFromRedis = async (key, metricMetadata) => { stats.increment('shopify_redis_calls', { type: 'get', field: 'all', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, }); const redisData = await RedisDB.getVal(key); if ( @@ -37,7 +38,8 @@ const getDataFromRedis = async (key, metricMetadata) => { (typeof redisData === 'object' && Object.keys(redisData).length === 0) ) { stats.increment('shopify_redis_no_val', { - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, }); } return redisData; @@ -45,7 +47,8 @@ const getDataFromRedis = async (key, metricMetadata) => { logger.debug(`{{SHOPIFY::}} Get call Failed due redis error ${e}`); stats.increment('shopify_redis_failures', { type: 'get', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, }); } return null; @@ -166,7 +169,9 @@ const getAnonymousIdAndSessionId = async (message, metricMetadata, redisData = n if (isDefinedAndNotNull(anonymousId) && isDefinedAndNotNull(sessionId)) { stats.increment('shopify_anon_id_resolve', { method: 'note_attributes', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, + shopifyTopic: metricMetadata.shopifyTopic, }); return { anonymousId, sessionId }; } @@ -198,7 +203,9 @@ const getAnonymousIdAndSessionId = async (message, metricMetadata, redisData = n // and for how many stats.increment('shopify_anon_id_resolve', { method: 'database', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, + shopifyTopic: metricMetadata.shopifyTopic, }); } return { anonymousId, sessionId }; @@ -215,14 +222,16 @@ const updateCartItemsInRedis = async (cartToken, newCartItemsHash, metricMetadat stats.increment('shopify_redis_calls', { type: 'set', field: 'itemsHash', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, }); await RedisDB.setVal(`${cartToken}`, value); } catch (e) { logger.debug(`{{SHOPIFY::}} itemsHash set call Failed due redis error ${e}`); stats.increment('shopify_redis_failures', { type: 'set', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, }); } }; From a0ca61bfd4fdb0197e40e39f9d21d49a97d726da Mon Sep 17 00:00:00 2001 From: ItsSudip Date: Mon, 11 Mar 2024 19:35:18 +0530 Subject: [PATCH 017/240] chore: address comments --- .../clevertap/dataDelivery/business.ts | 103 ++++++++++++++---- .../destinations/clevertap/network.ts | 29 +++++ 2 files changed, 113 insertions(+), 19 deletions(-) diff --git a/test/integrations/destinations/clevertap/dataDelivery/business.ts b/test/integrations/destinations/clevertap/dataDelivery/business.ts index edab4ee6d7..d9f83f52f3 100644 --- a/test/integrations/destinations/clevertap/dataDelivery/business.ts +++ b/test/integrations/destinations/clevertap/dataDelivery/business.ts @@ -10,6 +10,18 @@ const headers = { 'fbee74a147828e2932c701d19dc1f2dcfa4ac0048be3aa3a88d427090a59dc1c0fa002f1', 'Content-Type': 'application/json', }; + +const statTags = { + destType: 'CLEVERTAP', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + export const V1BusinessTestScenarion: ProxyV1TestData[] = [ { id: 'clevertap_business_0', @@ -133,16 +145,7 @@ export const V1BusinessTestScenarion: ProxyV1TestData[] = [ statusCode: 401, }, ], - statTags: { - destType: 'CLEVERTAP', - destinationId: 'default-destinationId', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'default-workspaceId', - }, + statTags, }, }, }, @@ -201,16 +204,78 @@ export const V1BusinessTestScenarion: ProxyV1TestData[] = [ statusCode: 400, }, ], - statTags: { - destType: 'CLEVERTAP', - destinationId: 'default-destinationId', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'default-workspaceId', + statTags, + }, + }, + }, + }, + }, + { + id: 'clevertap_business_3', + scenario: 'business', + successCriteria: 'should return 200 status code with success message', + name: 'clevertap', + description: '[business]:: create an user through identify call', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + params, + headers, + JSON: { + d: [ + { + identity: 'testUser1', + type: 'profile', + profileData: { + Name: 'Test User1', + Email: 'test1@testMail.com', + }, + }, + { + evtData: { + name: 1234, + revenue: 4.99, + }, + type: 'event', + identity: 'user123', + }, + { + identity: 'testUser2', + type: 'profile', + profileData: { + Name: 'Test User2', + Email: 'test2@testMail.com', + }, + }, + ], }, + endpoint: 'https://api.clevertap.com/1/upload/test4', + }, + [generateMetadata(123)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + statTags, + status: 400, + message: 'Request failed with status: 200', + response: [ + { + metadata: generateMetadata(123), + error: + '{"status":"partial","processed":2,"unprocessed":[{"status":"fail","code":509,"error":"Event Name is incorrect. ErrorCode: 509 - Event name is mandatory. Skipped record number : 2","record":{"evtData":{"name":1234,"revenue":4.99},"type":"event","identity":"user123"}}]}', + statusCode: 400, + }, + ], }, }, }, diff --git a/test/integrations/destinations/clevertap/network.ts b/test/integrations/destinations/clevertap/network.ts index 57a647e684..9122ba1129 100644 --- a/test/integrations/destinations/clevertap/network.ts +++ b/test/integrations/destinations/clevertap/network.ts @@ -87,6 +87,35 @@ const dataDeliveryMocksData = [ }, httpRes: { data: { status: 'fail', processed: 0, unprocessed: [] }, status: 200 }, }, + { + httpReq: { + url: 'https://api.clevertap.com/1/upload/test4', + method: 'POST', + }, + httpRes: { + data: { + status: 'partial', + processed: 2, + unprocessed: [ + { + status: 'fail', + code: 509, + error: + 'Event Name is incorrect. ErrorCode: 509 - Event name is mandatory. Skipped record number : 2', + record: { + evtData: { + name: 1234, + revenue: 4.99, + }, + type: 'event', + identity: 'user123', + }, + }, + ], + }, + status: 200, + }, + }, ]; const deleteNwData = [ { From fe72a9db9aee53bcab66b21b2f77a344f7ed61e3 Mon Sep 17 00:00:00 2001 From: Abhimanyu Babbar Date: Mon, 11 Mar 2024 23:46:33 +0530 Subject: [PATCH 018/240] chore: added step to raise PR to dedicated enterprise customers in devops on merge to master (#2802) * chore: added step to raise PR to dedicated enterprise customers as well in devops on merge to master * chore: automatic devops pr raise issue fixed --------- Co-authored-by: anshulrudderstack Co-authored-by: anshulrudderstack <144046982+anshulrudderstack@users.noreply.github.com> Co-authored-by: Jayachand --- .../workflows/prepare-for-prod-dt-deploy.yml | 36 +++++++++++++++++ .../workflows/prepare-for-prod-rollback.yml | 40 ++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/.github/workflows/prepare-for-prod-dt-deploy.yml b/.github/workflows/prepare-for-prod-dt-deploy.yml index 2af853f643..a5ca48e3f8 100644 --- a/.github/workflows/prepare-for-prod-dt-deploy.yml +++ b/.github/workflows/prepare-for-prod-dt-deploy.yml @@ -144,3 +144,39 @@ jobs: git push -u origin hosted-transformer-$TAG_NAME gh pr create --fill + + - name: Update helm charts and raise pull request for enterprise customers on dedicated transformers + env: + GITHUB_TOKEN: ${{ secrets.PAT }} + run: | + cd rudder-devops + git checkout -b dedicated-transformer-$TAG_NAME + + cd customer-objects + + declare -a enabled_ut_customers=() + declare -a sub_directories=('enterprise-us' 'enterprise-eu') + + # identify the customers enabled in sub-directories + for directory in "${sub_directories[@]}"; do + for f in "./$directory"/*; do + [[ -f $f ]] || continue + + enabled="$(yq e '.spec.user_transformer.enabled' $f)" + if [ $enabled == "true" ]; then + enabled_ut_customers+=( $f ) + fi + done + done + + # bump up the customers version and repository information + for customer in "${enabled_ut_customers[@]}"; do + yq eval -i ".spec.user_transformer.image.version=\"$TAG_NAME\"" $customer + yq eval -i ".spec.user_transformer.image.repository=\"$TF_IMAGE_REPOSITORY\"" $customer + git add $customer + done + + git commit -m "chore: upgrade dedicated transformers to $TAG_NAME" + git push -u origin dedicated-transformer-$TAG_NAME + + gh pr create --fill diff --git a/.github/workflows/prepare-for-prod-rollback.yml b/.github/workflows/prepare-for-prod-rollback.yml index 9ac144a21e..825720efe1 100644 --- a/.github/workflows/prepare-for-prod-rollback.yml +++ b/.github/workflows/prepare-for-prod-rollback.yml @@ -27,11 +27,14 @@ jobs: git config --global user.name "GitHub Actions" git config --global user.email "noreply@github.com" + - name: Clone Devops Repo + run: | + git clone https://${{secrets.PAT}}@github.com/rudderlabs/rudder-devops.git + - name: Update Helm Charts and Raise Pull Request env: GITHUB_TOKEN: ${{ secrets.PAT }} run: | - git clone https://${{secrets.PAT}}@github.com/rudderlabs/rudder-devops.git cd rudder-devops git checkout -b shared-transformer-rollback-${{ steps.target-version.outputs.tag_name }} @@ -57,3 +60,38 @@ jobs: git push -u origin shared-transformer-rollback-${{ steps.target-version.outputs.tag_name }} gh pr create --fill + + - name: Update helm charts and raise pull request for enterprise customers on dedicated transformers + env: + GITHUB_TOKEN: ${{ secrets.PAT }} + run: | + cd rudder-devops + git checkout -b dedicated-transformer-rollback-${{ steps.target-version.outputs.tag_name }} + + cd customer-objects + + declare -a enabled_ut_customers=() + declare -a sub_directories=('enterprise-us' 'enterprise-eu') + + # identify the customers enabled in sub-directories + for directory in "${sub_directories[@]}"; do + for f in "./$directory"/*; do + [[ -f $f ]] || continue + + enabled="$(yq e '.spec.user_transformer.enabled' $f)" + if [ $enabled == "true" ]; then + enabled_ut_customers+=( $f ) + fi + done + done + + # bump up the customers version and repository information + for customer in "${enabled_ut_customers[@]}"; do + yq eval -i ".spec.user_transformer.image.version=\"${{ steps.target-version.outputs.tag_name }}\"" $customer + git add $customer + done + + git commit -m "chore: rollback dedicated transformers to ${{ steps.target-version.outputs.tag_name }}" + git push -u origin dedicated-transformer-rollback-${{ steps.target-version.outputs.tag_name }} + + gh pr create --fill From 1ca039d64ebb1a18a0fc6b78ed5ee08528ad6b48 Mon Sep 17 00:00:00 2001 From: Gustavo Warmling Teixeira Date: Tue, 12 Mar 2024 01:21:19 -0300 Subject: [PATCH 019/240] feat: add Koala destination (#3122) * Add koala procWorkflow file Basic steps for koala destination * Add koala canonicalNames * Add Koala integration test processor data * Add User-Agent rudderstack header * Add identity and track steps Basic implementation of payload data per event_type * Add messageId to Track call * Include ip information * Update endpoint url profiles -> projects * Update src/cdk/v2/destinations/koala/procWorkflow.yaml Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> * Update src/cdk/v2/destinations/koala/procWorkflow.yaml Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> * Update src/cdk/v2/destinations/koala/procWorkflow.yaml Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> * Add KOALA as a routerTransform * Fix wrong attr assignment * Add rtWorkflow file * Remove batch_mode step * Conside properties data when collecting ko_profile_id and email * Update src/cdk/v2/destinations/koala/procWorkflow.yaml Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> * Update src/cdk/v2/destinations/koala/procWorkflow.yaml Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> * Remove tool-versions, added by mistake * Use context variables * remove event attr from identity call * Update tests data * Update test/integrations/destinations/koala/processor/data.ts Co-authored-by: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> * Update test/integrations/destinations/koala/processor/data.ts Co-authored-by: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> * Update test/integrations/destinations/koala/processor/data.ts Co-authored-by: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> * Update src/cdk/v2/destinations/koala/procWorkflow.yaml Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> * Do not remove email from properties lets keep email in properties --------- Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Co-authored-by: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> --- .../v2/destinations/koala/procWorkflow.yaml | 65 ++++ src/cdk/v2/destinations/koala/rtWorkflow.yaml | 31 ++ src/constants/destinationCanonicalNames.js | 1 + src/features.json | 3 +- .../destinations/koala/processor/data.ts | 319 ++++++++++++++++++ .../destinations/koala/router/data.ts | 200 +++++++++++ 6 files changed, 618 insertions(+), 1 deletion(-) create mode 100644 src/cdk/v2/destinations/koala/procWorkflow.yaml create mode 100644 src/cdk/v2/destinations/koala/rtWorkflow.yaml create mode 100644 test/integrations/destinations/koala/processor/data.ts create mode 100644 test/integrations/destinations/koala/router/data.ts diff --git a/src/cdk/v2/destinations/koala/procWorkflow.yaml b/src/cdk/v2/destinations/koala/procWorkflow.yaml new file mode 100644 index 0000000000..9ec0202b13 --- /dev/null +++ b/src/cdk/v2/destinations/koala/procWorkflow.yaml @@ -0,0 +1,65 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + - name: defaultRequestConfig + path: ../../../../v0/util + +steps: + - name: validateInput + template: | + $.assert(.message.type, "message Type is not present. Aborting message"); + $.assert(.message.type in {{$.EventType.([.IDENTIFY, .TRACK])}}, + "message type " + .message.type + " is not supported"); + $.assertConfig(.destination.Config.publicKey, "publicKey is not present. Aborting message"); + $.context.email = .message.().({{{{$.getGenericPaths("emailOnly")}}}}); + $.context.ko_profile_id = .message.traits.ko_profile_id ?? .message.context.traits.ko_profile_id ?? .message.properties.ko_profile_id; + $.assert($.context.email || $.context.ko_profile_id, "Neither email or ko_profile_id are present on traits. Aborting message"); + - name: setMessageType + template: | + $.context.messageType = .message.type.toLowerCase(); + - name: preparePayloadForIdentify + condition: $.context.messageType === {{$.EventType.IDENTIFY}} + template: | + const traits = .message.traits ?? .message.context.traits ?? {}; + const koTraits = traits{~['ko_profile_id']} + const basePayload = { + email: $.context.email, + profile_id: $.context.ko_profile_id, + identifies: [{ + type: $.context.messageType, + sent_at: .message.().({{{{$.getGenericPaths("timestamp")}}}}), + traits: koTraits + }] + }; + + $.context.payload = basePayload + - name: preparePayloadForTrack + condition: $.context.messageType === {{$.EventType.TRACK}} + template: | + const properties = .message.properties ?? {}; + const koProperties = properties{~['ko_profile_id']} + const basePayload = { + ip: .message.context.ip ?? .message.request_ip, + email: $.context.email, + profile_id: $.context.ko_profile_id, + events: [{ + type: $.context.messageType, + event: .message.event, + message_id: .message.messageId, + sent_at: .message.().({{{{$.getGenericPaths("timestamp")}}}}), + properties: koProperties, + context: .message.context + }] + }; + + $.context.payload = basePayload + - name: buildResponseForProcessTransformation + template: | + const response = $.defaultRequestConfig(); + response.body.JSON = $.context.payload; + response.endpoint = "https://api2.getkoala.com/web/projects/" + .destination.Config.publicKey + "/batch"; + response.headers = { + "content-type": "application/json" + }; + response diff --git a/src/cdk/v2/destinations/koala/rtWorkflow.yaml b/src/cdk/v2/destinations/koala/rtWorkflow.yaml new file mode 100644 index 0000000000..335293b6db --- /dev/null +++ b/src/cdk/v2/destinations/koala/rtWorkflow.yaml @@ -0,0 +1,31 @@ +bindings: + - name: handleRtTfSingleEventError + path: ../../../../v0/util/index + +steps: + - name: validateInput + template: | + $.assert(Array.isArray(^) && ^.length > 0, "Invalid event array") + + - name: transform + externalWorkflow: + path: ./procWorkflow.yaml + loopOverInput: true + + - name: successfulEvents + template: | + $.outputs.transform#idx.output.({ + "batchedRequest": ., + "batched": false, + "destination": ^[idx].destination, + "metadata": ^[idx].metadata[], + "statusCode": 200 + })[] + - name: failedEvents + template: | + $.outputs.transform#idx.error.( + $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) + )[] + - name: finalPayload + template: | + [...$.outputs.failedEvents, ...$.outputs.successfulEvents] diff --git a/src/constants/destinationCanonicalNames.js b/src/constants/destinationCanonicalNames.js index d1b2b24de0..17848e6b94 100644 --- a/src/constants/destinationCanonicalNames.js +++ b/src/constants/destinationCanonicalNames.js @@ -152,6 +152,7 @@ const DestCanonicalNames = { 'the trade desk', ], INTERCOM: ['INTERCOM', 'intercom', 'Intercom'], + koala: ['Koala', 'koala', 'KOALA'], }; module.exports = { DestHandlerMap, DestCanonicalNames }; diff --git a/src/features.json b/src/features.json index 5460111a22..dc52044048 100644 --- a/src/features.json +++ b/src/features.json @@ -66,7 +66,8 @@ "REDDIT": true, "THE_TRADE_DESK": true, "INTERCOM": true, - "NINETAILED": true + "NINETAILED": true, + "KOALA": true }, "regulations": [ "BRAZE", diff --git a/test/integrations/destinations/koala/processor/data.ts b/test/integrations/destinations/koala/processor/data.ts new file mode 100644 index 0000000000..9c1ea97a77 --- /dev/null +++ b/test/integrations/destinations/koala/processor/data.ts @@ -0,0 +1,319 @@ +export const data = [ + { + name: 'koala', + description: 'Sucessful track event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + annonymousId: 'annonymous-uuid', + event: 'User Signed Up', + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + properties: { + email: 'johndoe@somemail.com', + label: 'test', + value: 10, + }, + context: { + network: 'wifi' + }, + originalTimestamp: '2024-01-23T08:35:17.562Z', + sentAt: '2024-01-23T08:35:17.562Z', + request_ip: '192.11.22.33', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + FORM: {}, + JSON_ARRAY: {}, + XML: {}, + JSON: { + ip: '192.11.22.33', + email: 'johndoe@somemail.com', + events: [{ + type: 'track', + event: 'User Signed Up', + sent_at: '2024-01-23T08:35:17.562Z', + message_id: '84e26acc-56a5-4835-8233-591137fca468', + properties: { + email: 'johndoe@somemail.com', + label: 'test', + value: 10, + }, + context: { + network: 'wifi' + }, + }] + }, + }, + endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', + files: {}, + params: {}, + type: 'REST', + version: '1', + method: 'POST', + userId: '', + headers: { + 'content-type': 'application/json', + }, + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'koala', + description: 'Successful identify event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + type: 'identify', + traits: { + FirstName: 'John', + LastName: 'Doe', + address: { + city: 'San Francisco', + state: 'CA', + postalCode: '94107', + }, + email: 'johndoe@somemail.com', + ko_profile_id: 'xxxx-2222-xxxx-xxxx' + }, + originalTimestamp: '2024-01-23T08:35:17.342Z', + sentAt: '2024-01-23T08:35:35.234Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + FORM: {}, + JSON_ARRAY: {}, + XML: {}, + JSON: { + email: 'johndoe@somemail.com', + profile_id: 'xxxx-2222-xxxx-xxxx', + identifies: [{ + type: 'identify', + sent_at: '2024-01-23T08:35:17.342Z', + traits: { + FirstName: 'John', + LastName: 'Doe', + address: { + city: 'San Francisco', + state: 'CA', + postalCode: '94107', + }, + email: 'johndoe@somemail.com', + }, + }], + }, + }, + endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', + files: {}, + params: {}, + type: 'REST', + version: '1', + method: 'POST', + userId: '', + headers: { + 'content-type': 'application/json', + }, + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'koala', + description: 'Missing required email or ko_profile_id fields in traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + type: 'track', + traits: { + name: 'John Doe', + }, + originalTimestamp: '2024-01-23T08:35:17.342Z', + sentAt: '2024-01-23T08:35:35.234Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'Neither email or ko_profile_id are present on traits. Aborting message: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Neither email or ko_profile_id are present on traits. Aborting message', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'KOALA', + module: 'destination', + implementation: 'cdkV2', + destinationId: 'destId', + workspaceId: 'wspId', + feature: 'processor', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'koala', + description: 'Invalid message type page', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + type: 'page', + groupId: 'group-uuid', + originalTimestamp: '2024-01-23T08:35:17.342Z', + sentAt: '2024-01-23T08:35:35.234Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'message type page is not supported: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message type page is not supported', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'KOALA', + module: 'destination', + implementation: 'cdkV2', + destinationId: 'destId', + workspaceId: 'wspId', + feature: 'processor', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/koala/router/data.ts b/test/integrations/destinations/koala/router/data.ts new file mode 100644 index 0000000000..fb0db3e3fb --- /dev/null +++ b/test/integrations/destinations/koala/router/data.ts @@ -0,0 +1,200 @@ +export const data = [ + { + name: 'koala', + description: 'Router batch request', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + annonymousId: 'annonymous-uuid', + event: 'User Signed Up', + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + traits: { + email: 'johndoe@somemail.com' + }, + properties: { + label: 'test', + value: 10, + }, + context: { + network: 'wifi' + }, + originalTimestamp: '2024-01-23T08:35:17.562Z', + sentAt: '2024-01-23T08:35:17.562Z', + request_ip: '192.11.22.33', + }, + metadata: { + jobId: 1, + userId: 'u1', + destinationId: 'destId', + workspaceId: 'wspId' + } + }, + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + annonymousId: 'annonymous-uuid', + event: 'User Deleted account', + type: 'track', + messageId: '8bc79b03-2a5c-4615-b2da-54c0aaaaaae8', + traits: { + ko_profile_id: '123456' + }, + properties: { + attr1: 'foo', + attr2: 'bar' + }, + context: { + network: 'wifi' + }, + originalTimestamp: '2024-01-23T08:35:17.562Z', + sentAt: '2024-01-23T08:35:17.562Z', + request_ip: '192.11.55.1', + }, + metadata: { + jobId: 2, + userId: 'u1', + destinationId: 'destId', + workspaceId: 'wspId' + } + }, + ], + destType: 'koala', + }, + method: 'POST' + } + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + body: { + FORM: {}, + JSON_ARRAY: {}, + XML: {}, + JSON: { + ip: '192.11.22.33', + email: 'johndoe@somemail.com', + events: [{ + type: 'track', + event: 'User Signed Up', + sent_at: '2024-01-23T08:35:17.562Z', + message_id: '84e26acc-56a5-4835-8233-591137fca468', + properties: { + label: 'test', + value: 10, + }, + context: { + network: 'wifi' + }, + }] + }, + }, + endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', + files: {}, + params: {}, + type: 'REST', + version: '1', + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + }, + batched: false, + metadata: [{ jobId: 1, userId: 'u1', workspaceId: 'wspId', destinationId: 'destId' }], + statusCode: 200, + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + }, + { + batchedRequest: { + body: { + FORM: {}, + JSON_ARRAY: {}, + XML: {}, + JSON: { + ip: '192.11.55.1', + profile_id: '123456', + events: [{ + type: 'track', + event: 'User Deleted account', + sent_at: '2024-01-23T08:35:17.562Z', + message_id: '8bc79b03-2a5c-4615-b2da-54c0aaaaaae8', + properties: { + attr1: 'foo', + attr2: 'bar' + }, + context: { + network: 'wifi' + }, + }] + }, + }, + endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', + files: {}, + params: {}, + type: 'REST', + version: '1', + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + }, + batched: false, + metadata: [{ jobId: 2, userId: 'u1', workspaceId: 'wspId', destinationId: 'destId' }], + statusCode: 200, + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + } + ] + } + } + } + } +] From 69afa97396b1933fc11ae962dc93c0fe30dd1f03 Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Tue, 12 Mar 2024 10:23:17 +0530 Subject: [PATCH 020/240] chore: add v1 proxy tests and mocks for Marketo (#3116) --- test/integrations/common/network.ts | 11 + .../marketo/dataDelivery/business.ts | 352 ++++++ .../destinations/marketo/dataDelivery/data.ts | 7 +- .../marketo/dataDelivery/other.ts | 266 ++++ .../destinations/marketo/network.ts | 1100 +++++++++++++++-- 5 files changed, 1663 insertions(+), 73 deletions(-) create mode 100644 test/integrations/destinations/marketo/dataDelivery/business.ts create mode 100644 test/integrations/destinations/marketo/dataDelivery/other.ts diff --git a/test/integrations/common/network.ts b/test/integrations/common/network.ts index 8b0ed16c72..a6ab202a4e 100644 --- a/test/integrations/common/network.ts +++ b/test/integrations/common/network.ts @@ -81,4 +81,15 @@ export const networkCallsData = [ status: 429, }, }, + { + description: 'Mock response depicting DNS lookup failure error', + httpReq: { + method: 'post', + url: 'https://random_test_url/dns_lookup_failure', + }, + httpRes: { + data: {}, + status: 400, + }, + }, ]; diff --git a/test/integrations/destinations/marketo/dataDelivery/business.ts b/test/integrations/destinations/marketo/dataDelivery/business.ts new file mode 100644 index 0000000000..ca4e05afa9 --- /dev/null +++ b/test/integrations/destinations/marketo/dataDelivery/business.ts @@ -0,0 +1,352 @@ +import { ProxyMetdata } from '../../../../../src/types'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const statTags = { + aborted: { + destType: 'MARKETO', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, + retryable: { + destType: 'MARKETO', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, + throttled: { + destType: 'MARKETO', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'throttled', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, +}; + +export const proxyMetdata: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; + +export const reqMetadataArray = [proxyMetdata]; +const params = { + destination: 'marketo', +}; + +const commonRequestParameters = { + JSON: { + action: 'createOrUpdate', + input: [ + { + City: 'Tokyo', + Country: 'JP', + Email: 'gabi29@gmail.com', + PostalCode: '100-0001', + Title: 'Owner', + id: 1328328, + userId: 'gabi_userId_45', + }, + ], + lookupField: 'id', + }, + params, +}; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'marketo_v1_scenario_1', + name: 'marketo', + description: '[Proxy v1 API] :: Test for a successful update request', + successCriteria: 'Should return a 200 status code with status updated and record id', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer test_token_1', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test1', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + error: + '{"requestId":"664#17dae8c3d48","result":[{"id":1328328,"status":"updated"}],"success":true}', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_2', + name: 'marketo', + description: '[Proxy v1 API] :: Test for Access token invalid scenario', + successCriteria: 'Should return a 500 status code with message Access token invalid', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer test_token_2', + 'Content-Type': 'application/json', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test2', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + statTags: statTags.retryable, + message: + 'Request Failed for marketo, Access token invalid (Retryable).during Marketo Response Handling', + response: [ + { + error: + '{"requestId":"a61c#17daea5968a","success":false,"errors":[{"code":"601","message":"Access token invalid"}]}', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_3', + name: 'marketo', + description: '[Proxy v1 API] :: Test for Requested resource not found scenario', + successCriteria: 'Should return a 400 status code with message Requested resource not found', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer test_token_3', + 'Content-Type': 'application/json', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test3', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: + 'Request Failed for marketo, Requested resource not found (Aborted).during Marketo Response Handling', + response: [ + { + error: + '{"requestId":"a61c#17daea5968a","success":false,"errors":[{"code":"610","message":"Requested resource not found"}]}', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_4', + name: 'marketo', + description: '[Proxy v1 API] :: Test for Unknown error with empty response', + successCriteria: 'Should return a 500 status code with empty response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer test_token_4', + 'Content-Type': 'application/json', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test4', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + statTags: statTags.retryable, + message: 'Request failed with status: 500', + response: [ + { + error: '""', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_5', + name: 'marketo', + description: '[Proxy v1 API] :: Test for missing content type header scenario', + successCriteria: 'Should return a 612 status code with Invalid Content Type ', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'invalid', + 'User-Agent': 'RudderLabs', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test_invalid_header', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: + 'Request Failed for marketo, Invalid Content Type (Aborted).during Marketo Response Handling', + response: [ + { + error: + '{"success":false,"errors":[{"code":"612","message":"Invalid Content Type"}]}', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_6', + name: 'marketo', + description: '[Proxy v1 API] :: Test for a passed field exceeding max length', + successCriteria: 'Should return a 1077 status code with Value for field exceeds max length', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test_exceeded_length', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: 'Request failed with status: 400', + response: [ + { + error: + '{"success":false,"errors":[{"code":"1077","message":"Value for field exceeds max length"}]}', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/marketo/dataDelivery/data.ts b/test/integrations/destinations/marketo/dataDelivery/data.ts index 47dd8e9236..db379c9e95 100644 --- a/test/integrations/destinations/marketo/dataDelivery/data.ts +++ b/test/integrations/destinations/marketo/dataDelivery/data.ts @@ -1,4 +1,7 @@ -export const data = [ +import { testScenariosForV1API } from './business'; +import { otheMarketoScenariosV1 } from './other'; + +const legacyTests = [ { name: 'marketo', description: 'Test 0', @@ -488,3 +491,5 @@ export const data = [ }, }, ]; + +export const data = [...legacyTests, ...testScenariosForV1API, ...otheMarketoScenariosV1]; diff --git a/test/integrations/destinations/marketo/dataDelivery/other.ts b/test/integrations/destinations/marketo/dataDelivery/other.ts new file mode 100644 index 0000000000..5d4e3b1f17 --- /dev/null +++ b/test/integrations/destinations/marketo/dataDelivery/other.ts @@ -0,0 +1,266 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const statTags = { + aborted: { + destType: 'MARKETO', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', + }, + retryable: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'MARKETO', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, +}; + +const metadata = { + jobId: 1, + secret: { + accessToken: 'default-accessToken', + }, + attemptNum: 1, + userId: 'default-userId', + sourceId: 'default-sourceId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + dontBatch: false, +}; + +export const otheMarketoScenariosV1: ProxyV1TestData[] = [ + { + id: 'marketo_v1_other_scenario_1', + name: 'marketo', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 503 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 503, + metadata, + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 503', + status: 503, + }, + }, + }, + }, + }, + { + id: 'marketo_v1_other_scenario_2', + name: 'marketo', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata, + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, + { + id: 'marketo_v1_other_scenario_3', + name: 'marketo', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout"', + statusCode: 504, + metadata, + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 504', + status: 504, + }, + }, + }, + }, + }, + { + id: 'marketo_v1_other_scenario_4', + name: 'marketo', + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with empty error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_response', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata, + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, + { + id: 'marketo_v1_other_scenario_5', + name: 'marketo', + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with empty error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata, + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_6', + name: 'marketo', + description: '[Proxy v1 API] :: Test for DNS lookup failed scenario', + successCriteria: 'Should return a 400 status code with empty response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/dns_lookup_failure', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: 'Request failed with status: 400', + response: [ + { + error: '{}', + metadata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/marketo/network.ts b/test/integrations/destinations/marketo/network.ts index 9c28a9aef1..1606e78c51 100644 --- a/test/integrations/destinations/marketo/network.ts +++ b/test/integrations/destinations/marketo/network.ts @@ -1,20 +1,26 @@ -export const networkCallsData = [ +const userObject = { + City: 'Tokyo', + Country: 'JP', + Email: 'gabi29@gmail.com', + PostalCode: '100-0001', + Title: 'Owner', + id: 1328328, + userId: 'gabi_userId_45', +}; + +const headerObject = { + Authorization: 'Bearer test_token_2', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', +}; + +const tfProxyMocksData = [ { httpReq: { url: 'https://mktId.mktorest.com/rest/v1/leads.json/test1', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, headers: { @@ -44,24 +50,10 @@ export const networkCallsData = [ url: 'https://mktId.mktorest.com/rest/v1/leads.json/test2', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, - headers: { - Authorization: 'Bearer test_token_2', - 'Content-Type': 'application/json', - 'User-Agent': 'RudderLabs', - }, + headers: headerObject, method: 'POST', }, httpRes: { @@ -84,17 +76,7 @@ export const networkCallsData = [ url: 'https://mktId.mktorest.com/rest/v1/leads.json/test3', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, headers: { @@ -124,17 +106,7 @@ export const networkCallsData = [ url: 'https://mktId.mktorest.com/rest/v1/leads.json/test4', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, headers: { @@ -151,17 +123,7 @@ export const networkCallsData = [ url: 'https://mktId.mktorest.com/rest/v1/leads.json/test5', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, headers: { @@ -178,17 +140,7 @@ export const networkCallsData = [ url: 'https://mktId.mktorest.com/rest/v1/leads.json/test6', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, headers: { @@ -970,3 +922,1007 @@ export const networkCallsData = [ }, }, ]; + +const businessMockData = [ + { + description: 'Mock response for a successful update request', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test1', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer test_token_1', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + requestId: '664#17dae8c3d48', + result: [ + { + id: 1328328, + status: 'updated', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed update request due to invalid access token', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test2', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: headerObject, + method: 'POST', + }, + httpRes: { + data: { + requestId: 'a61c#17daea5968a', + success: false, + errors: [ + { + code: '601', + message: 'Access token invalid', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed update request due to requested resource not found', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test3', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer test_token_3', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + requestId: 'a61c#17daea5968a', + success: false, + errors: [ + { + code: '610', + message: 'Requested resource not found', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful create/update request', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test4', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer test_token_4', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: {}, + }, + { + description: 'Mock response for a successful create/update request', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test5', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer test_token_5', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: '', + }, + { + description: 'Mock response for a failed create/update request due to DNS lookup failure', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test6', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + code: '[ENOTFOUND] :: DNS lookup failed', + status: 400, + }, + }, + { + description: + 'Mock response for a failed create/update request due to unhandled exception in proxy request', + httpReq: { + url: 'https://unhandled_exception_in_proxy_req.mktorest.com/rest/v1/leads.json', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer access_token_success', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + requestId: '142e4#1835b117b76', + success: false, + errors: [ + { + code: 'random_marketo_code', + message: 'problem', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful access token request', + httpReq: { + url: 'https://marketo_acct_id_success.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed access token request due to expired token', + httpReq: { + url: 'https://marketo_acct_id_token_failure.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_expired', + expires_in: 0, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request', + httpReq: { + url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/leads.json', + method: 'get', + }, + httpRes: { + data: { + requestId: '7ab2#17672a46a99', + result: [ + { + id: 4, + status: 'created', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request with filterType=email with no results', + httpReq: { + url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/leads.json?filterType=email&filterValues=arnab.compsc%40gmail.com', + method: 'GET', + }, + httpRes: { + data: { + requestId: '107#17672aeadba', + result: [], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request with filterType=userId with results', + httpReq: { + url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/leads.json?filterType=userId&filterValues=test-user-6j55yr', + method: 'GET', + }, + httpRes: { + data: { + requestId: '12093#17672aeaee6', + result: [ + { + createdAt: '2020-12-17T21:39:07Z', + email: null, + firstName: null, + id: 4, + lastName: null, + updatedAt: '2020-12-17T21:39:07Z', + userId: 'test-user-6j55yr', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed access token request due to expired token', + httpReq: { + url: 'https://marketo_acct_id_failed.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + success: false, + errors: [ + { + code: '601', + message: 'Access Token Expired', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful access token request', + httpReq: { + url: 'https://munchkinId.mktorest.com/identity/oauth/token?client_id=b&client_secret=clientSecret&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_acess', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed access token request due to invalid client id', + httpReq: { + url: 'https://munchkinId.mktorest.com/identity/oauth/token?client_id=wrongClientId&client_secret=clientSecret&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request to get list of fields', + httpReq: { + url: 'https://munchkinId.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bulk request with queued status', + httpReq: { + url: 'https://munchkinId.mktorest.com/bulk/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '5bdd#17fd1ff88cd', + result: [ + { + batchId: 2977, + importId: '2977', + status: 'Queued', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for bulk request', + httpReq: { + url: 'https://a.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for bulk request for throttling error', + httpReq: { + url: 'https://a.mktorest.com/identity/oauth/token?client_id=b&client_secret=forThrottle&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request to get list of fields', + httpReq: { + url: 'https://a.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a succesful oauth token request', + httpReq: { + url: 'https://testMunchkin4.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed bulk request with 400 error', + httpReq: { + url: 'https://testMunchkin4.mktorest.com/bulk/v1/leads/batch/1234.json', + method: 'GET', + }, + httpRes: { + data: { + errors: [ + { + message: 'Any 400 error', + code: 1000, + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful oauth token request', + httpReq: { + url: 'https://testMunchkin3.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful oauth token request', + httpReq: { + url: 'https://testMunchkin500.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed bulk request with 500 error', + httpReq: { + url: 'https://testMunchkin500.mktorest.com/bulk/v1/leads/batch/1234.json', + method: 'GET', + }, + httpRes: { + data: { + errors: [ + { + message: 'Any 500 error', + code: 502, + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bulk request with warnings', + httpReq: { + url: 'https://a.mktorest.com/bulk/v1/leads/batch/12345/warnings.json', + method: 'GET', + }, + httpRes: { + data: 'data \n data', + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bulk request with failures', + httpReq: { + url: 'https://a.mktorest.com/bulk/v1/leads/batch/12345/failures.json', + method: 'GET', + }, + httpRes: { + data: 'data \n data', + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bearer token request', + httpReq: { + url: 'https://testMunchkin1.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request to get list of fields', + httpReq: { + url: 'https://testMunchkin1.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed bulk request with 603 error code', + httpReq: { + url: 'https://testMunchkin1.mktorest.com/bulk/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + success: false, + errors: [ + { + code: 603, + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bearer token request', + httpReq: { + url: 'https://testMunchkin2.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request to get list of fields', + httpReq: { + url: 'https://testMunchkin2.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'Email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed leads query request with pending import error', + httpReq: { + url: 'https://testMunchkin2.mktorest.com/bulk/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + success: false, + errors: [ + { + message: 'There are 10 imports currently being processed. Please try again later', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a succesful leads query request', + httpReq: { + url: 'https://testMunchkin3.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'Email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed leads query request with empty file error', + httpReq: { + url: 'https://testMunchkin3.mktorest.com/bulk/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + success: false, + errors: [ + { + message: 'Empty file', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful leads query request', + httpReq: { + url: 'https://testMunchkin4.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'Email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed leads query request with any other error', + httpReq: { + url: 'https://testMunchkin4.mktorest.com/bulk/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + success: false, + errors: [ + { + message: 'Any other error', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a succesful bearer token request', + httpReq: { + url: 'https://valid_account_broken_event.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response for a successful get request with filterType=email and filterValues specified with no results', + httpReq: { + url: 'https://valid_account_broken_event.mktorest.com/rest/v1/leads.json?filterType=email&filterValues=0c7b8b80-9c43-4f8e-b2d2-5e2448a25040@j.mail', + method: 'GET', + }, + httpRes: { + data: { + requestId: '12093#17672aeaee6', + result: [], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed get request due to missing lookup field', + httpReq: { + url: 'https://valid_account_broken_event.mktorest.com/rest/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '142e4#1835b117b76', + success: false, + errors: [ + { + code: '1006', + message: "Lookup field 'userId' not found", + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bearer token request', + httpReq: { + url: 'https://unhandled_status_code.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response for a successful get request with filterType=email and filterValues specified with no results', + httpReq: { + url: 'https://unhandled_status_code.mktorest.com/rest/v1/leads.json?filterType=email&filterValues=0c7b8b80-9c43-4f8e-b2d2-5e2448a25040@j.mail', + method: 'GET', + }, + httpRes: { + data: { + requestId: '12093#17672aeaee6', + result: [], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed get request due to random marketo error code', + httpReq: { + url: 'https://unhandled_status_code.mktorest.com/rest/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '142e4#1835b117b76', + success: false, + errors: [ + { + code: 'random_marketo_code', + message: 'some other problem', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bearer token request', + httpReq: { + url: 'https://successful_identify_transformation.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response for a successful get request with no filterType and filterValues specified', + httpReq: { + url: 'https://successful_identify_transformation.mktorest.com/rest/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7ab2#17672a46a99', + result: [ + { + id: 4, + status: 'created', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response for a successful get request with filterType=email and filterValues specified with results', + httpReq: { + url: 'https://successful_identify_transformation.mktorest.com/rest/v1/leads.json?filterType=email&filterValues=0c7b8b80-9c43-4f8e-b2d2-5e2448a25040@j.mail', + method: 'GET', + }, + httpRes: { + data: { + requestId: '12093#17672aeaee6', + result: [ + { + createdAt: '2022-09-17T21:39:07Z', + email: '0c7b8b80-9c43-4f8e-b2d2-5e2448a25040@j.mail', + firstName: 'random_first', + id: 4, + lastName: 'random_last', + updatedAt: '2022-09-20T21:48:07Z', + userId: 'test-user-957ue', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed lead request due to invalid header', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test_invalid_header', + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'invalid', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + success: false, + errors: [ + { + code: '612', + message: 'Invalid Content Type', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed lead request due to length exceeded', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test_exceeded_length', + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + success: false, + errors: [ + { + code: '1077', + message: 'Value for field exceeds max length', + }, + ], + }, + status: 400, + statusText: 'OK', + }, + }, +]; + +export const networkCallsData = [...businessMockData, ...tfProxyMocksData]; From 25b042cd9e47b6eb3cb5cc9f15d27f1a2d9605cc Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Tue, 12 Mar 2024 10:23:33 +0530 Subject: [PATCH 021/240] chore: add v1 proxy tests for MSL (#3130) --- .../dataDelivery/business.ts | 255 ++++++++++++++++++ .../marketo_static_list/dataDelivery/data.ts | 73 +---- .../marketo_static_list/dataDelivery/other.ts | 194 +++++++++++++ .../marketo_static_list/network.ts | 63 ----- 4 files changed, 458 insertions(+), 127 deletions(-) create mode 100644 test/integrations/destinations/marketo_static_list/dataDelivery/business.ts create mode 100644 test/integrations/destinations/marketo_static_list/dataDelivery/other.ts diff --git a/test/integrations/destinations/marketo_static_list/dataDelivery/business.ts b/test/integrations/destinations/marketo_static_list/dataDelivery/business.ts new file mode 100644 index 0000000000..08be877ba8 --- /dev/null +++ b/test/integrations/destinations/marketo_static_list/dataDelivery/business.ts @@ -0,0 +1,255 @@ +import { ProxyMetdata } from '../../../../../src/types'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +export const statTags = { + aborted: { + destType: 'MARKETO_STATIC_LIST', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, + retryable: { + destType: 'MARKETO_STATIC_LIST', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', + }, + throttled: { + destType: 'MARKETO_STATIC_LIST', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'throttled', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, +}; + +export const proxyMetdata: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + secret: {}, + dontBatch: false, +}; + +export const reqMetadataArray = [proxyMetdata]; +const params = { + destination: 'marketo_static_list', +}; + +const commonRequestParameters = { + params, + userId: '', + body: { + FORM: {}, + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + }, + files: {}, +}; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'msl_v1_scenario_1', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Test for a partial successful request with multiple ids', + successCriteria: 'Should return a 200 status code with respective status for each record id', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer Incorrect_token', + 'Content-Type': 'application/json', + }, + endpoint: + 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=110&id=111&id=112', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + error: + '{"requestId":"b6d1#18a8d2c10e7","result":[{"id":110,"status":"skipped","reasons":[{"code":"1015","message":"Lead not in list"}]},{"id":111,"status":"removed"},{"id":112,"status":"removed"}],"success":true}', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'msl_v1_scenario_2', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Test for Access token invalid scenario', + successCriteria: 'Should return a 500 status code with message Access token invalid', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer Incorrect_token', + 'Content-Type': 'application/json', + }, + endpoint: + 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=1&id=2&id=3', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + statTags: statTags.retryable, + message: + 'Request Failed for Marketo Static List, Access token invalid (Retryable).during Marketo Static List Response Handling', + response: [ + { + error: + '{"requestId":"68d8#1846058ee27","success":false,"errors":[{"code":"601","message":"Access token invalid"}]}', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + }, + }, + }, + }, + }, + { + id: 'msl_v1_scenario_3', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Test for a complete successful request with multiple ids', + successCriteria: + 'Should return a 200 status code with respective added status for each record id', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer token', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + endpoint: + 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=1&id=2', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + error: + '{"requestId":"12d3c#1846057dce2","result":[{"id":1,"status":"added"},{"id":2,"status":"added"}],"success":true}', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'msl_v1_scenario_4', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Test for DNS lookup failed scenario', + successCriteria: 'Should return a 400 status code with empty response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test6', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + statTags: statTags.retryable, + message: 'Request failed with status: 500', + response: [ + { + error: '""', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/marketo_static_list/dataDelivery/data.ts b/test/integrations/destinations/marketo_static_list/dataDelivery/data.ts index f0275e329e..c0398f6d2b 100644 --- a/test/integrations/destinations/marketo_static_list/dataDelivery/data.ts +++ b/test/integrations/destinations/marketo_static_list/dataDelivery/data.ts @@ -1,4 +1,7 @@ -export const data = [ +import { testScenariosForV1API } from './business'; +import { otherScenariosV1 } from './other'; + +const legacyTests = [ { name: 'marketo_static_list', description: 'Test 0', @@ -158,21 +161,7 @@ export const data = [ }, body: { FORM: {}, - JSON: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], - lookupField: 'id', - }, + JSON: {}, JSON_ARRAY: {}, XML: {}, }, @@ -234,30 +223,7 @@ export const data = [ params: {}, body: { FORM: {}, - JSON: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - { - City: 'Tokyo', - Country: 'JP', - Email: 'b@s.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328329, - userId: 'ben_userId_45', - }, - ], - lookupField: 'id', - }, + JSON: {}, JSON_ARRAY: {}, XML: {}, }, @@ -311,30 +277,7 @@ export const data = [ params: {}, body: { FORM: {}, - JSON: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - { - City: 'Tokyo', - Country: 'JP', - Email: 'b@s.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328329, - userId: 'ben_userId_45', - }, - ], - lookupField: 'id', - }, + JSON: {}, JSON_ARRAY: {}, XML: {}, }, @@ -373,3 +316,5 @@ export const data = [ }, }, ]; + +export const data = [...legacyTests, ...testScenariosForV1API, ...otherScenariosV1]; diff --git a/test/integrations/destinations/marketo_static_list/dataDelivery/other.ts b/test/integrations/destinations/marketo_static_list/dataDelivery/other.ts new file mode 100644 index 0000000000..b1f3403fa6 --- /dev/null +++ b/test/integrations/destinations/marketo_static_list/dataDelivery/other.ts @@ -0,0 +1,194 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload, generateMetadata } from '../../../testUtils'; +import { reqMetadataArray, statTags } from './business'; + +export const otherScenariosV1: ProxyV1TestData[] = [ + { + id: 'marketo_static_list_v1_other_scenario_1', + name: 'marketo_static_list', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 503, + metadata: generateMetadata(1), + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 503', + status: 503, + }, + }, + }, + }, + }, + { + id: 'marketo_static_list_v1_other_scenario_2', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, + { + id: 'marketo_static_list_v1_other_scenario_3', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout"', + statusCode: 504, + metadata: generateMetadata(1), + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 504', + status: 504, + }, + }, + }, + }, + }, + { + id: 'marketo_static_list_v1_other_scenario_4', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_response', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, + { + id: 'marketo_static_list_v1_other_scenario_5', + name: 'marketo_static_list', + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/marketo_static_list/network.ts b/test/integrations/destinations/marketo_static_list/network.ts index 5c13273859..f165291c15 100644 --- a/test/integrations/destinations/marketo_static_list/network.ts +++ b/test/integrations/destinations/marketo_static_list/network.ts @@ -46,21 +46,6 @@ const deliveryCallsData = [ { httpReq: { url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=1&id=2', - data: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], - lookupField: 'id', - }, params: { destination: 'marketo_static_list' }, headers: { Authorization: 'Bearer token', @@ -84,30 +69,6 @@ const deliveryCallsData = [ { httpReq: { url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=3&id=4', - data: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - { - City: 'Tokyo', - Country: 'JP', - Email: 'b@s.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328329, - userId: 'ben_userId_45', - }, - ], - lookupField: 'id', - }, params: {}, headers: { Authorization: 'Bearer token', @@ -131,30 +92,6 @@ const deliveryCallsData = [ { httpReq: { url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=5&id=6', - data: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - { - City: 'Tokyo', - Country: 'JP', - Email: 'b@s.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328329, - userId: 'ben_userId_45', - }, - ], - lookupField: 'id', - }, params: {}, headers: { Authorization: 'Bearer token', From 1ef20d66f87c296b4efd3c6d7129cde87aca9af2 Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Tue, 12 Mar 2024 10:33:07 +0530 Subject: [PATCH 022/240] chore: upgrade node version to v18.19.1 to resolve synk vulnerabilities (#3154) --- .nvmrc | 2 +- Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.nvmrc b/.nvmrc index a9d087399d..3c5535cf60 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.19.0 +18.19.1 diff --git a/Dockerfile b/Dockerfile index 6bd03c9515..8cd4005a7b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1.4 -FROM node:18.19.0-alpine3.18 AS base +FROM node:18.19.1-alpine3.18 AS base ENV HUSKY 0 RUN apk update From 916aaecb1939160620d5fd3c4c0c0e33f2a371b2 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Tue, 12 Mar 2024 12:43:15 +0530 Subject: [PATCH 023/240] feat: use dontBatch directive in algolia (#3169) * feat: algolia v1 proxy --- .../v2/destinations/algolia/rtWorkflow.yaml | 8 +- src/v1/destinations/algolia/networkHandler.js | 84 + .../destinations/algolia/router/data.ts | 2402 +++++++++++++++++ 3 files changed, 2492 insertions(+), 2 deletions(-) create mode 100644 src/v1/destinations/algolia/networkHandler.js diff --git a/src/cdk/v2/destinations/algolia/rtWorkflow.yaml b/src/cdk/v2/destinations/algolia/rtWorkflow.yaml index 758a71bf5b..f5442f3209 100644 --- a/src/cdk/v2/destinations/algolia/rtWorkflow.yaml +++ b/src/cdk/v2/destinations/algolia/rtWorkflow.yaml @@ -2,7 +2,6 @@ bindings: - path: ../../../../v0/destinations/algolia/config - name: handleRtTfSingleEventError path: ../../../../v0/util/index - steps: - name: validateInput template: | @@ -28,10 +27,14 @@ steps: $.outputs.transform#idx.error.( $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) )[] + - name: batchSuccessfulEvents description: Batches the successfulEvents template: | - let batches = $.chunk($.outputs.successfulEvents, $.MAX_BATCH_SIZE); + const dontBatchTrueEvents = $.outputs.successfulEvents{.metadata.dontBatch}[]; + const dontBatchFalseEvents = $.outputs.successfulEvents{!.metadata.dontBatch}[]; + + let batches = [...$.chunk(dontBatchFalseEvents, $.MAX_BATCH_SIZE), ...$.chunk(dontBatchTrueEvents, 1)]; batches@batch.({ "batchedRequest": { "body": { @@ -56,6 +59,7 @@ steps: "statusCode": 200, "destination": batch[0].destination })[]; + - name: finalPayload template: | [...$.outputs.failedEvents, ...$.outputs.batchSuccessfulEvents] diff --git a/src/v1/destinations/algolia/networkHandler.js b/src/v1/destinations/algolia/networkHandler.js new file mode 100644 index 0000000000..d953251050 --- /dev/null +++ b/src/v1/destinations/algolia/networkHandler.js @@ -0,0 +1,84 @@ +/* eslint-disable no-restricted-syntax */ +const { TransformerProxyError } = require('../../../v0/util/errorTypes'); +const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); +const { isHttpStatusSuccess, getAuthErrCategoryFromStCode } = require('../../../v0/util/index'); + +const { + processAxiosResponse, + getDynamicErrorType, +} = require('../../../adapters/utils/networkUtils'); +const tags = require('../../../v0/util/tags'); + +const responseHandler = (responseParams) => { + const { destinationResponse, rudderJobMetadata } = responseParams; + const message = `[ALGOLIA Response V1 Handler] - Request Processed Successfully`; + const responseWithIndividualEvents = []; + // response: + // {status: 200, message: 'OK'} + // {response:'[ENOTFOUND] :: DNS lookup failed', status: 400} + // destinationResponse = { + // response: {"status": 422, "message": "EventType must be one of \"click\", \"conversion\" or \"view\""}, status: 422 + // } + const { response, status } = destinationResponse; + + if (isHttpStatusSuccess(status)) { + for (const mData of rudderJobMetadata) { + const proxyOutputObj = { + statusCode: 200, + metadata: mData, + error: 'success', + }; + responseWithIndividualEvents.push(proxyOutputObj); + } + + return { + status, + message, + destinationResponse, + response: responseWithIndividualEvents, + }; + } + + // in case of non 2xx status sending 500 for every event, populate response and update dontBatch to true + const errorMessage = response?.error?.message || response?.message || 'unknown error format'; + let serverStatus = 400; + for (const metadata of rudderJobMetadata) { + // handling case if dontBatch is true, and again we got invalid from destination + if (metadata.dontBatch && status === 422) { + responseWithIndividualEvents.push({ + statusCode: 400, + metadata, + error: errorMessage, + }); + } else { + serverStatus = 500; + metadata.dontBatch = true; + responseWithIndividualEvents.push({ + statusCode: 500, + metadata, + error: errorMessage, + }); + } + } + + // sending back 500 for retry + throw new TransformerProxyError( + `ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation`, + serverStatus, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + getAuthErrCategoryFromStCode(status), + responseWithIndividualEvents, + ); +}; + +function networkHandler() { + this.prepareProxy = prepareProxyRequest; + this.proxy = proxyRequest; + this.processAxiosResponse = processAxiosResponse; + this.responseHandler = responseHandler; +} + +module.exports = { networkHandler }; diff --git a/test/integrations/destinations/algolia/router/data.ts b/test/integrations/destinations/algolia/router/data.ts index 65c74342dc..dca899693e 100644 --- a/test/integrations/destinations/algolia/router/data.ts +++ b/test/integrations/destinations/algolia/router/data.ts @@ -2220,4 +2220,2406 @@ export const data = [ }, }, }, + { + name: 'algolia', + description: 'dontBatch true for all', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + ], + destType: 'algolia', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + }, + }, + { + name: 'algolia', + description: 'dontBatch Partial true for events in a batch', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 5, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + ], + destType: 'algolia', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 5, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + }, + }, + { + name: 'algolia', + description: 'dontBatch false for all events, all events are batched in 1', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 5, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + ], + destType: 'algolia', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 5, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + }, + }, ]; From 541b596e472a242a454a438e487a8712adace5de Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Tue, 12 Mar 2024 13:06:32 +0530 Subject: [PATCH 024/240] chore: garl proxy test refactor (#3073) * chore: garl proxy test refactor * chore: code review changes --- .../dataDelivery/business.ts | 376 ++++++++++++++++++ .../dataDelivery/data.ts | 264 +----------- 2 files changed, 379 insertions(+), 261 deletions(-) create mode 100644 test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/business.ts diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/business.ts b/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/business.ts new file mode 100644 index 0000000000..2aefb18fdd --- /dev/null +++ b/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/business.ts @@ -0,0 +1,376 @@ +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; + +const commonHeaders = { + Authorization: 'Bearer dummy-access', + 'Content-Type': 'application/json', + 'developer-token': 'dummy-dev-token', +}; + +const commonParams = { + destination: 'google_adwords_remarketing_lists', + listId: '709078448', + customerId: '7693729833', + consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, +}; + +const validRequestPayload1 = { + enablePartialFailure: true, + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + hashedEmail: '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: 'e56d336922eaab3be8c1244dbaa713e134a8eba50ddbd4f50fd2fe18d72595cd', + }, + }, + ], + }, + }, + ], +}; + +const validRequestPayload2 = { + enablePartialFailure: true, + operations: [ + { + remove: { + userIdentifiers: [ + { + hashedEmail: '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + hashedEmail: '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: 'e56d336922eaab3be8c1244dbaa713e134a8eba50ddbd4f50fd2fe18d72595cd', + }, + }, + ], + }, + }, + ], +}; + +const invalidArgumentRequestPayload = { + enablePartialFailure: true, + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: 'abcd@testmail.com', + }, + ], + }, + }, + ], +}; + +const metadataArray = [generateMetadata(1)]; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const testScenariosForV0API = [ + { + id: 'garl_v0_scenario_1', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v0 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: commonHeaders, + params: commonParams, + JSON: validRequestPayload1, + endpoint: 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + destinationResponse: { response: '', status: 200 }, + }, + }, + }, + }, + }, + { + id: 'garl_v0_scenario_2', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v0 API] :: Test for a invalid argument request with a 400 response from the destination', + successCriteria: 'Should return 400 with invalid argument error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: commonHeaders, + params: commonParams, + JSON: invalidArgumentRequestPayload, + endpoint: 'https://googleads.googleapis.com/v15/customers/7693729834/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + status: 400, + message: + 'Request contains an invalid argument. during ga_audience response transformation', + destinationResponse: { + error: { + code: 400, + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v9.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + offlineUserDataJobError: 'INVALID_SHA256_FORMAT', + }, + message: 'The SHA256 encoded value is malformed.', + location: { + fieldPathElements: [ + { fieldName: 'operations', index: 0 }, + { fieldName: 'remove' }, + { fieldName: 'user_identifiers', index: 0 }, + { fieldName: 'hashed_email' }, + ], + }, + }, + ], + }, + ], + message: 'Request contains an invalid argument.', + status: 'INVALID_ARGUMENT', + }, + }, + statTags: expectedStatTags, + }, + }, + }, + }, + }, + { + id: 'garl_v0_scenario_3', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v0 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: commonHeaders, + params: commonParams, + JSON: validRequestPayload2, + endpoint: 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + destinationResponse: { response: '', status: 200 }, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API = [ + { + id: 'garl_v1_scenario_1', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v1 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: commonHeaders, + params: commonParams, + JSON: validRequestPayload1, + endpoint: + 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: '""', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'garl_v1_scenario_2', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v1 API] :: Test for a invalid argument request with a 400 response from the destination', + successCriteria: 'Should return 400 with invalid argument error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: commonHeaders, + params: commonParams, + JSON: invalidArgumentRequestPayload, + endpoint: + 'https://googleads.googleapis.com/v15/customers/7693729834/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + 'Request contains an invalid argument. during ga_audience response transformation', + response: [ + { + error: + 'Request contains an invalid argument. during ga_audience response transformation', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'garl_v1_scenario_3', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v1 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: commonHeaders, + params: commonParams, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: '""', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/data.ts b/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/data.ts index fe16ffef47..51827a38e2 100644 --- a/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/data.ts +++ b/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/data.ts @@ -1,261 +1,3 @@ -export const data = [ - { - name: 'google_adwords_remarketing_lists', - description: 'Test 0', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', - headers: { - Authorization: 'Bearer dummy-access', - 'Content-Type': 'application/json', - 'developer-token': 'dummy-dev-token', - }, - params: { - destination: 'google_adwords_remarketing_lists', - listId: '709078448', - customerId: '7693729833', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, - }, - body: { - JSON: { - enablePartialFailure: true, - operations: [ - { - create: { - userIdentifiers: [ - { - hashedEmail: - '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', - }, - { - hashedPhoneNumber: - '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', - }, - { - hashedEmail: - '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', - }, - { - hashedPhoneNumber: - '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', - }, - { - addressInfo: { - hashedFirstName: - 'e56d336922eaab3be8c1244dbaa713e134a8eba50ddbd4f50fd2fe18d72595cd', - }, - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: { - status: 200, - message: 'Request Processed Successfully', - destinationResponse: { response: '', status: 200 }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_remarketing_lists', - description: 'Test 1', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v15/customers/7693729834/offlineUserDataJobs', - headers: { - Authorization: 'Bearer dummy-access', - 'Content-Type': 'application/json', - 'developer-token': 'dummy-dev-token', - }, - params: { - listId: '709078448', - customerId: '7693729833', - destination: 'google_adwords_remarketing_lists', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, - }, - body: { - JSON: { - enablePartialFailure: true, - operations: [ - { - create: { - userIdentifiers: [ - { - hashedEmail: 'abcd@testmail.com', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - status: 400, - message: - 'Request contains an invalid argument. during ga_audience response transformation', - destinationResponse: { - error: { - code: 400, - details: [ - { - '@type': 'type.googleapis.com/google.ads.googleads.v9.errors.GoogleAdsFailure', - errors: [ - { - errorCode: { - offlineUserDataJobError: 'INVALID_SHA256_FORMAT', - }, - message: 'The SHA256 encoded value is malformed.', - location: { - fieldPathElements: [ - { fieldName: 'operations', index: 0 }, - { fieldName: 'remove' }, - { fieldName: 'user_identifiers', index: 0 }, - { fieldName: 'hashed_email' }, - ], - }, - }, - ], - }, - ], - message: 'Request contains an invalid argument.', - status: 'INVALID_ARGUMENT', - }, - }, - statTags: { - destType: 'GOOGLE_ADWORDS_REMARKETING_LISTS', - errorCategory: 'network', - destinationId: 'Non-determininable', - workspaceId: 'Non-determininable', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_remarketing_lists', - description: 'Test 2', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', - headers: { - Authorization: 'Bearer dummy-access', - 'Content-Type': 'application/json', - 'developer-token': 'dummy-dev-token', - }, - params: { - listId: '709078448', - customerId: '7693729833', - destination: 'google_adwords_remarketing_lists', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, - }, - body: { - JSON: { - enablePartialFailure: true, - operations: [ - { - remove: { - userIdentifiers: [ - { - hashedEmail: - '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', - }, - { - hashedPhoneNumber: - '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', - }, - { - hashedEmail: - '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', - }, - { - hashedPhoneNumber: - '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', - }, - { - addressInfo: { - hashedFirstName: - 'e56d336922eaab3be8c1244dbaa713e134a8eba50ddbd4f50fd2fe18d72595cd', - }, - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: { - status: 200, - message: 'Request Processed Successfully', - destinationResponse: { response: '', status: 200 }, - }, - }, - }, - }, - }, -]; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + +export const data = [...testScenariosForV0API, ...testScenariosForV1API]; From e43b83dfc192c03317d26535f67f64c41eafc709 Mon Sep 17 00:00:00 2001 From: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Date: Tue, 12 Mar 2024 13:18:25 +0530 Subject: [PATCH 025/240] refactor: trade desk proxy testcases (#3175) * refactor: trade desk proxy testcases * test: add network failure testcases * doc: add trade desk error doc * chore: format koala testcases --- .../destinations/koala/processor/data.ts | 58 ++-- .../destinations/koala/router/data.ts | 88 +++--- .../destinations/the_trade_desk/common.ts | 23 +- .../the_trade_desk/delivery/business.ts | 175 +++++++++++ .../the_trade_desk/delivery/data.ts | 251 +-------------- .../the_trade_desk/delivery/other.ts | 290 ++++++++++++++++++ .../destinations/the_trade_desk/mocks.ts | 9 + 7 files changed, 575 insertions(+), 319 deletions(-) create mode 100644 test/integrations/destinations/the_trade_desk/delivery/business.ts create mode 100644 test/integrations/destinations/the_trade_desk/delivery/other.ts diff --git a/test/integrations/destinations/koala/processor/data.ts b/test/integrations/destinations/koala/processor/data.ts index 9c1ea97a77..b866353066 100644 --- a/test/integrations/destinations/koala/processor/data.ts +++ b/test/integrations/destinations/koala/processor/data.ts @@ -31,7 +31,7 @@ export const data = [ value: 10, }, context: { - network: 'wifi' + network: 'wifi', }, originalTimestamp: '2024-01-23T08:35:17.562Z', sentAt: '2024-01-23T08:35:17.562Z', @@ -58,20 +58,22 @@ export const data = [ JSON: { ip: '192.11.22.33', email: 'johndoe@somemail.com', - events: [{ - type: 'track', - event: 'User Signed Up', - sent_at: '2024-01-23T08:35:17.562Z', - message_id: '84e26acc-56a5-4835-8233-591137fca468', - properties: { - email: 'johndoe@somemail.com', - label: 'test', - value: 10, - }, - context: { - network: 'wifi' + events: [ + { + type: 'track', + event: 'User Signed Up', + sent_at: '2024-01-23T08:35:17.562Z', + message_id: '84e26acc-56a5-4835-8233-591137fca468', + properties: { + email: 'johndoe@somemail.com', + label: 'test', + value: 10, + }, + context: { + network: 'wifi', + }, }, - }] + ], }, }, endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', @@ -127,7 +129,7 @@ export const data = [ postalCode: '94107', }, email: 'johndoe@somemail.com', - ko_profile_id: 'xxxx-2222-xxxx-xxxx' + ko_profile_id: 'xxxx-2222-xxxx-xxxx', }, originalTimestamp: '2024-01-23T08:35:17.342Z', sentAt: '2024-01-23T08:35:35.234Z', @@ -153,20 +155,22 @@ export const data = [ JSON: { email: 'johndoe@somemail.com', profile_id: 'xxxx-2222-xxxx-xxxx', - identifies: [{ - type: 'identify', - sent_at: '2024-01-23T08:35:17.342Z', - traits: { - FirstName: 'John', - LastName: 'Doe', - address: { - city: 'San Francisco', - state: 'CA', - postalCode: '94107', + identifies: [ + { + type: 'identify', + sent_at: '2024-01-23T08:35:17.342Z', + traits: { + FirstName: 'John', + LastName: 'Doe', + address: { + city: 'San Francisco', + state: 'CA', + postalCode: '94107', + }, + email: 'johndoe@somemail.com', }, - email: 'johndoe@somemail.com', }, - }], + ], }, }, endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', diff --git a/test/integrations/destinations/koala/router/data.ts b/test/integrations/destinations/koala/router/data.ts index fb0db3e3fb..f998f48dc4 100644 --- a/test/integrations/destinations/koala/router/data.ts +++ b/test/integrations/destinations/koala/router/data.ts @@ -27,14 +27,14 @@ export const data = [ type: 'track', messageId: '84e26acc-56a5-4835-8233-591137fca468', traits: { - email: 'johndoe@somemail.com' + email: 'johndoe@somemail.com', }, properties: { label: 'test', value: 10, }, context: { - network: 'wifi' + network: 'wifi', }, originalTimestamp: '2024-01-23T08:35:17.562Z', sentAt: '2024-01-23T08:35:17.562Z', @@ -44,8 +44,8 @@ export const data = [ jobId: 1, userId: 'u1', destinationId: 'destId', - workspaceId: 'wspId' - } + workspaceId: 'wspId', + }, }, { destination: { @@ -65,14 +65,14 @@ export const data = [ type: 'track', messageId: '8bc79b03-2a5c-4615-b2da-54c0aaaaaae8', traits: { - ko_profile_id: '123456' + ko_profile_id: '123456', }, properties: { attr1: 'foo', - attr2: 'bar' + attr2: 'bar', }, context: { - network: 'wifi' + network: 'wifi', }, originalTimestamp: '2024-01-23T08:35:17.562Z', sentAt: '2024-01-23T08:35:17.562Z', @@ -82,14 +82,14 @@ export const data = [ jobId: 2, userId: 'u1', destinationId: 'destId', - workspaceId: 'wspId' - } + workspaceId: 'wspId', + }, }, ], destType: 'koala', }, - method: 'POST' - } + method: 'POST', + }, }, output: { response: { @@ -105,19 +105,21 @@ export const data = [ JSON: { ip: '192.11.22.33', email: 'johndoe@somemail.com', - events: [{ - type: 'track', - event: 'User Signed Up', - sent_at: '2024-01-23T08:35:17.562Z', - message_id: '84e26acc-56a5-4835-8233-591137fca468', - properties: { - label: 'test', - value: 10, + events: [ + { + type: 'track', + event: 'User Signed Up', + sent_at: '2024-01-23T08:35:17.562Z', + message_id: '84e26acc-56a5-4835-8233-591137fca468', + properties: { + label: 'test', + value: 10, + }, + context: { + network: 'wifi', + }, }, - context: { - network: 'wifi' - }, - }] + ], }, }, endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', @@ -153,19 +155,21 @@ export const data = [ JSON: { ip: '192.11.55.1', profile_id: '123456', - events: [{ - type: 'track', - event: 'User Deleted account', - sent_at: '2024-01-23T08:35:17.562Z', - message_id: '8bc79b03-2a5c-4615-b2da-54c0aaaaaae8', - properties: { - attr1: 'foo', - attr2: 'bar' - }, - context: { - network: 'wifi' + events: [ + { + type: 'track', + event: 'User Deleted account', + sent_at: '2024-01-23T08:35:17.562Z', + message_id: '8bc79b03-2a5c-4615-b2da-54c0aaaaaae8', + properties: { + attr1: 'foo', + attr2: 'bar', + }, + context: { + network: 'wifi', + }, }, - }] + ], }, }, endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', @@ -191,10 +195,10 @@ export const data = [ }, }, }, - } - ] - } - } - } - } -] + }, + ], + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/the_trade_desk/common.ts b/test/integrations/destinations/the_trade_desk/common.ts index 28f46df829..08ba0ee6ea 100644 --- a/test/integrations/destinations/the_trade_desk/common.ts +++ b/test/integrations/destinations/the_trade_desk/common.ts @@ -5,8 +5,7 @@ const destTypeInUpperCase = 'THE_TRADE_DESK'; const advertiserId = 'test-advertiser-id'; const dataProviderId = 'rudderstack'; const segmentName = 'test-segment'; - -const trackerId = 'test-trackerId'; +const firstPartyDataEndpoint = 'https://sin-data.adsrvr.org/data/advertiser'; const sampleDestination: Destination = { Config: { @@ -48,6 +47,22 @@ const sampleContext = { sources: sampleSource, }; +const proxyV1AbortableErrorStatTags = { + destType: destTypeInUpperCase, + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +const { errorType: _, ...proxyV1PlatformErrorStatTags } = proxyV1AbortableErrorStatTags; +proxyV1PlatformErrorStatTags.errorCategory = 'platform'; + +const proxyV1RetryableErrorStatTags = { ...proxyV1AbortableErrorStatTags, errorType: 'retryable' }; + export { destType, destTypeInUpperCase, @@ -56,4 +71,8 @@ export { segmentName, sampleDestination, sampleContext, + proxyV1AbortableErrorStatTags, + proxyV1PlatformErrorStatTags, + proxyV1RetryableErrorStatTags, + firstPartyDataEndpoint, }; diff --git a/test/integrations/destinations/the_trade_desk/delivery/business.ts b/test/integrations/destinations/the_trade_desk/delivery/business.ts new file mode 100644 index 0000000000..0406a5f0bc --- /dev/null +++ b/test/integrations/destinations/the_trade_desk/delivery/business.ts @@ -0,0 +1,175 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload, generateMetadata } from '../../../testUtils'; +import { + destType, + advertiserId, + dataProviderId, + segmentName, + sampleDestination, + proxyV1AbortableErrorStatTags, + firstPartyDataEndpoint, +} from '../common'; + +const validRequestPayload1 = { + AdvertiserId: advertiserId, + DataProviderId: dataProviderId, + Items: [ + { + DAID: 'test-daid-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + { + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + UID2: 'test-uid2-1', + }, + { + DAID: 'test-daid-2', + Data: [ + { + Name: segmentName, + TTLInMinutes: 0, + }, + ], + }, + { + Data: [ + { + Name: segmentName, + TTLInMinutes: 0, + }, + ], + UID2: 'test-uid2-2', + }, + ], +}; + +const invalidRequestPayload1 = { + AdvertiserId: advertiserId, + DataProviderId: dataProviderId, + Items: [ + { + DAID: 'test-daid', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + { + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + UID2: 'test-invalid-uid2', + }, + ], +}; + +const metadataArray = [generateMetadata(1)]; + +export const businessProxyV1: ProxyV1TestData[] = [ + { + id: 'ttd_v1_scenario_1', + name: destType, + description: + '[Proxy v1 API] :: Test for a valid request - Successful delivery of Add/Remove IDs to Trade Desk', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: {}, + params: {}, + JSON: validRequestPayload1, + endpoint: firstPartyDataEndpoint, + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: '{}', + }, + ], + }, + }, + }, + }, + }, + { + id: 'ttd_v1_scenario_2', + name: destType, + description: + '[Proxy v1 API] :: Test for invalid ID - where the destination responds with 200 with invalid ID', + successCriteria: 'Should return 400 with error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: {}, + params: {}, + JSON: invalidRequestPayload1, + endpoint: firstPartyDataEndpoint, + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + 'Request failed with status: 200 due to {"FailedLines":[{"ErrorCode":"MissingUserId","Message":"Invalid UID2, item #2"}]}', + response: [ + { + error: + '{"FailedLines":[{"ErrorCode":"MissingUserId","Message":"Invalid UID2, item #2"}]}', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: proxyV1AbortableErrorStatTags, + status: 400, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/the_trade_desk/delivery/data.ts b/test/integrations/destinations/the_trade_desk/delivery/data.ts index 320eb6dcfe..5099eafce7 100644 --- a/test/integrations/destinations/the_trade_desk/delivery/data.ts +++ b/test/integrations/destinations/the_trade_desk/delivery/data.ts @@ -1,248 +1,3 @@ -import { - destType, - destTypeInUpperCase, - advertiserId, - dataProviderId, - segmentName, - sampleDestination, -} from '../common'; - -beforeAll(() => { - process.env.THE_TRADE_DESK_DATA_PROVIDER_SECRET_KEY = 'mockedDataProviderSecretKey'; -}); - -afterAll(() => { - delete process.env.THE_TRADE_DESK_DATA_PROVIDER_SECRET_KEY; -}); - -export const data = [ - { - name: destType, - description: 'Successful delivery of Add/Remove IDs to/from Trade Desk', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - destinationConfig: sampleDestination.Config, - body: { - JSON: { - AdvertiserId: advertiserId, - DataProviderId: dataProviderId, - Items: [ - { - DAID: 'test-daid-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - { - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - UID2: 'test-uid2-1', - }, - { - DAID: 'test-daid-2', - Data: [ - { - Name: segmentName, - TTLInMinutes: 0, - }, - ], - }, - { - Data: [ - { - Name: segmentName, - TTLInMinutes: 0, - }, - ], - UID2: 'test-uid2-2', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: { - destinationResponse: { - response: {}, - status: 200, - }, - message: 'Request Processed Successfully', - status: 200, - }, - }, - }, - }, - }, - { - name: destType, - description: 'Error response from The Trade Desk due to invalid IDs', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - destinationConfig: sampleDestination.Config, - body: { - JSON: { - AdvertiserId: advertiserId, - DataProviderId: dataProviderId, - Items: [ - { - DAID: 'test-daid', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - { - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - UID2: 'test-invalid-uid2', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - destinationResponse: { - response: { - FailedLines: [{ ErrorCode: 'MissingUserId', Message: 'Invalid UID2, item #2' }], - }, - status: 200, - }, - message: - 'Request failed with status: 200 due to {"FailedLines":[{"ErrorCode":"MissingUserId","Message":"Invalid UID2, item #2"}]}', - statTags: { - destType: destTypeInUpperCase, - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - status: 400, - }, - }, - }, - }, - }, - { - name: destType, - description: - 'Missing advertiser secret key in destination config from proxy request from server', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - body: { - JSON: { - AdvertiserId: advertiserId, - DataProviderId: dataProviderId, - Items: [ - { - DAID: 'test-daid-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - destinationResponse: '', - message: 'Advertiser secret key is missing in destination config. Aborting', - statTags: { - destType: destTypeInUpperCase, - destinationId: 'Non-determininable', - errorCategory: 'platform', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - status: 400, - }, - }, - }, - }, - }, -]; +import { businessProxyV1 } from './business'; +import { otherProxyV1 } from './other'; +export const data = [...businessProxyV1, ...otherProxyV1]; diff --git a/test/integrations/destinations/the_trade_desk/delivery/other.ts b/test/integrations/destinations/the_trade_desk/delivery/other.ts new file mode 100644 index 0000000000..bed10e6ec5 --- /dev/null +++ b/test/integrations/destinations/the_trade_desk/delivery/other.ts @@ -0,0 +1,290 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload, generateMetadata } from '../../../testUtils'; +import { + destType, + advertiserId, + dataProviderId, + segmentName, + proxyV1PlatformErrorStatTags, + proxyV1RetryableErrorStatTags, + firstPartyDataEndpoint, + sampleDestination, +} from '../common'; +import { envMock } from '../mocks'; + +envMock(); + +const validRequestPayload1 = { + AdvertiserId: advertiserId, + DataProviderId: dataProviderId, + Items: [ + { + DAID: 'test-daid-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + ], +}; + +const metadataArray = [generateMetadata(1)]; + +// https://partner.thetradedesk.com/v3/portal/data/doc/post-data-advertiser-external#error-codes-messages +export const otherProxyV1: ProxyV1TestData[] = [ + { + id: 'ttd_v1_other_scenario_1', + name: destType, + description: + '[Proxy v1 API] :: Missing advertiser secret key in destination config from proxy request from server', + successCriteria: 'Should return 400 with platform error', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: {}, + params: {}, + JSON: validRequestPayload1, + endpoint: firstPartyDataEndpoint, + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Advertiser secret key is missing in destination config. Aborting', + response: [ + { + error: 'Advertiser secret key is missing in destination config. Aborting', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: proxyV1PlatformErrorStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'ttd_v1_other_scenario_2', + name: destType, + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_service_not_available', + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 503, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: + 'Request failed with status: 503 due to {"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + status: 503, + }, + }, + }, + }, + }, + { + id: 'ttd_v1_other_scenario_3', + name: destType, + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_internal_server_error', + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'Request failed with status: 500 due to "Internal Server Error"', + status: 500, + }, + }, + }, + }, + }, + { + id: 'ttd_v1_other_scenario_4', + name: destType, + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout"', + statusCode: 504, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'Request failed with status: 504 due to "Gateway Timeout"', + status: 504, + }, + }, + }, + }, + }, + { + id: 'ttd_v1_other_scenario_5', + name: destType, + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_null_response', + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'Request failed with status: 500 due to ""', + status: 500, + }, + }, + }, + }, + }, + { + id: 'ttd_v1_other_scenario_6', + name: destType, + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'Request failed with status: 500 due to ""', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/the_trade_desk/mocks.ts b/test/integrations/destinations/the_trade_desk/mocks.ts index ddcbebae88..bf9d910d42 100644 --- a/test/integrations/destinations/the_trade_desk/mocks.ts +++ b/test/integrations/destinations/the_trade_desk/mocks.ts @@ -3,3 +3,12 @@ import config from '../../../../src/cdk/v2/destinations/the_trade_desk/config'; export const defaultMockFns = () => { jest.replaceProperty(config, 'MAX_REQUEST_SIZE_IN_BYTES', 250); }; + +export const envMock = () => { + process.env.THE_TRADE_DESK_DATA_PROVIDER_SECRET_KEY = 'mockedDataProviderSecretKey'; + + // Clean up after all tests are done + afterAll(() => { + delete process.env.THE_TRADE_DESK_DATA_PROVIDER_SECRET_KEY; + }); +}; From 229ce473af1ddd62d946bea1b018c882b142a5ef Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Tue, 12 Mar 2024 17:53:51 +0530 Subject: [PATCH 026/240] fix: send proper status to server in cm360 (#3127) --- .../campaign_manager/networkHandler.js | 4 ++-- .../campaign_manager/dataDelivery/oauth.ts | 24 +++++++++---------- .../campaign_manager/dataDelivery/other.ts | 8 +++---- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/v1/destinations/campaign_manager/networkHandler.js b/src/v1/destinations/campaign_manager/networkHandler.js index 79f7e7f93b..300b5f9676 100644 --- a/src/v1/destinations/campaign_manager/networkHandler.js +++ b/src/v1/destinations/campaign_manager/networkHandler.js @@ -69,7 +69,7 @@ const responseHandler = (responseParams) => { const errorMessage = response.error?.message || 'unknown error format'; for (const metadata of rudderJobMetadata) { responseWithIndividualEvents.push({ - statusCode: 500, + statusCode: status, metadata, error: errorMessage, }); @@ -77,7 +77,7 @@ const responseHandler = (responseParams) => { throw new TransformerProxyError( `Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation`, - 500, + status, { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), }, diff --git a/test/integrations/destinations/campaign_manager/dataDelivery/oauth.ts b/test/integrations/destinations/campaign_manager/dataDelivery/oauth.ts index 929af485d8..288a06bfe6 100644 --- a/test/integrations/destinations/campaign_manager/dataDelivery/oauth.ts +++ b/test/integrations/destinations/campaign_manager/dataDelivery/oauth.ts @@ -326,14 +326,14 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ }, output: { response: { - status: 500, + status: 401, body: { output: { response: [ { error: '{"error":{"code":401,"message":"Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.","errors":[{"message":"Login Required.","domain":"global","reason":"required","location":"Authorization","locationType":"header"}],"status":"UNAUTHENTICATED","details":[{"@type":"type.googleapis.com/google.rpc.ErrorInfo","reason":"CREDENTIALS_MISSING","domain":"googleapis.com","metadata":{"method":"google.ads.xfa.op.v4.DfareportingConversions.Batchinsert","service":"googleapis.com"}}]}}', - statusCode: 500, + statusCode: 401, metadata: { jobId: 1, attemptNum: 1, @@ -361,7 +361,7 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ authErrorCategory: 'REFRESH_TOKEN', message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 401, }, }, }, @@ -389,14 +389,14 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ }, output: { response: { - status: 500, + status: 403, body: { output: { response: [ { error: '{"error":{"code":403,"message":"Request had insufficient authentication scopes.","errors":[{"message":"Insufficient Permission","domain":"global","reason":"insufficientPermissions"}],"status":"PERMISSION_DENIED","details":[{"@type":"type.googleapis.com/google.rpc.ErrorInfo","reason":"ACCESS_TOKEN_SCOPE_INSUFFICIENT","domain":"googleapis.com","metadata":{"service":"gmail.googleapis.com","method":"caribou.api.proto.MailboxService.GetProfile"}}]}}', - statusCode: 500, + statusCode: 403, metadata: { jobId: 1, attemptNum: 1, @@ -424,7 +424,7 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ authErrorCategory: 'AUTH_STATUS_INACTIVE', message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 403, }, }, }, @@ -452,14 +452,14 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ }, output: { response: { - status: 500, + status: 403, body: { output: { response: [ { error: '{"error":{"code":403,"message":"invalid_grant","error_description":"Bad accesss"}}', - statusCode: 500, + statusCode: 403, metadata: { jobId: 1, attemptNum: 1, @@ -487,7 +487,7 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ authErrorCategory: 'AUTH_STATUS_INACTIVE', message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 403, }, }, }, @@ -514,14 +514,14 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ }, output: { response: { - status: 500, + status: 401, body: { output: { response: [ { error: '{"error":"unauthorized","error_description":"Access token expired: 2020-10-20T12:00:00.000Z"}', - statusCode: 500, + statusCode: 401, metadata: { jobId: 1, attemptNum: 1, @@ -549,7 +549,7 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ authErrorCategory: 'REFRESH_TOKEN', message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 401, }, }, }, diff --git a/test/integrations/destinations/campaign_manager/dataDelivery/other.ts b/test/integrations/destinations/campaign_manager/dataDelivery/other.ts index 709f55a4c0..1c0c45728c 100644 --- a/test/integrations/destinations/campaign_manager/dataDelivery/other.ts +++ b/test/integrations/destinations/campaign_manager/dataDelivery/other.ts @@ -260,7 +260,7 @@ export const otherScenariosV1: ProxyV1TestData[] = [ { error: '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', - statusCode: 500, + statusCode: 503, metadata: { jobId: 1, attemptNum: 1, @@ -287,7 +287,7 @@ export const otherScenariosV1: ProxyV1TestData[] = [ }, message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 503, }, }, }, @@ -376,7 +376,7 @@ export const otherScenariosV1: ProxyV1TestData[] = [ response: [ { error: '"Gateway Timeout"', - statusCode: 500, + statusCode: 504, metadata: { jobId: 1, attemptNum: 1, @@ -403,7 +403,7 @@ export const otherScenariosV1: ProxyV1TestData[] = [ }, message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 504, }, }, }, From 5dd5142a5d55a5eca5517c75d5356df416308afc Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:56:56 +0530 Subject: [PATCH 027/240] chore: adding proxy test cases for salesforce oauth (#3170) * chore: add initial business tests * chore: cleanup * chore: add other scenario test, refactor * chore: address commentsx1 * chore: move test to other * chore: address commentsx2 * chore: adding proxy test cases for salesforce oauth access token refresh * code review suggestion Co-authored-by: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> * fix: review comments address --------- Co-authored-by: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> --- .../salesforce_oauth/dataDelivery/data.ts | 3 + .../salesforce_oauth/dataDelivery/oauth.ts | 174 ++++++++++++++++++ .../destinations/salesforce_oauth/network.ts | 51 +++++ 3 files changed, 228 insertions(+) create mode 100644 test/integrations/destinations/salesforce_oauth/dataDelivery/data.ts create mode 100644 test/integrations/destinations/salesforce_oauth/dataDelivery/oauth.ts create mode 100644 test/integrations/destinations/salesforce_oauth/network.ts diff --git a/test/integrations/destinations/salesforce_oauth/dataDelivery/data.ts b/test/integrations/destinations/salesforce_oauth/dataDelivery/data.ts new file mode 100644 index 0000000000..bed8eec8db --- /dev/null +++ b/test/integrations/destinations/salesforce_oauth/dataDelivery/data.ts @@ -0,0 +1,3 @@ +import { testScenariosForV1API } from './oauth'; + +export const data = [...testScenariosForV1API]; diff --git a/test/integrations/destinations/salesforce_oauth/dataDelivery/oauth.ts b/test/integrations/destinations/salesforce_oauth/dataDelivery/oauth.ts new file mode 100644 index 0000000000..55eaa9cca1 --- /dev/null +++ b/test/integrations/destinations/salesforce_oauth/dataDelivery/oauth.ts @@ -0,0 +1,174 @@ +import { ProxyMetdata } from '../../../../../src/types'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const commonHeadersForWrongToken = { + Authorization: 'Bearer expiredAccessToken', + 'Content-Type': 'application/json', +}; + +const commonHeadersForRightToken = { + Authorization: 'Bearer correctAccessToken', + 'Content-Type': 'application/json', +}; +const params = { destination: 'salesforce_oauth' }; + +const users = [ + { + Email: 'danis.archurav@sbermarket.ru', + Company: 'itus.ru', + LastName: 'Danis', + FirstName: 'Archurav', + LeadSource: 'App Signup', + account_type__c: 'free_trial', + }, +]; + +const statTags = { + retryable: { + destType: 'SALESFORCE_OAUTH', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, +}; + +const commonRequestParametersWithWrongToken = { + headers: commonHeadersForWrongToken, + JSON: users[0], + params, +}; + +const commonRequestParametersWithRightToken = { + headers: commonHeadersForRightToken, + JSON: users[0], + params, +}; + +export const proxyMetdataWithSecretWithWrongAccessToken: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: { + access_token: 'expiredAccessToken', + instanceUrl: 'https://rudderstack.my.salesforce_oauth.com', + }, + destInfo: { authKey: 'dummyDestinationId' }, + dontBatch: false, +}; + +export const proxyMetdataWithSecretWithRightAccessToken: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: { + access_token: 'expiredRightToken', + instanceUrl: 'https://rudderstack.my.salesforce_oauth.com', + }, + destInfo: { authKey: 'dummyDestinationId' }, + dontBatch: false, +}; + +export const reqMetadataArrayWithWrongSecret = [proxyMetdataWithSecretWithWrongAccessToken]; +export const reqMetadataArray = [proxyMetdataWithSecretWithRightAccessToken]; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'salesforce_v1_scenario_1', + name: 'salesforce_oauth', + description: '[Proxy v1 API] :: Test with expired access token scenario', + successCriteria: + 'Should return 5XX with error category REFRESH_TOKEN and Session expired or invalid, INVALID_SESSION_ID', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParametersWithWrongToken, + endpoint: + 'https://rudderstack.my.salesforce_oauth.com/services/data/v50.0/sobjects/Lead/20', + }, + reqMetadataArrayWithWrongSecret, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + authErrorCategory: 'REFRESH_TOKEN', + message: + 'Salesforce Request Failed - due to "INVALID_SESSION_ID", (Retryable) during Salesforce Response Handling', + response: [ + { + error: + '[{"message":"Session expired or invalid","errorCode":"INVALID_SESSION_ID"}]', + metadata: proxyMetdataWithSecretWithWrongAccessToken, + statusCode: 500, + }, + ], + statTags: statTags.retryable, + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_scenario_2', + name: 'salesforce', + description: + '[Proxy v1 API] :: Test for a valid request - Lead creation with existing unchanged leadId and unchanged data', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParametersWithRightToken, + endpoint: + 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/existing_unchanged_leadId', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request for destination: salesforce Processed Successfully', + response: [ + { + error: '{"statusText":"No Content"}', + metadata: proxyMetdataWithSecretWithRightAccessToken, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/salesforce_oauth/network.ts b/test/integrations/destinations/salesforce_oauth/network.ts new file mode 100644 index 0000000000..ae5f9d3fe4 --- /dev/null +++ b/test/integrations/destinations/salesforce_oauth/network.ts @@ -0,0 +1,51 @@ +const headerWithWrongAccessToken = { + Authorization: 'Bearer expiredAccessToken', + 'Content-Type': 'application/json', +}; + +const headerWithRightAccessToken = { + Authorization: 'Bearer correctAccessToken', + 'Content-Type': 'application/json', +}; + +const dataValue = { + Email: 'danis.archurav@sbermarket.ru', + Company: 'itus.ru', + LastName: 'Danis', + FirstName: 'Archurav', + LeadSource: 'App Signup', + account_type__c: 'free_trial', +}; + +const businessMockData = [ + { + description: 'Mock response from destination depicting an expired access token', + httpReq: { + method: 'post', + url: 'https://rudderstack.my.salesforce_oauth.com/services/data/v50.0/sobjects/Lead/20', + headers: headerWithWrongAccessToken, + data: dataValue, + params: { destination: 'salesforce_oauth' }, + }, + httpRes: { + data: [{ message: 'Session expired or invalid', errorCode: 'INVALID_SESSION_ID' }], + status: 401, + }, + }, + { + description: + 'Mock response from destination depicting a valid lead request, with no changed data', + httpReq: { + method: 'post', + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/existing_unchanged_leadId', + data: dataValue, + headers: headerWithRightAccessToken, + }, + httpRes: { + data: { statusText: 'No Content' }, + status: 204, + }, + }, +]; + +export const networkCallsData = [...businessMockData]; From d2eba21191dc4f7b610414158af68e5533016014 Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Tue, 12 Mar 2024 22:26:07 +0530 Subject: [PATCH 028/240] fix: upload js coverage to codecov (#3179) --- .github/workflows/dt-test-and-report-code-coverage.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/dt-test-and-report-code-coverage.yml b/.github/workflows/dt-test-and-report-code-coverage.yml index 51a9f8c9ee..4375b3383e 100644 --- a/.github/workflows/dt-test-and-report-code-coverage.yml +++ b/.github/workflows/dt-test-and-report-code-coverage.yml @@ -41,6 +41,8 @@ jobs: - name: Upload Coverage Reports to Codecov uses: codecov/codecov-action@v4.0.1 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: directory: ./reports/coverage From 67fcdd3f4e8c60b545e7bf7164dde0d1df7b6e2c Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Wed, 13 Mar 2024 07:33:22 +0530 Subject: [PATCH 029/240] chore: resolve code injection vulnerabilities (#3164) --- src/controllers/bulkUpload.ts | 10 +++---- src/util/fetchDestinationHandlers.ts | 42 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 src/util/fetchDestinationHandlers.ts diff --git a/src/controllers/bulkUpload.ts b/src/controllers/bulkUpload.ts index babb8b6db1..dbd77dc07f 100644 --- a/src/controllers/bulkUpload.ts +++ b/src/controllers/bulkUpload.ts @@ -1,14 +1,14 @@ /* eslint-disable global-require, import/no-dynamic-require, @typescript-eslint/no-unused-vars */ import { client as errNotificationClient } from '../util/errorNotifier'; import logger from '../logger'; +import { + getJobStatusHandler, + getPollStatusHandler, + getDestFileUploadHandler, +} from '../util/fetchDestinationHandlers'; import { CatchErr, ContextBodySimple } from '../util/types'; // TODO: To be refactored and redisgned -const getDestFileUploadHandler = (version, dest) => - require(`../${version}/destinations/${dest}/fileUpload`); -const getPollStatusHandler = (version, dest) => require(`../${version}/destinations/${dest}/poll`); -const getJobStatusHandler = (version, dest) => - require(`../${version}/destinations/${dest}/fetchJobStatus`); const ERROR_MESSAGE_PROCESSOR_STRING = 'Error occurred while processing payload.'; const getCommonMetadata = (ctx) => diff --git a/src/util/fetchDestinationHandlers.ts b/src/util/fetchDestinationHandlers.ts new file mode 100644 index 0000000000..2661ef2e68 --- /dev/null +++ b/src/util/fetchDestinationHandlers.ts @@ -0,0 +1,42 @@ +import * as V0MarketoBulkUploadFileUpload from '../v0/destinations/marketo_bulk_upload/fileUpload'; +import * as V0MarketoBulkUploadPollStatus from '../v0/destinations/marketo_bulk_upload/poll'; +import * as V0MarketoBulkUploadJobStatus from '../v0/destinations/marketo_bulk_upload/fetchJobStatus'; + +const fileUploadHandlers = { + v0: { + marketo_bulk_upload: V0MarketoBulkUploadFileUpload, + }, +}; + +const pollStatusHandlers = { + v0: { + marketo_bulk_upload: V0MarketoBulkUploadPollStatus, + }, +}; + +const jobStatusHandlers = { + v0: { + marketo_bulk_upload: V0MarketoBulkUploadJobStatus, + }, +}; + +export const getDestFileUploadHandler = (version, dest) => { + if (fileUploadHandlers[version] && fileUploadHandlers[version][dest]) { + return fileUploadHandlers[version][dest]; + } + return undefined; +}; + +export const getPollStatusHandler = (version, dest) => { + if (pollStatusHandlers[version] && pollStatusHandlers[version][dest]) { + return pollStatusHandlers[version][dest]; + } + return undefined; +}; + +export const getJobStatusHandler = (version, dest) => { + if (jobStatusHandlers[version] && jobStatusHandlers[version][dest]) { + return jobStatusHandlers[version][dest]; + } + return undefined; +}; From ecbe61abef3f02a8ab2bd66f646ae088c4b50d9d Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Wed, 13 Mar 2024 12:33:49 +0530 Subject: [PATCH 030/240] chore: refactor intercom proxy tests to new component structure (#3088) * chore: refactor intercom proxy tests to new component structure * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes --- .../intercom/dataDelivery/business.ts | 615 ++++++++++++++++++ .../intercom/dataDelivery/data.ts | 96 +-- .../intercom/dataDelivery/other.ts | 384 +++++++++++ .../destinations/intercom/network.ts | 358 +++++++--- 4 files changed, 1271 insertions(+), 182 deletions(-) create mode 100644 test/integrations/destinations/intercom/dataDelivery/business.ts create mode 100644 test/integrations/destinations/intercom/dataDelivery/other.ts diff --git a/test/integrations/destinations/intercom/dataDelivery/business.ts b/test/integrations/destinations/intercom/dataDelivery/business.ts new file mode 100644 index 0000000000..2490041832 --- /dev/null +++ b/test/integrations/destinations/intercom/dataDelivery/business.ts @@ -0,0 +1,615 @@ +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; + +const commonHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const unauthorizedResponseHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer invalidApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const createUserPayload = { + email: 'test_1@test.com', + phone: '9876543210', + name: 'Test Name', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const conflictUserPayload = { + email: 'test_1@test.com', + name: 'Rudder Labs', + signed_up_at: 1601496060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_2', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const updateUserPayload = { + email: 'test_1@test.com', + phone: '9876543211', + name: 'Sample Name', + signed_up_at: 1601493060, + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const createCompanyPayload = { + company_id: 'rudderlabs', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', + custom_attributes: { isOpenSource: true }, +}; + +const sendMessagePayload = { + from: { + type: 'user', + id: 'id@1', + }, + body: 'heyy, how are you', + referer: 'https://twitter.com/bob', +}; + +const createUserRequestParameters = { + JSON: createUserPayload, + headers: commonHeaders, +}; + +const updateUserRequestParameters = { + JSON: updateUserPayload, + headers: commonHeaders, +}; + +const createCompanyRequestParameters = { + JSON: createCompanyPayload, + headers: commonHeaders, +}; + +const sendMessageRequestParameters = { + JSON: sendMessagePayload, + headers: commonHeaders, +}; + +const metadataArray = [generateMetadata(1)]; + +export const testScenariosForV0API = [ + { + id: 'intercom_v0_other_scenario_1', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario to test Invalid Credentials Handling during Destination Authentication', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...createUserRequestParameters, + headers: unauthorizedResponseHeaders, + endpoint: 'https://api.intercom.io/users/test1', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'unauthorized', + message: 'Access Token Invalid', + }, + ], + request_id: 'request123', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_2', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario to test Malformed Payload Response Handling from Destination', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...createCompanyRequestParameters, + endpoint: 'https://api.eu.intercom.io/companies', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'parameter_invalid', + message: "Custom attribute 'isOpenSource' does not exist", + }, + ], + request_id: 'request_1', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_3', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario to test Plan-Restricted Response Handling from Destination', + successCriteria: 'Should return 403 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...sendMessageRequestParameters, + endpoint: 'https://api.intercom.io/messages', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'api_plan_restricted', + message: 'Active subscription needed.', + }, + ], + request_id: 'request124', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 403, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_4', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario to test Rate Limit Exceeded Handling from Destination', + successCriteria: 'Should return 429 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...updateUserRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 429, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'rate_limit_exceeded', + message: 'The rate limit for the App has been exceeded', + }, + ], + request_id: 'request125', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 429, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_5', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario to test Conflict User Handling from Destination', + successCriteria: 'Should return 409 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + JSON: conflictUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/contacts', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 409, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'conflict', + message: 'A contact matching those details already exists with id=test1', + }, + ], + request_id: 'request126', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 409, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_6', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario to test Unsupported Media Handling from Destination', + successCriteria: 'Should return 406 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + JSON: createUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/users', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 406, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'media_type_not_acceptable', + message: 'The Accept header should send a media type of application/json', + }, + ], + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 406, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API = [ + { + id: 'intercom_v1_other_scenario_1', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario to test Invalid Credentials Handling during Destination Authentication', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...createUserRequestParameters, + headers: unauthorizedResponseHeaders, + endpoint: 'https://api.intercom.io/users/test1', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request123","errors":[{"code":"unauthorized","message":"Access Token Invalid"}]}', + metadata: generateMetadata(1), + statusCode: 401, + }, + ], + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_2', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario to test Malformed Payload Response Handling from Destination', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...createCompanyRequestParameters, + endpoint: 'https://api.eu.intercom.io/companies', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request_1","errors":[{"code":"parameter_invalid","message":"Custom attribute \'isOpenSource\' does not exist"}]}', + metadata: generateMetadata(1), + statusCode: 401, + }, + ], + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_3', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario to test Plan-Restricted Response Handling from Destination', + successCriteria: 'Should return 403 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...sendMessageRequestParameters, + endpoint: 'https://api.intercom.io/messages', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request124","errors":[{"code":"api_plan_restricted","message":"Active subscription needed."}]}', + metadata: generateMetadata(1), + statusCode: 403, + }, + ], + status: 403, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_4', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario to test Rate Limit Exceeded Handling from Destination', + successCriteria: 'Should return 429 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...updateUserRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request125","errors":[{"code":"rate_limit_exceeded","message":"The rate limit for the App has been exceeded"}]}', + metadata: generateMetadata(1), + statusCode: 429, + }, + ], + status: 429, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_5', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario to test Conflict User Handling from Destination', + successCriteria: 'Should return 409 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: conflictUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/contacts', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request126","errors":[{"code":"conflict","message":"A contact matching those details already exists with id=test1"}]}', + metadata: generateMetadata(1), + statusCode: 409, + }, + ], + status: 409, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_6', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario to test Unsupported Media Handling from Destination', + successCriteria: 'Should return 406 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: createUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/users', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"errors":[{"code":"media_type_not_acceptable","message":"The Accept header should send a media type of application/json"}],"type":"error.list"}', + metadata: generateMetadata(1), + statusCode: 406, + }, + ], + status: 406, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/intercom/dataDelivery/data.ts b/test/integrations/destinations/intercom/dataDelivery/data.ts index db7aafc963..e5d2f4696b 100644 --- a/test/integrations/destinations/intercom/dataDelivery/data.ts +++ b/test/integrations/destinations/intercom/dataDelivery/data.ts @@ -1,91 +1,9 @@ +import { otherScenariosV0, otherScenariosV1 } from './other'; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + export const data = [ - { - name: 'intercom', - description: 'Test 0', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users/test1', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - name: 'Test Name', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - update_last_request_at: true, - user_id: 'test_user_id_1', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - 'address.city': 'Kolkata', - 'address.state': 'West Bengal', - 'originalArray[0].nested_field': 'nested value', - 'originalArray[0].tags[0]': 'tag_1', - 'originalArray[0].tags[1]': 'tag_2', - 'originalArray[0].tags[2]': 'tag_3', - 'originalArray[1].nested_field': 'nested value', - 'originalArray[1].tags[0]': 'tag_1', - 'originalArray[2].nested_field': 'nested value', - }, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 500, - body: { - output: { - status: 500, - message: - '[Intercom Response Handler] Request failed for destination intercom with status: 408', - destinationResponse: { - response: { - type: 'error.list', - request_id: '000on04msi4jpk7d3u60', - errors: [ - { - code: 'Request Timeout', - message: 'The server would not wait any longer for the client', - }, - ], - }, - status: 408, - }, - statTags: { - destType: 'INTERCOM', - errorCategory: 'network', - destinationId: 'Non-determininable', - workspaceId: 'Non-determininable', - errorType: 'retryable', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - }, - }, - }, - }, - }, - }, + ...otherScenariosV0, + ...otherScenariosV1, + ...testScenariosForV0API, + ...testScenariosForV1API, ]; diff --git a/test/integrations/destinations/intercom/dataDelivery/other.ts b/test/integrations/destinations/intercom/dataDelivery/other.ts new file mode 100644 index 0000000000..46cdcac468 --- /dev/null +++ b/test/integrations/destinations/intercom/dataDelivery/other.ts @@ -0,0 +1,384 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; + +const commonHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const createUserPayload = { + email: 'test_1@test.com', + phone: '9876543210', + name: 'Test Name', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const commonRequestParameters = { + JSON: createUserPayload, + headers: commonHeaders, +}; + +const expectedStatTags = { + destType: 'INTERCOM', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +const metadataArray = [generateMetadata(1)]; + +export const otherScenariosV0 = [ + { + id: 'intercom_v0_other_scenario_1', + name: 'intercom', + description: '[Proxy v0 API] :: Request Timeout Error Handling from Destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + message: + '[Intercom Response Handler] Request failed for destination intercom with status: 408', + destinationResponse: { + response: { + type: 'error.list', + request_id: '000on04msi4jpk7d3u60', + errors: [ + { + code: 'Request Timeout', + message: 'The server would not wait any longer for the client', + }, + ], + }, + status: 408, + }, + statTags: expectedStatTags, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_2', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 503 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test2', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 503, + body: { + output: { + status: 503, + message: 'Request Processed Successfully', + destinationResponse: { + type: 'error.list', + request_id: 'request127', + errors: [ + { + code: 'service_unavailable', + message: 'Sorry, the API service is temporarily unavailable', + }, + ], + }, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_3', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test3', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'client_error', + message: 'Unknown server error', + }, + ], + request_id: 'request128', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 500, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_4', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test4', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 504, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'server_timeout', + message: 'Server timed out when making request', + }, + ], + request_id: 'request129', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 504, + }, + }, + }, + }, + }, +]; + +export const otherScenariosV1: ProxyV1TestData[] = [ + { + id: 'intercom_v1_other_scenario_1', + name: 'intercom', + description: '[Proxy v1 API] :: Request Timeout Error Handling from Destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Intercom Response Handler] Request failed for destination intercom with status: 408', + response: [ + { + error: + '{"type":"error.list","request_id":"000on04msi4jpk7d3u60","errors":[{"code":"Request Timeout","message":"The server would not wait any longer for the client"}]}', + metadata: generateMetadata(1), + statusCode: 500, + }, + ], + statTags: expectedStatTags, + status: 500, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_2', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 503 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test2', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request127","errors":[{"code":"service_unavailable","message":"Sorry, the API service is temporarily unavailable"}]}', + metadata: generateMetadata(1), + statusCode: 503, + }, + ], + status: 503, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_3', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test3', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request128","errors":[{"code":"client_error","message":"Unknown server error"}]}', + metadata: generateMetadata(1), + statusCode: 500, + }, + ], + status: 500, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_4', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test4', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request129","errors":[{"code":"server_timeout","message":"Server timed out when making request"}]}', + metadata: generateMetadata(1), + statusCode: 504, + }, + ], + status: 504, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/intercom/network.ts b/test/integrations/destinations/intercom/network.ts index 74c861259f..5fa8ac6e96 100644 --- a/test/integrations/destinations/intercom/network.ts +++ b/test/integrations/destinations/intercom/network.ts @@ -1,3 +1,48 @@ +const commonHeaders = { + Accept: 'application/json', + Authorization: 'Bearer testApiKey', + 'Content-Type': 'application/json', +}; + +const v0VersionHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', + 'User-Agent': 'RudderLabs', +}; + +const v1VersionHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '2.10', + 'User-Agent': 'RudderLabs', +}; + +const userPayload = { + email: 'test_1@test.com', + phone: '9876543210', + name: 'Test Name', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const companyPayload = { + company_id: 'rudderlabs', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', +}; + const deleteNwData = [ { httpReq: { @@ -6,11 +51,7 @@ const deleteNwData = [ data: { intercom_user_id: '1', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { data: { @@ -33,11 +74,7 @@ const deleteNwData = [ data: { intercom_user_id: '12', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -54,11 +91,7 @@ const deleteNwData = [ data: { intercom_user_id: '7', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -75,11 +108,7 @@ const deleteNwData = [ data: { intercom_user_id: '9', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -89,6 +118,8 @@ const deleteNwData = [ }, }, }, +]; +const deliveryCallsData = [ { httpReq: { method: 'post', @@ -99,11 +130,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -131,11 +158,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test+2@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -172,11 +195,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test+5@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -213,11 +232,7 @@ const deleteNwData = [ value: [{ field: 'phone', operator: '=', value: '+91 9299999999' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -254,11 +269,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test+4@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -310,19 +321,8 @@ const deleteNwData = [ httpReq: { method: 'post', url: 'https://api.eu.intercom.io/companies', - data: { - company_id: 'rudderlabs', - name: 'RudderStack', - website: 'www.rudderstack.com', - plan: 'enterprise', - size: 500, - industry: 'CDP', - }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + data: companyPayload, + headers: commonHeaders, }, httpRes: { status: 200, @@ -346,19 +346,10 @@ const deleteNwData = [ method: 'post', url: 'https://api.eu.intercom.io/companies', data: { - company_id: 'rudderlabs', - name: 'RudderStack', - website: 'www.rudderstack.com', - plan: 'enterprise', - size: 500, - industry: 'CDP', + ...companyPayload, custom_attributes: { isOpenSource: true }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 401, @@ -374,55 +365,236 @@ const deleteNwData = [ }, }, }, -]; -const deliveryCallsData = [ + { + httpReq: { + url: 'https://api.intercom.io/users/test1', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: '000on04msi4jpk7d3u60', + errors: [ + { + code: 'Request Timeout', + message: 'The server would not wait any longer for the client', + }, + ], + }, + status: 408, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test1', + data: userPayload, + params: {}, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer invalidApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request123', + errors: [ + { + code: 'unauthorized', + message: 'Access Token Invalid', + }, + ], + }, + status: 401, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/messages', + data: { + from: { + type: 'user', + id: 'id@1', + }, + body: 'heyy, how are you', + referer: 'https://twitter.com/bob', + }, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request124', + errors: [ + { + code: 'api_plan_restricted', + message: 'Active subscription needed.', + }, + ], + }, + status: 403, + }, + }, { httpReq: { url: 'https://api.intercom.io/users/test1', data: { email: 'test_1@test.com', - phone: '9876543210', - name: 'Test Name', + phone: '9876543211', + name: 'Sample Name', signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', update_last_request_at: true, user_id: 'test_user_id_1', custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', 'address.city': 'Kolkata', 'address.state': 'West Bengal', - 'originalArray[0].nested_field': 'nested value', - 'originalArray[0].tags[0]': 'tag_1', - 'originalArray[0].tags[1]': 'tag_2', - 'originalArray[0].tags[2]': 'tag_3', - 'originalArray[1].nested_field': 'nested value', - 'originalArray[1].tags[0]': 'tag_1', - 'originalArray[2].nested_field': 'nested value', }, }, params: {}, - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - 'User-Agent': 'RudderLabs', + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request125', + errors: [ + { + code: 'rate_limit_exceeded', + message: 'The rate limit for the App has been exceeded', + }, + ], }, + status: 429, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/contacts', + data: { + email: 'test_1@test.com', + name: 'Rudder Labs', + signed_up_at: 1601496060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_2', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, + }, + params: {}, + headers: v1VersionHeaders, method: 'POST', }, httpRes: { data: { type: 'error.list', - request_id: '000on04msi4jpk7d3u60', + request_id: 'request126', errors: [ { - code: 'Request Timeout', - message: 'The server would not wait any longer for the client', + code: 'conflict', + message: 'A contact matching those details already exists with id=test1', }, ], }, - status: 408, + status: 409, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users', + data: userPayload, + params: {}, + headers: v1VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + errors: [ + { + code: 'media_type_not_acceptable', + message: 'The Accept header should send a media type of application/json', + }, + ], + type: 'error.list', + }, + status: 406, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test2', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request127', + errors: [ + { + code: 'service_unavailable', + message: 'Sorry, the API service is temporarily unavailable', + }, + ], + }, + status: 503, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test3', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request128', + errors: [ + { + code: 'client_error', + message: 'Unknown server error', + }, + ], + }, + status: 500, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test4', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request129', + errors: [ + { + code: 'server_timeout', + message: 'Server timed out when making request', + }, + ], + }, + status: 504, }, }, ]; From 9dff6860248c2bd7980140c8e21c4ed3e434ee33 Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Wed, 13 Mar 2024 12:35:27 +0530 Subject: [PATCH 031/240] chore: gaoc proxy test refactor (#3072) * chore: gaoc proxy test refactor * chore: code review changes * chore: partial failure tests added * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes --- .../dataDelivery/business.ts | 669 +++++++++++++++ .../dataDelivery/data.ts | 773 +----------------- .../dataDelivery/oauth.ts | 263 ++++++ .../network.ts | 154 ++++ 4 files changed, 1093 insertions(+), 766 deletions(-) create mode 100644 test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts create mode 100644 test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts diff --git a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts new file mode 100644 index 0000000000..fbeaf7f250 --- /dev/null +++ b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts @@ -0,0 +1,669 @@ +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; + +const transactionAttribute = { + CUSTOM_KEY: 'CUSTOM_VALUE', + currency_code: 'INR', + order_id: 'order id', + store_attribute: { + store_code: 'store code', + }, + transaction_amount_micros: '100000000', + transaction_date_time: '2019-10-14 11:15:18+00:00', +}; + +const createJobPayload = { + job: { + storeSalesMetadata: { + custom_key: 'CUSTOM_KEY', + loyaltyFraction: 1, + transaction_upload_fraction: '1', + }, + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + }, +}; + +const products = [ + { + product_id: '507f1f77bcf86cd799439011', + quantity: '2', + price: '50', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + position: '1', + category: 'cars', + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg', + }, +]; + +const headers = { + header1: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', + }, + header2: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + }, +}; + +const params = { + param1: { + customerId: '1112223333', + event: 'Sign-up - click', + }, + param2: { + event: 'Sign-up - click', + customerId: '1234567891', + customVariables: [ + { + from: 'Value', + to: 'revenue', + }, + { + from: 'total', + to: 'cost', + }, + ], + properties: { + gbraid: 'gbraid', + wbraid: 'wbraid', + externalAttributionCredit: 10, + externalAttributionModel: 'externalAttributionModel', + conversionCustomVariable: 'conversionCustomVariable', + Value: 'value', + merchantId: '9876merchantId', + feedCountryCode: 'feedCountryCode', + feedLanguageCode: 'feedLanguageCode', + localTransactionCost: 20, + products, + userIdentifierSource: 'FIRST_PARTY', + conversionEnvironment: 'WEB', + gclid: 'gclid', + conversionDateTime: '2022-01-01 12:32:45-08:00', + conversionValue: '1', + currency: 'GBP', + orderId: 'PL-123QR', + }, + }, + param3: {}, + param4: {}, +}; + +params['param3'] = { ...params.param2, customVariables: [] }; + +params['param4'] = { ...params.param3, customerId: '1234567893', conversionEnvironment: 'APP' }; + +const validRequestPayload1 = { + addConversionPayload: { + enable_partial_failure: false, + enable_warnings: false, + operations: [ + { + create: { + transaction_attribute: transactionAttribute, + userIdentifiers: [ + { + hashedEmail: '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', + userIdentifierSource: 'UNSPECIFIED', + }, + ], + }, + }, + ], + validate_only: false, + }, + createJobPayload, + event: '1112223333', + executeJobPayload: { + validate_only: false, + }, + isStoreConversion: true, +}; + +const validRequestPayload2 = { + conversions: [ + { + gbraid: 'gbraid', + wbraid: 'wbraid', + externalAttributionData: { + externalAttributionCredit: 10, + externalAttributionModel: 'externalAttributionModel', + }, + cartData: { + merchantId: 9876, + feedCountryCode: 'feedCountryCode', + feedLanguageCode: 'feedLanguageCode', + localTransactionCost: 20, + items: [ + { + productId: '507f1f77bcf86cd799439011', + quantity: 2, + unitPrice: 50, + }, + ], + }, + userIdentifiers: [ + { + userIdentifierSource: 'FIRST_PARTY', + hashedPhoneNumber: '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', + }, + ], + conversionEnvironment: 'WEB', + gclid: 'gclid', + conversionDateTime: '2022-01-01 12:32:45-08:00', + conversionValue: 1, + currencyCode: 'GBP', + orderId: 'PL-123QR', + }, + ], + partialFailure: true, +}; + +const invalidArgumentRequestPayload = { + addConversionPayload: { + enable_partial_failure: false, + enable_warnings: false, + operations: [ + { + create: { + transaction_attribute: transactionAttribute, + userIdentifiers: [ + { + hashedEmail: '6db61e6dcbcf2390e4a46af26f26a133a3bee45021422fc7ae86e9136f14110', + userIdentifierSource: 'UNSPECIFIED', + }, + ], + }, + }, + ], + validate_only: false, + }, + createJobPayload, + event: '1112223333', + executeJobPayload: { + validate_only: false, + }, + isStoreConversion: true, +}; + +const notAllowedToAccessFeatureRequestPayload = { + ...validRequestPayload2, + conversions: [ + { + ...validRequestPayload2.conversions[0], + conversionEnvironment: 'APP', + }, + ], +}; + +const metadataArray = [generateMetadata(1)]; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const testScenariosForV0API = [ + { + id: 'gaoc_v0_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for invalid argument - where the destination responds with 400 with invalid argument error', + successCriteria: 'Should return 400 with error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header1, + params: params.param1, + JSON: invalidArgumentRequestPayload, + endpoint: + 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + status: 400, + message: + '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', + destinationResponse: { + error: { + code: 400, + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v14.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + offlineUserDataJobError: 'INVALID_SHA256_FORMAT', + }, + message: 'The SHA256 encoded value is malformed.', + location: { + fieldPathElements: [ + { + fieldName: 'operations', + index: 0, + }, + { + fieldName: 'create', + }, + { + fieldName: 'user_identifiers', + index: 0, + }, + { + fieldName: 'hashed_email', + }, + ], + }, + }, + ], + requestId: '68697987', + }, + ], + message: 'Request contains an invalid argument.', + status: 'INVALID_ARGUMENT', + }, + }, + statTags: expectedStatTags, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for a valid operations request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header1, + params: params.param1, + JSON: validRequestPayload1, + endpoint: 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + destinationResponse: { + response: { + name: 'customers/111-222-3333/operations/abcd=', + }, + status: 200, + }, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_scenario_3', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header2, + params: params.param2, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + destinationResponse: { + response: [ + { + adjustmentType: 'ENHANCEMENT', + conversionAction: 'customers/1234567891/conversionActions/874224905', + adjustmentDateTime: '2021-01-01 12:32:45-08:00', + gclidDateTimePair: { + gclid: '1234', + conversionDateTime: '2021-01-01 12:32:45-08:00', + }, + orderId: '12345', + }, + ], + status: 200, + }, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_scenario_4', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for a valid conversion action request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header2, + params: params.param3, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + destinationResponse: { + response: [ + { + adjustmentDateTime: '2021-01-01 12:32:45-08:00', + adjustmentType: 'ENHANCEMENT', + conversionAction: 'customers/1234567891/conversionActions/874224905', + gclidDateTimePair: { + conversionDateTime: '2021-01-01 12:32:45-08:00', + gclid: '1234', + }, + orderId: '12345', + }, + ], + status: 200, + }, + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + status: 200, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API = [ + { + id: 'gaoc_v1_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for invalid argument - where the destination responds with 400 with invalid argument error', + successCriteria: 'Should return 400 with error with destination response', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header1, + params: params.param1, + JSON: invalidArgumentRequestPayload, + endpoint: + 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', + response: [ + { + error: + '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for a valid operations request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header1, + params: params.param1, + JSON: validRequestPayload1, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + response: [ + { + error: '{"name":"customers/111-222-3333/operations/abcd="}', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_3', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header2, + params: params.param2, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + response: [ + { + error: + '[{"adjustmentType":"ENHANCEMENT","conversionAction":"customers/1234567891/conversionActions/874224905","adjustmentDateTime":"2021-01-01 12:32:45-08:00","gclidDateTimePair":{"gclid":"1234","conversionDateTime":"2021-01-01 12:32:45-08:00"},"orderId":"12345"}]', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_4', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for a valid conversion action request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header2, + params: params.param3, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + response: [ + { + error: + '[{"adjustmentType":"ENHANCEMENT","conversionAction":"customers/1234567891/conversionActions/874224905","adjustmentDateTime":"2021-01-01 12:32:45-08:00","gclidDateTimePair":{"gclid":"1234","conversionDateTime":"2021-01-01 12:32:45-08:00"},"orderId":"12345"}]', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_5', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for customer is not allowed Test for a valid request with a successful 200 response from the destinationto access feature partial failure error', + successCriteria: 'Should return 400 with error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header2, + params: params.param4, + JSON: notAllowedToAccessFeatureRequestPayload, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1234567893:uploadClickConversions', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions]:: partialFailureError - Customer is not allowlisted for accessing this feature., at conversions[0].conversion_environment', + response: [ + { + error: + '[Google Ads Offline Conversions]:: partialFailureError - Customer is not allowlisted for accessing this feature., at conversions[0].conversion_environment', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts index ae75273399..709ab6d2a8 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts @@ -1,768 +1,9 @@ +import { v0oauthScenarios, v1oauthScenarios } from './oauth'; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + export const data = [ - { - name: 'google_adwords_offline_conversions', - description: 'Test 0', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': 'logincustomerid', - }, - params: { - customerId: '1112223333', - event: 'Sign-up - click', - }, - body: { - JSON: { - addConversionPayload: { - enable_partial_failure: false, - enable_warnings: false, - operations: [ - { - create: { - transaction_attribute: { - CUSTOM_KEY: 'CUSTOM_VALUE', - currency_code: 'INR', - order_id: 'order id', - store_attribute: { - store_code: 'store code', - }, - transaction_amount_micros: '100000000', - transaction_date_time: '2019-10-14 11:15:18+00:00', - }, - userIdentifiers: [ - { - hashedEmail: - '6db61e6dcbcf2390e4a46af26f26a133a3bee45021422fc7ae86e9136f14110', - userIdentifierSource: 'UNSPECIFIED', - }, - ], - }, - }, - ], - validate_only: false, - }, - createJobPayload: { - job: { - storeSalesMetadata: { - custom_key: 'CUSTOM_KEY', - loyaltyFraction: 1, - transaction_upload_fraction: '1', - }, - type: 'STORE_SALES_UPLOAD_FIRST_PARTY', - }, - }, - event: '1112223333', - executeJobPayload: { - validate_only: false, - }, - isStoreConversion: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - status: 400, - message: - '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', - destinationResponse: { - error: { - code: 400, - details: [ - { - '@type': 'type.googleapis.com/google.ads.googleads.v14.errors.GoogleAdsFailure', - errors: [ - { - errorCode: { - offlineUserDataJobError: 'INVALID_SHA256_FORMAT', - }, - message: 'The SHA256 encoded value is malformed.', - location: { - fieldPathElements: [ - { - fieldName: 'operations', - index: 0, - }, - { - fieldName: 'create', - }, - { - fieldName: 'user_identifiers', - index: 0, - }, - { - fieldName: 'hashed_email', - }, - ], - }, - }, - ], - requestId: '68697987', - }, - ], - message: 'Request contains an invalid argument.', - status: 'INVALID_ARGUMENT', - }, - }, - statTags: { - destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 1', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': 'logincustomerid', - }, - params: { - customerId: '1112223333', - event: 'Sign-up - click', - }, - body: { - JSON: { - addConversionPayload: { - enable_partial_failure: false, - enable_warnings: false, - operations: [ - { - create: { - transaction_attribute: { - CUSTOM_KEY: 'CUSTOM_VALUE', - currency_code: 'INR', - order_id: 'order id', - store_attribute: { - store_code: 'store code', - }, - transaction_amount_micros: '100000000', - transaction_date_time: '2019-10-14 11:15:18+00:00', - }, - userIdentifiers: [ - { - hashedEmail: - '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', - userIdentifierSource: 'UNSPECIFIED', - }, - ], - }, - }, - ], - validate_only: false, - }, - createJobPayload: { - job: { - storeSalesMetadata: { - custom_key: 'CUSTOM_KEY', - loyaltyFraction: 1, - transaction_upload_fraction: '1', - }, - type: 'STORE_SALES_UPLOAD_FIRST_PARTY', - }, - }, - event: '1112223333', - executeJobPayload: { - validate_only: false, - }, - isStoreConversion: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: { - status: 200, - message: - '[Google Ads Offline Conversions Response Handler] - Request processed successfully', - destinationResponse: { - response: { - name: 'customers/111-222-3333/operations/abcd=', - }, - status: 200, - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 2', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': 'logincustomerid', - }, - params: { - customerId: '1112223333', - event: 'Sign-up - click', - }, - body: { - JSON: { - addConversionPayload: { - enable_partial_failure: false, - enable_warnings: false, - operations: [ - { - create: { - transaction_attribute: { - CUSTOM_KEY: 'CUSTOM_VALUE', - currency_code: 'INR', - order_id: 'order id', - store_attribute: { - store_code: 'store code', - }, - transaction_amount_micros: '100000000', - transaction_date_time: '2019-10-14 11:15:18+00:00', - }, - userIdentifiers: [ - { - hashedEmail: - '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', - userIdentifierSource: 'UNSPECIFIED', - }, - ], - }, - }, - ], - validate_only: false, - }, - createJobPayload: { - job: { - storeSalesMetadata: { - custom_key: 'CUSTOM_KEY', - loyaltyFraction: 1, - transaction_upload_fraction: '1', - }, - type: 'STORE_SALES_UPLOAD_FIRST_PARTY', - }, - }, - event: '1112223333', - executeJobPayload: { - validate_only: false, - }, - isStoreConversion: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 401, - body: { - output: { - status: 401, - message: - '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', - authErrorCategory: 'REFRESH_TOKEN', - destinationResponse: { - error: { - code: 401, - message: - 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', - status: 'UNAUTHENTICATED', - }, - }, - statTags: { - destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 3', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567890:uploadClickConversions', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - }, - params: { - event: 'Sign-up - click', - customerId: '1234567890', - customVariables: [ - { - from: 'value', - to: 'revenue', - }, - { - from: 'total', - to: 'cost', - }, - ], - properties: { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - conversionCustomVariable: 'conversionCustomVariable', - value: 'value', - merchantId: '9876merchantId', - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - quantity: '2', - price: '50', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - position: '1', - category: 'cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - }, - ], - userIdentifierSource: 'FIRST_PARTY', - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: '1', - currency: 'GBP', - orderId: 'PL-123QR', - }, - }, - body: { - JSON: { - conversions: [ - { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionData: { - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - }, - cartData: { - merchantId: 9876, - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - items: [ - { - productId: '507f1f77bcf86cd799439011', - quantity: 2, - unitPrice: 50, - }, - ], - }, - userIdentifiers: [ - { - userIdentifierSource: 'FIRST_PARTY', - hashedEmail: - '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', - }, - ], - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: 1, - currencyCode: 'GBP', - orderId: 'PL-123QR', - }, - ], - partialFailure: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 401, - body: { - output: { - status: 401, - message: - '[Google Ads Offline Conversions]:: [{"error":{"code":401,"message":"Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.","status":"UNAUTHENTICATED"}}] during google_ads_offline_conversions response transformation', - authErrorCategory: 'REFRESH_TOKEN', - destinationResponse: [ - { - error: { - code: 401, - message: - 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', - status: 'UNAUTHENTICATED', - }, - }, - ], - statTags: { - destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 4', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - }, - params: { - event: 'Sign-up - click', - customerId: '1234567891', - customVariables: [ - { - from: 'Value', - to: 'revenue', - }, - { - from: 'total', - to: 'cost', - }, - ], - properties: { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - conversionCustomVariable: 'conversionCustomVariable', - Value: 'value', - merchantId: '9876merchantId', - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - quantity: '2', - price: '50', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - position: '1', - category: 'cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - }, - ], - userIdentifierSource: 'FIRST_PARTY', - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: '1', - currency: 'GBP', - orderId: 'PL-123QR', - }, - }, - body: { - JSON: { - conversions: [ - { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionData: { - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - }, - cartData: { - merchantId: 9876, - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - items: [ - { - productId: '507f1f77bcf86cd799439011', - quantity: 2, - unitPrice: 50, - }, - ], - }, - userIdentifiers: [ - { - userIdentifierSource: 'FIRST_PARTY', - hashedPhoneNumber: - '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', - }, - ], - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: 1, - currencyCode: 'GBP', - orderId: 'PL-123QR', - }, - ], - partialFailure: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: { - status: 200, - message: - '[Google Ads Offline Conversions Response Handler] - Request processed successfully', - destinationResponse: { - response: [ - { - adjustmentType: 'ENHANCEMENT', - conversionAction: 'customers/1234567891/conversionActions/874224905', - adjustmentDateTime: '2021-01-01 12:32:45-08:00', - gclidDateTimePair: { - gclid: '1234', - conversionDateTime: '2021-01-01 12:32:45-08:00', - }, - orderId: '12345', - }, - ], - status: 200, - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 5', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - }, - params: { - event: 'Sign-up - click', - customerId: '1234567891', - customVariables: [], - properties: { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - conversionCustomVariable: 'conversionCustomVariable', - value: 'value', - merchantId: '9876merchantId', - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - quantity: '2', - price: '50', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - position: '1', - category: 'cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - }, - ], - userIdentifierSource: 'FIRST_PARTY', - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: '1', - currency: 'GBP', - orderId: 'PL-123QR', - }, - }, - body: { - JSON: { - conversions: [ - { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionData: { - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - }, - cartData: { - merchantId: 9876, - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - items: [ - { - productId: '507f1f77bcf86cd799439011', - quantity: 2, - unitPrice: 50, - }, - ], - }, - userIdentifiers: [ - { - userIdentifierSource: 'FIRST_PARTY', - hashedPhoneNumber: - '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', - }, - ], - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: 1, - currencyCode: 'GBP', - orderId: 'PL-123QR', - }, - ], - partialFailure: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: { - destinationResponse: { - response: [ - { - adjustmentDateTime: '2021-01-01 12:32:45-08:00', - adjustmentType: 'ENHANCEMENT', - conversionAction: 'customers/1234567891/conversionActions/874224905', - gclidDateTimePair: { - conversionDateTime: '2021-01-01 12:32:45-08:00', - gclid: '1234', - }, - orderId: '12345', - }, - ], - status: 200, - }, - message: - '[Google Ads Offline Conversions Response Handler] - Request processed successfully', - status: 200, - }, - }, - }, - }, - }, + ...v0oauthScenarios, + ...v1oauthScenarios, + ...testScenariosForV0API, + ...testScenariosForV1API, ]; diff --git a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts new file mode 100644 index 0000000000..15a150d0e5 --- /dev/null +++ b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts @@ -0,0 +1,263 @@ +import { + generateMetadata, + generateProxyV1Payload, + generateProxyV0Payload, +} from '../../../testUtils'; + +const commonHeaders = { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', +}; + +const commonParams = { + customerId: '1112223333', + event: 'Sign-up - click', +}; + +const commonRequestPayload = { + addConversionPayload: { + enable_partial_failure: false, + enable_warnings: false, + operations: [ + { + create: { + transaction_attribute: { + CUSTOM_KEY: 'CUSTOM_VALUE', + currency_code: 'INR', + order_id: 'order id', + store_attribute: { + store_code: 'store code', + }, + transaction_amount_micros: '100000000', + transaction_date_time: '2019-10-14 11:15:18+00:00', + }, + userIdentifiers: [ + { + hashedEmail: '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', + userIdentifierSource: 'UNSPECIFIED', + }, + ], + }, + }, + ], + validate_only: false, + }, + createJobPayload: { + job: { + storeSalesMetadata: { + custom_key: 'CUSTOM_KEY', + loyaltyFraction: 1, + transaction_upload_fraction: '1', + }, + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + }, + }, + event: '1112223333', + executeJobPayload: { + validate_only: false, + }, + isStoreConversion: true, +}; + +const commonRequestParameters = { + headers: commonHeaders, + params: commonParams, + JSON: commonRequestPayload, +}; + +const metadataArray = [generateMetadata(1)]; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const v0oauthScenarios = [ + { + id: 'gaoc_v0_oauth_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: 'The proxy should return 401 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + status: 401, + message: + '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', + authErrorCategory: 'REFRESH_TOKEN', + destinationResponse: { + error: { + code: 401, + message: + 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', + status: 'UNAUTHENTICATED', + }, + }, + statTags: expectedStatTags, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_oauth_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Oauth where ACCESS_TOKEN_SCOPE_INSUFFICIENT error as mock response from destination', + successCriteria: + 'Since the error from the destination is 403 - the proxy should return 403 with authErrorCategory as AUTH_STATUS_INACTIVE', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://googleads.googleapis.com/v14/customers/1234/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + authErrorCategory: 'AUTH_STATUS_INACTIVE', + destinationResponse: { + error: { + code: 403, + message: 'Request had insufficient authentication scopes', + status: 'PERMISSION_DENIED', + }, + }, + message: + '[Google Ads Offline Conversions]:: Request had insufficient authentication scopes during google_ads_offline_store_conversions Job Creation', + statTags: expectedStatTags, + status: 403, + }, + }, + }, + }, + }, +]; + +export const v1oauthScenarios = [ + { + id: 'gaoc_v1_oauth_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: 'The proxy should return 401 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + authErrorCategory: 'REFRESH_TOKEN', + message: + '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', + response: [ + { + error: + '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', + metadata: generateMetadata(1), + statusCode: 401, + }, + ], + statTags: expectedStatTags, + status: 401, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_oauth_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Oauth where ACCESS_TOKEN_SCOPE_INSUFFICIENT error as mock response from destination', + successCriteria: + 'Since the error from the destination is 403 - the proxy should return 403 with authErrorCategory as AUTH_STATUS_INACTIVE', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://googleads.googleapis.com/v14/customers/1234/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + authErrorCategory: 'AUTH_STATUS_INACTIVE', + message: + '[Google Ads Offline Conversions]:: Request had insufficient authentication scopes during google_ads_offline_store_conversions Job Creation', + response: [ + { + error: + '[Google Ads Offline Conversions]:: Request had insufficient authentication scopes during google_ads_offline_store_conversions Job Creation', + metadata: generateMetadata(1), + statusCode: 403, + }, + ], + statTags: expectedStatTags, + status: 403, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_offline_conversions/network.ts b/test/integrations/destinations/google_adwords_offline_conversions/network.ts index 375879727b..7dc7f97933 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/network.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/network.ts @@ -235,6 +235,8 @@ export const networkCallsData = [ }, }, { + description: + 'Mock response from destination depicting a request with invalid authentication credentials', httpReq: { url: 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs:create', data: { @@ -268,6 +270,41 @@ export const networkCallsData = [ }, }, }, + { + description: + 'Mock response from destination depicting a request with invalid authentication scopes', + httpReq: { + url: 'https://googleads.googleapis.com/v14/customers/1234/offlineUserDataJobs:create', + data: { + job: { + storeSalesMetadata: { + custom_key: 'CUSTOM_KEY', + loyaltyFraction: 1, + transaction_upload_fraction: '1', + }, + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + }, + }, + params: { destination: 'google_adwords_offline_conversion' }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', + }, + method: 'POST', + }, + httpRes: { + status: 403, + data: { + error: { + code: 403, + message: 'Request had insufficient authentication scopes', + status: 'PERMISSION_DENIED', + }, + }, + }, + }, { httpReq: { url: 'https://googleads.googleapis.com/v14/customers/1234567890/googleAds:searchStream', @@ -491,4 +528,121 @@ export const networkCallsData = [ status: 200, }, }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v14/customers/1234567893/googleAds:searchStream', + data: { + query: + "SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Sign-up - click'", + }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + }, + method: 'POST', + params: { destination: 'google_adwords_offline_conversion' }, + }, + httpRes: { + data: [ + { + results: [ + { + conversionAction: { + resourceName: 'customers/1234567893/conversionActions/848898417', + id: '848898417', + }, + }, + ], + fieldMask: 'conversionAction.id', + requestId: 'dummyRequestId', + }, + ], + status: 200, + }, + }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v14/customers/1234567893:uploadClickConversions', + data: { + conversions: [ + { + gbraid: 'gbraid', + wbraid: 'wbraid', + externalAttributionData: { + externalAttributionCredit: 10, + externalAttributionModel: 'externalAttributionModel', + }, + cartData: { + merchantId: 9876, + feedCountryCode: 'feedCountryCode', + feedLanguageCode: 'feedLanguageCode', + localTransactionCost: 20, + items: [{ productId: '507f1f77bcf86cd799439011', quantity: 2, unitPrice: 50 }], + }, + userIdentifiers: [ + { + userIdentifierSource: 'FIRST_PARTY', + hashedPhoneNumber: + '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', + }, + ], + conversionEnvironment: 'APP', + gclid: 'gclid', + conversionDateTime: '2022-01-01 12:32:45-08:00', + conversionValue: 1, + currencyCode: 'GBP', + orderId: 'PL-123QR', + conversionAction: 'customers/1234567893/conversionActions/848898417', + }, + ], + partialFailure: true, + }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + }, + method: 'POST', + params: { destination: 'google_adwords_offline_conversion' }, + }, + httpRes: { + status: 200, + data: { + partialFailureError: { + code: 3, + message: + 'Customer is not allowlisted for accessing this feature., at conversions[0].conversion_environment', + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v14.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + notAllowlistedError: 'CUSTOMER_NOT_ALLOWLISTED_FOR_THIS_FEATURE', + }, + message: 'Customer is not allowlisted for accessing this feature.', + trigger: { + int64Value: '2', + }, + location: { + fieldPathElements: [ + { + fieldName: 'conversions', + index: 0, + }, + { + fieldName: 'conversion_environment', + }, + ], + }, + }, + ], + requestId: 'dummyRequestId', + }, + ], + }, + }, + }, + }, ]; From bdc0f41c57776e95809414b7598107004aa2aa6b Mon Sep 17 00:00:00 2001 From: Abhimanyu Babbar Date: Wed, 13 Mar 2024 14:58:30 +0530 Subject: [PATCH 032/240] chore: base support for raising stat per tracking plan event (#2847) chore: added base support for raising event per tracking plan event --- src/controllers/trackingPlan.ts | 2 +- src/services/trackingPlan.ts | 113 ++++++++++++++++---------------- src/util/prometheus.js | 30 +++++++-- src/v0/util/index.js | 6 ++ 4 files changed, 88 insertions(+), 63 deletions(-) diff --git a/src/controllers/trackingPlan.ts b/src/controllers/trackingPlan.ts index 74e47e0ec9..e4802cfc4d 100644 --- a/src/controllers/trackingPlan.ts +++ b/src/controllers/trackingPlan.ts @@ -7,7 +7,7 @@ export class TrackingPlanController { const events = ctx.request.body; const requestSize = Number(ctx.request.get('content-length')); const reqParams = ctx.request.query; - const response = await TrackingPlanservice.validateTrackingPlan(events, requestSize, reqParams); + const response = await TrackingPlanservice.validate(events, requestSize, reqParams); ctx.body = response.body; ControllerUtility.postProcess(ctx, response.status); return ctx; diff --git a/src/services/trackingPlan.ts b/src/services/trackingPlan.ts index 2e68df55e9..93b6ee11ff 100644 --- a/src/services/trackingPlan.ts +++ b/src/services/trackingPlan.ts @@ -1,88 +1,87 @@ import logger from '../logger'; import { RetryRequestError, RespStatusError, constructValidationErrors } from '../util/utils'; -import { getMetadata } from '../v0/util'; +import { getMetadata, getTrackingPlanMetadata } from '../v0/util'; import eventValidator from '../util/eventValidation'; import stats from '../util/stats'; +import { HTTP_STATUS_CODES } from '../v0/util/constant'; export class TrackingPlanservice { - public static async validateTrackingPlan(events, requestSize, reqParams) { - const requestStartTime = new Date(); + public static async validate(events, requestSize, reqParams) { + const startTime = Date.now(); const respList: any[] = []; - const metaTags = events[0].metadata ? getMetadata(events[0].metadata) : {}; + const metaTags = events.length && events[0].metadata ? getMetadata(events[0].metadata) : {}; + const tpTags: any = + events.length && events[0].metadata ? getTrackingPlanMetadata(events[0].metadata) : {}; let ctxStatusCode = 200; for (let i = 0; i < events.length; i++) { + let eventValidationResponse: any; + let exceptionOccured = false; + const eventStartTime = Date.now(); const event = events[i]; - const eventStartTime = new Date(); + try { - const parsedEvent = event; - parsedEvent.request = { query: reqParams }; - const hv = await eventValidator.handleValidation(parsedEvent); - if (hv['dropEvent']) { - respList.push({ - output: event.message, - metadata: event.metadata, - statusCode: 400, - validationErrors: hv['validationErrors'], - error: JSON.stringify(constructValidationErrors(hv['validationErrors'])), - }); - stats.counter('tp_violation_type', 1, { - violationType: hv['violationType'], - ...metaTags, - }); - } else { - respList.push({ - output: event.message, - metadata: event.metadata, - statusCode: 200, - validationErrors: hv['validationErrors'], - error: JSON.stringify(constructValidationErrors(hv['validationErrors'])), - }); - stats.counter('tp_propagated_events', 1, { - ...metaTags, - }); - } - } catch (error) { - const errMessage = `Error occurred while validating : ${error}`; - logger.error(errMessage); - let status = 200; + event.request = { query: reqParams }; + const validatedEvent = await eventValidator.handleValidation(event); + eventValidationResponse = { + output: event.message, + metadata: event.metadata, + statusCode: validatedEvent['dropEvent'] + ? HTTP_STATUS_CODES.BAD_REQUEST + : HTTP_STATUS_CODES.OK, + validationErrors: validatedEvent['validationErrors'], + error: JSON.stringify(constructValidationErrors(validatedEvent['validationErrors'])), + }; + } catch (error: any) { + logger.debug( + `Error occurred while validating event`, + 'event', + `${event.message?.event}::${event.message?.type}`, + 'trackingPlan', + `${tpTags?.trackingPlanId}`, + 'error', + error.message, + ); + + exceptionOccured = true; + // no need to process further if + // we have error of retry request error if (error instanceof RetryRequestError) { ctxStatusCode = error.statusCode; + break; } - if (error instanceof RespStatusError) { - status = error.statusCode; - } - respList.push({ + + eventValidationResponse = { output: event.message, metadata: event.metadata, - statusCode: status, + statusCode: error instanceof RespStatusError ? error.statusCode : HTTP_STATUS_CODES.OK, validationErrors: [], - error: errMessage, - }); - stats.counter('tp_errors', 1, { - ...metaTags, - workspaceId: event.metadata?.workspaceId, - trackingPlanId: event.metadata?.trackingPlanId, - }); + error: `Error occurred while validating: ${error}`, + }; } finally { - stats.timing('tp_event_latency', eventStartTime, { + // finally on every event, we need to + // capture the information related to the validates event + stats.timing('tp_event_validation_latency', eventStartTime, { ...metaTags, + ...tpTags, + status: eventValidationResponse.statusCode, + exception: exceptionOccured, }); } - } - stats.counter('tp_events_count', events.length, { - ...metaTags, - }); + respList.push(eventValidationResponse); + } - stats.histogram('tp_request_size', requestSize, { + stats.histogram('tp_batch_size', requestSize, { ...metaTags, + ...tpTags, }); - stats.timing('tp_request_latency', requestStartTime, { + // capture overall function latency + // with metadata tags + stats.histogram('tp_batch_validation_latency', (Date.now() - startTime) / 1000, { ...metaTags, - workspaceId: events[0]?.metadata?.workspaceId, - trackingPlanId: events[0]?.metadata?.trackingPlanId, + ...tpTags, }); return { body: respList, status: ctxStatusCode }; diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 89e5424c0c..b502681987 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -590,14 +590,34 @@ class Prometheus { labelNames: ['method', 'route', 'code'], }, { - name: 'tp_request_size', - help: 'tp_request_size', + name: 'tp_batch_size', + help: 'Size of batch of events for tracking plan validation', type: 'histogram', - labelNames: ['sourceType', 'destinationType', 'k8_namespace'], + labelNames: [ + 'sourceType', + 'destinationType', + 'k8_namespace', + 'workspaceId', + 'trackingPlanId', + ], + }, + { + name: 'tp_event_validation_latency', + help: 'Latency of validating tracking plan at event level', + type: 'histogram', + labelNames: [ + 'sourceType', + 'destinationType', + 'k8_namespace', + 'workspaceId', + 'trackingPlanId', + 'status', + 'exception', + ], }, { - name: 'tp_request_latency', - help: 'tp_request_latency', + name: 'tp_batch_validation_latency', + help: 'Latency of validating tracking plan at batch level', type: 'histogram', labelNames: [ 'sourceType', diff --git a/src/v0/util/index.js b/src/v0/util/index.js index c1debce088..32872cc5d9 100644 --- a/src/v0/util/index.js +++ b/src/v0/util/index.js @@ -1419,6 +1419,11 @@ function getStringValueOfJSON(json) { return output; } +const getTrackingPlanMetadata = (metadata) => ({ + trackingPlanId: metadata.trackingPlanId, + workspaceId: metadata.workspaceId, +}); + const getMetadata = (metadata) => ({ sourceType: metadata.sourceType, destinationType: metadata.destinationType, @@ -2267,6 +2272,7 @@ module.exports = { getMappingConfig, getMetadata, getTransformationMetadata, + getTrackingPlanMetadata, getParsedIP, getStringValueOfJSON, getSuccessRespEvents, From 3bda582c0d11631980b4038b4c73a81e84b0404b Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:37:40 +0530 Subject: [PATCH 033/240] chore: ga4 proxy test refactor (#3137) * chore: ga4 proxy test refactor * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes --- .../destinations/ga4/dataDelivery/business.ts | 350 ++++++++++++++++++ .../destinations/ga4/dataDelivery/data.ts | 180 +-------- test/integrations/destinations/ga4/network.ts | 166 +++++---- 3 files changed, 446 insertions(+), 250 deletions(-) create mode 100644 test/integrations/destinations/ga4/dataDelivery/business.ts diff --git a/test/integrations/destinations/ga4/dataDelivery/business.ts b/test/integrations/destinations/ga4/dataDelivery/business.ts new file mode 100644 index 0000000000..80271abbdb --- /dev/null +++ b/test/integrations/destinations/ga4/dataDelivery/business.ts @@ -0,0 +1,350 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; +import { JSON_MIME_TYPE } from '../../../../../src/v0/util/constant'; + +const headers = { + HOST: 'www.google-analytics.com', + 'Content-Type': JSON_MIME_TYPE, +}; + +const params = { + api_secret: 'dymmyApiSecret', +}; + +const validRequest = { + events: [ + { + name: 'sign_up', + params: { + method: 'google', + engagement_time_msec: 1, + }, + }, + ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, +}; + +const invalidEventNameRequest = { + events: [ + { + name: 'campaign@details', + params: { + term: 'summer+travel', + medium: 'cpc', + source: 'google', + content: 'logo link', + campaign: 'Summer_fun', + campaign_id: 'google_1234', + engagement_time_msec: 1, + }, + }, + ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, +}; + +const invalidParameterValueRequest = { + events: [ + { + name: 'add_to_cart', + params: { + currency: 'USD', + value: 7.77, + engagement_time_msec: 1, + items: [ + { + item_id: '507f1f77bcf86cd799439011', + item_name: 'Monopoly: 3rd Edition', + coupon: 'SUMMER_FUN', + item_category: 'Apparel', + item_brand: 'Google', + item_variant: 'green', + price: '$19', + quantity: 2, + affiliation: 'Google Merchandise Store', + currency: 'USD', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + }, + ], + }, + }, + ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, +}; + +const invalidParamMessage = + 'Validation of item.price should prevent conversion from unsupported value [string_value: "$19"]'; +const invalidParameterErrorMessage = `Validation Server Response Handler:: Validation Error for ga4 of field path :undefined | INTERNAL_ERROR-${invalidParamMessage}`; +const invalidEventNameErrorMessage = + 'Validation Server Response Handler:: Validation Error for ga4 of field path :events | NAME_INVALID-Event at index: [0] has invalid name [campaign@details]. Only alphanumeric characters and underscores are allowed.'; + +const metadataArray = [generateMetadata(1)]; + +const expectedStatTags = { + destType: 'GA4', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const testScenariosForV0API = [ + { + id: 'ga4_v0_scenario_1', + name: 'ga4', + description: + '[Proxy v0 API] :: Test for a valid request - where the destination responds with 200 without any error', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers, + params, + JSON: validRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + destinationResponse: { + response: { + validationMessages: [], + }, + status: 200, + }, + message: '[GA4 Response Handler] - Request Processed Successfully', + status: 200, + }, + }, + }, + }, + }, + { + id: 'ga4_v0_scenario_2', + name: 'ga4', + description: + '[Proxy v0 API] :: Test for a invalid event name - where the destination responds with 200 with error for invalid event name', + successCriteria: 'Should return 400 with error and destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers, + params, + JSON: invalidEventNameRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + destinationResponse: + 'Event at index: [0] has invalid name [campaign@details]. Only alphanumeric characters and underscores are allowed.', + message: invalidEventNameErrorMessage, + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'ga4_v0_scenario_3', + name: 'ga4', + description: + '[Proxy v0 API] :: Test for a invalid parameter value - where the destination responds with 200 with error for invalid parameter value', + successCriteria: 'Should return 400 with error and destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers, + params, + JSON: invalidParameterValueRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + destinationResponse: invalidParamMessage, + message: invalidParameterErrorMessage, + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'ga4_v1_scenario_1', + name: 'ga4', + description: + '[Proxy v1 API] :: Test for a valid request - where the destination responds with 200 without any error', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers, + params, + JSON: validRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: '[GA4 Response Handler] - Request Processed Successfully', + response: [ + { + error: '{"validationMessages":[]}', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'ga4_v1_scenario_2', + name: 'ga4', + description: + '[Proxy v1 API] :: Test for a invalid event name - where the destination responds with 200 with error for invalid event name', + successCriteria: 'Should return 400 with error and destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers, + params, + JSON: invalidEventNameRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: invalidEventNameErrorMessage, + response: [ + { + error: invalidEventNameErrorMessage, + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'ga4_v1_scenario_3', + name: 'ga4', + description: + '[Proxy v1 API] :: Test for a invalid parameter value - where the destination responds with 200 with error for invalid parameter value', + successCriteria: 'Should return 200 with error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers, + params, + JSON: invalidParameterValueRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: invalidParameterErrorMessage, + response: [ + { + error: invalidParameterErrorMessage, + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/ga4/dataDelivery/data.ts b/test/integrations/destinations/ga4/dataDelivery/data.ts index 9ccf35e2a1..51827a38e2 100644 --- a/test/integrations/destinations/ga4/dataDelivery/data.ts +++ b/test/integrations/destinations/ga4/dataDelivery/data.ts @@ -1,177 +1,3 @@ -export const data = [ - { - name: 'ga4', - description: 'Successful data delivery', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'dummyMeasurementId', - }, - body: { - JSON: { - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_item_list', - params: { - item_list_id: 'related_products', - item_list_name: 'Related_products', - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: { - destinationResponse: { - response: { - validationMessages: [], - }, - status: 200, - }, - message: '[GA4 Response Handler] - Request Processed Successfully', - status: 200, - }, - }, - }, - }, - }, - { - name: 'ga4', - description: 'Data delivery failure', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/debug/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'dummyMeasurementId', - }, - body: { - JSON: { - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_item', - params: { - category: 'Electronics', - productID: 'ABC123', - productName: 'Example Product', - customer_name: 'Sample User', - link_imageURL: 'https://example.com/images/product.jpg', - customer_email: 'testrudder@gmail.com', - link_productURL: 'https://example.com/products/ABC123', - stockAvailability: true, - details_features_0: 'wireless charging', - details_features_1: 'water-resistant', - engagement_time_msec: 1, - transaction_currency: 'USD', - customer_loyaltyPoints: 500, - transaction_totalAmount: 150.99, - transaction_discountApplied: 20.5, - details_specifications_color: 'blue', - details_specifications_specifications_specifications_specifications_color: - 'blue', - details_specifications_specifications_specifications_specifications_weight: - '1.5kg', - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - }, - }, - output: { - response: { - status: 400, - body: { - output: { - destinationResponse: - 'The event param [string_value: "1.5kg"] has a duplicate name [details_specifications_specifications_specifications_specifications_weight].', - message: - 'Validation Server Response Handler:: Validation Error for ga4 of field path :events.params | NAME_DUPLICATED-The event param [string_value: "1.5kg"] has a duplicate name [details_specifications_specifications_specifications_specifications_weight].', - statTags: { - destType: 'GA4', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - status: 400, - }, - }, - }, - }, - }, -]; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + +export const data = [...testScenariosForV0API, ...testScenariosForV1API]; diff --git a/test/integrations/destinations/ga4/network.ts b/test/integrations/destinations/ga4/network.ts index e8c91ef451..b5c8dc8e8e 100644 --- a/test/integrations/destinations/ga4/network.ts +++ b/test/integrations/destinations/ga4/network.ts @@ -1,119 +1,139 @@ -export const networkCallsData = [ +const headers = { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', +}; + +const params = { + api_secret: 'dymmyApiSecret', +}; + +const dataDeliveryMocksData = [ { + description: 'Mock response from destination depicting a valid request', httpReq: { - url: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - 'User-Agent': 'RudderLabs', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'dummyMeasurementId', - }, + method: 'post', + url: 'https://www.google-analytics.com/debug/mp/collect', data: { - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, events: [ { - name: 'view_item_list', + name: 'sign_up', params: { - item_list_id: 'related_products', - item_list_name: 'Related_products', - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], + method: 'google', engagement_time_msec: 1, }, }, ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, }, - method: 'POST', + headers, + params, }, httpRes: { data: { validationMessages: [], }, status: 200, + statusText: 'OK', }, }, { + description: 'Mock response from destination depicting a invalid event name request', httpReq: { + method: 'post', url: 'https://www.google-analytics.com/debug/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - 'User-Agent': 'RudderLabs', + data: { + events: [ + { + name: 'campaign@details', + params: { + term: 'summer+travel', + medium: 'cpc', + source: 'google', + content: 'logo link', + campaign: 'Summer_fun', + campaign_id: 'google_1234', + engagement_time_msec: 1, + }, + }, + ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'dummyMeasurementId', + headers, + params, + }, + httpRes: { + data: { + validationMessages: [ + { + fieldPath: 'events', + description: + 'Event at index: [0] has invalid name [campaign@details]. Only alphanumeric characters and underscores are allowed.', + validationCode: 'NAME_INVALID', + }, + ], }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response from destination depicting a invalid parameter value request', + httpReq: { + method: 'post', + url: 'https://www.google-analytics.com/debug/mp/collect', data: { - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, events: [ { - name: 'view_item', + name: 'add_to_cart', params: { - category: 'Electronics', - productID: 'ABC123', - productName: 'Example Product', - customer_name: 'Sample User', - link_imageURL: 'https://example.com/images/product.jpg', - customer_email: 'testrudder@gmail.com', - link_productURL: 'https://example.com/products/ABC123', - stockAvailability: true, - details_features_0: 'wireless charging', - details_features_1: 'water-resistant', + currency: 'USD', + value: 7.77, engagement_time_msec: 1, - transaction_currency: 'USD', - customer_loyaltyPoints: 500, - transaction_totalAmount: 150.99, - transaction_discountApplied: 20.5, - details_specifications_color: 'blue', - details_specifications_specifications_specifications_specifications_color: 'blue', - details_specifications_specifications_specifications_specifications_weight: '1.5kg', + items: [ + { + item_id: '507f1f77bcf86cd799439011', + item_name: 'Monopoly: 3rd Edition', + coupon: 'SUMMER_FUN', + item_category: 'Apparel', + item_brand: 'Google', + item_variant: 'green', + price: '$19', + quantity: 2, + affiliation: 'Google Merchandise Store', + currency: 'USD', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + }, + ], }, }, ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, }, - method: 'POST', + headers, + params, }, httpRes: { data: { validationMessages: [ { - fieldPath: 'events.params', description: - 'The event param [string_value: "1.5kg"] has a duplicate name [details_specifications_specifications_specifications_specifications_weight].', - validationCode: 'NAME_DUPLICATED', + 'Validation of item.price should prevent conversion from unsupported value [string_value: "$19"]', + validationCode: 'INTERNAL_ERROR', }, ], }, status: 200, + statusText: 'OK', }, }, ]; + +export const networkCallsData = [...dataDeliveryMocksData]; From c0ad21463981ef66154c8157083924f76825762d Mon Sep 17 00:00:00 2001 From: ItsSudip Date: Wed, 13 Mar 2024 22:09:43 +0530 Subject: [PATCH 034/240] feat: add support of --- src/v0/destinations/am/transform.js | 3 ++ src/v0/destinations/am/util.test.js | 35 ++++++++++++++++++- src/v0/destinations/am/utils.js | 9 +++++ .../destinations/am/processor/data.ts | 3 ++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/v0/destinations/am/transform.js b/src/v0/destinations/am/transform.js index 2d78479ced..ce157e7674 100644 --- a/src/v0/destinations/am/transform.js +++ b/src/v0/destinations/am/transform.js @@ -525,6 +525,9 @@ const responseBuilderSimple = ( ...campaign, }; + // we are updating the payload with skip_user_properties_sync + AMUtils.updateWithSkipAttribute(message, rawPayload); + const respData = getResponseData(evType, destination, rawPayload, message, groupInfo); const { groups, rawPayload: updatedRawPayload } = respData; diff --git a/src/v0/destinations/am/util.test.js b/src/v0/destinations/am/util.test.js index 723ff3a302..498980d182 100644 --- a/src/v0/destinations/am/util.test.js +++ b/src/v0/destinations/am/util.test.js @@ -1,4 +1,9 @@ -const { getUnsetObj, validateEventType, userPropertiesPostProcess } = require('./utils'); +const { + getUnsetObj, + validateEventType, + userPropertiesPostProcess, + updateWithSkipAttribute, +} = require('./utils'); describe('getUnsetObj', () => { it("should return undefined when 'message.integrations.Amplitude.fieldsToUnset' is not array", () => { @@ -164,3 +169,31 @@ describe('userPropertiesPostProcess', () => { }); }); }); + +describe('updateWithSkipAttribute', () => { + // when 'skipUserPropertiesSync ' is present in 'integrations.Amplitude', return the original payload. + it("should return the original payload when 'skipUserPropertiesSync' is present", () => { + const message = { integrations: { Amplitude: { skipUserPropertiesSync: true } } }; + const payload = { key: 'value' }; + const expectedPayload = { key: 'value', $skip_user_properties_sync: true }; + updateWithSkipAttribute(message, payload); + expect(expectedPayload).toEqual(payload); + }); + + // When 'skipUserPropertiesSync' is not present in 'integrations.Amplitude', return the original payload. + it("should return the original payload when 'skipUserPropertiesSync' is not present", () => { + const message = { integrations: { Amplitude: {} } }; + const payload = { key: 'value' }; + const expectedPayload = { key: 'value' }; + updateWithSkipAttribute(message, payload); + expect(payload).toEqual(expectedPayload); + }); + // When 'message' is null, return null. + it("should return null when 'message' is null", () => { + const message = null; + const payload = { key: 'value' }; + const expectedPayload = { key: 'value' }; + updateWithSkipAttribute(message, payload); + expect(payload).toEqual(expectedPayload); + }); +}); diff --git a/src/v0/destinations/am/utils.js b/src/v0/destinations/am/utils.js index 190a5c1bae..8de899182b 100644 --- a/src/v0/destinations/am/utils.js +++ b/src/v0/destinations/am/utils.js @@ -11,6 +11,7 @@ const get = require('get-value'); const uaParser = require('@amplitude/ua-parser-js'); const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const set = require('set-value'); const logger = require('../../../logger'); const { isDefinedAndNotNull } = require('../../util'); @@ -110,6 +111,13 @@ const getUnsetObj = (message) => { return unsetObject; }; +const updateWithSkipAttribute = (message, payload) => { + const skipAttribute = get(message, 'integrations.Amplitude.skipUserPropertiesSync'); + if (skipAttribute) { + set(payload, '$skip_user_properties_sync', true); + } +}; + /** * Check for evType as in some cases, like when the page name is absent, * either the template depends only on the event.name or there is no template provided by user @@ -187,4 +195,5 @@ module.exports = { getUnsetObj, validateEventType, userPropertiesPostProcess, + updateWithSkipAttribute, }; diff --git a/test/integrations/destinations/am/processor/data.ts b/test/integrations/destinations/am/processor/data.ts index b645fb5ac7..01f9feb44a 100644 --- a/test/integrations/destinations/am/processor/data.ts +++ b/test/integrations/destinations/am/processor/data.ts @@ -10739,6 +10739,7 @@ export const data = [ integrations: { All: true, Amplitude: { + skipUserPropertiesSync: false, event_id: 2, }, }, @@ -10894,6 +10895,7 @@ export const data = [ integrations: { All: true, Amplitude: { + skipUserPropertiesSync: true, event_id: 2, }, }, @@ -10949,6 +10951,7 @@ export const data = [ insert_id: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', ip: '1.1.1.1', event_id: 2, + $skip_user_properties_sync: true, user_properties: { initial_referrer: 'https://docs.rudderstack.com', initial_referring_domain: 'docs.rudderstack.com', From cff7d1c4578087a37614c0ef4529058481873479 Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Thu, 14 Mar 2024 11:05:53 +0530 Subject: [PATCH 035/240] fix: fb pixel test case refactor (#3075) * fix: initial commit * fix: identify validation page screen * fix: adding ecomm test cases * fix: adding config level features test cases * fix: updating the common destination * fix: review comments addressed * fix: review comments address * fix: enhance code coverage * fix: review comments addressed --- .../facebook_pixel/transform.test.js | 28 + .../destinations/facebook_pixel/utils.test.js | 154 + src/v0/util/facebookUtils/index.js | 1 + src/v0/util/facebookUtils/index.test.js | 340 +- .../processor/configLevelFeaturesTestData.ts | 766 ++ .../facebook_pixel/processor/data.ts | 6574 +---------------- .../facebook_pixel/processor/ecommTestData.ts | 1120 +++ .../processor/identifyTestData.ts | 209 + .../processor/pageScreenTestData.ts | 721 ++ .../facebook_pixel/processor/trackTestData.ts | 208 + .../processor/validationTestData.ts | 373 + test/integrations/testUtils.ts | 2 + 12 files changed, 3933 insertions(+), 6563 deletions(-) create mode 100644 src/v0/destinations/facebook_pixel/utils.test.js create mode 100644 test/integrations/destinations/facebook_pixel/processor/configLevelFeaturesTestData.ts create mode 100644 test/integrations/destinations/facebook_pixel/processor/ecommTestData.ts create mode 100644 test/integrations/destinations/facebook_pixel/processor/identifyTestData.ts create mode 100644 test/integrations/destinations/facebook_pixel/processor/pageScreenTestData.ts create mode 100644 test/integrations/destinations/facebook_pixel/processor/trackTestData.ts create mode 100644 test/integrations/destinations/facebook_pixel/processor/validationTestData.ts diff --git a/src/v0/destinations/facebook_pixel/transform.test.js b/src/v0/destinations/facebook_pixel/transform.test.js index 25332d770c..d3c5baa070 100644 --- a/src/v0/destinations/facebook_pixel/transform.test.js +++ b/src/v0/destinations/facebook_pixel/transform.test.js @@ -24,6 +24,23 @@ const getTestMessage = () => { return message; }; +const getTestMessageWithoutProductIdAndCategory = () => { + let message = { + properties: { + currency: 'CAD', + quantity: 1, + price: 24.75, + value: 30, + name: 'my product 1', + testDimension: true, + testMetric: true, + position: 4.5, + query: 'HDMI Cable', + }, + }; + return message; +}; + const getTestCategoryToContent = () => { let categoryToContent = [ { @@ -52,6 +69,17 @@ describe('Unit test cases for facebook_pixel handle search', () => { expect(handleSearch(getTestMessage())).toEqual(expectedOutput); }); + it('should return content with content_ids and content fields as empty array', async () => { + const expectedOutput = { + content_ids: [], + content_category: '', + value: 30, + search_string: 'HDMI Cable', + contents: [], + }; + expect(handleSearch(getTestMessageWithoutProductIdAndCategory())).toEqual(expectedOutput); + }); + it("mapping 'product_id' with contentId", async () => { let message = getTestMessage(); message.properties.product_id = 'prd-123'; diff --git a/src/v0/destinations/facebook_pixel/utils.test.js b/src/v0/destinations/facebook_pixel/utils.test.js new file mode 100644 index 0000000000..f32d7d7024 --- /dev/null +++ b/src/v0/destinations/facebook_pixel/utils.test.js @@ -0,0 +1,154 @@ +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { getActionSource, formatRevenue, getCategoryFromEvent } = require('./utils'); +const { CONFIG_CATEGORIES, OTHER_STANDARD_EVENTS } = require('./config'); + +describe('Test Facebook Pixel Utils', () => { + describe('getActionSource', () => { + // Returns 'other' if payload.action_source is not defined and channel is neither 'web' nor 'mobile' + it('should return "other" when payload.action_source is not defined and channel is neither "web" nor "mobile"', () => { + const payload = {}; + const channel = 'email'; + const result = getActionSource(payload, channel); + expect(result).toBe('other'); + }); + + // Returns payload.action_source if it is defined and is a valid value from ACTION_SOURCES_VALUES + it('should return payload.action_source when it is defined and is a valid value from ACTION_SOURCES_VALUES', () => { + const payload = { action_source: 'website' }; + const channel = 'email'; + const result = getActionSource(payload, channel); + expect(result).toBe('website'); + }); + + // Returns 'website' if channel is 'web' and payload.action_source is not defined + it('should return "website" when channel is "web" and payload.action_source is not defined', () => { + const payload = {}; + const channel = 'web'; + const result = getActionSource(payload, channel); + expect(result).toBe('website'); + }); + + // Throws an InstrumentationError if payload.action_source is defined but not a valid value from ACTION_SOURCES_VALUES + it('should throw an InstrumentationError when payload.action_source is defined but not a valid value from ACTION_SOURCES_VALUES', () => { + const payload = { action_source: 'invalid' }; + const channel = 'email'; + expect(() => { + getActionSource(payload, channel); + }).toThrow(InstrumentationError); + }); + }); + + describe('formatRevenue', () => { + // Returns a number with two decimal places when passed a valid revenue value. + it('should return a number with two decimal places when passed a valid revenue value', () => { + const revenue = '100.50'; + const formattedRevenue = formatRevenue(revenue); + expect(formattedRevenue).toBe(100.5); + }); + + // Returns 0 when passed a null revenue value. + it('should return 0 when passed a null revenue value', () => { + const revenue = null; + const formattedRevenue = formatRevenue(revenue); + expect(formattedRevenue).toBe(0); + }); + + // Returns 0 when passed an undefined revenue value. + it('should return 0 when passed an undefined revenue value', () => { + const revenue = undefined; + const formattedRevenue = formatRevenue(revenue); + expect(formattedRevenue).toBe(0); + }); + + // Throws an InstrumentationError when passed a non-numeric string revenue value. + it('should throw an InstrumentationError when passed a non-numeric string revenue value', () => { + const revenue = 'abc'; + expect(() => { + formatRevenue(revenue); + }).toThrow(InstrumentationError); + }); + + // Returns a number with two decimal places when passed a numeric string revenue value with more than two decimal places. + it('should return a number with two decimal places when passed a numeric string revenue value with more than two decimal places', () => { + const revenue = '100.555'; + const formattedRevenue = formatRevenue(revenue); + expect(formattedRevenue).toBe(100.56); + }); + + // Returns a number with two decimal places when passed a numeric value with more than two decimal places. + it('should return a number with two decimal places when passed a numeric value with more than two decimal places', () => { + const revenue = 100.555; + const formattedRevenue = formatRevenue(revenue); + expect(formattedRevenue).toBe(100.56); + }); + }); + + describe('getCategoryFromEvent', () => { + // The function correctly maps the eventName to its corresponding category. + it('should correctly map the eventName to its corresponding category', () => { + const eventName = CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED.type; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED); + }); + + // The function returns the correct category for a given eventName. + it('should return the correct category for a given eventName', () => { + const eventName = CONFIG_CATEGORIES.PRODUCT_VIEWED.type; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.PRODUCT_VIEWED); + }); + + // The function returns the default category if the eventName is not recognized. + it('should return the default category if the eventName is not recognized', () => { + const eventName = 'unknownEvent'; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); + }); + + // The function handles null or undefined eventName inputs. + it('should handle null or undefined eventName inputs', () => { + const eventName = null; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); + }); + + // The function handles empty string eventName inputs. + it('should handle empty string eventName inputs', () => { + const eventName = ''; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); + }); + + // The function handles eventName inputs that are not strings. + it('should handle eventName inputs that are not strings', () => { + const eventName = 123; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); + }); + + // The function handles multiple eventNames that map to the same category. + it('should correctly map multiple eventNames to the same category', () => { + const eventName1 = CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED.type; + const eventName2 = CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED.eventName; + const result1 = getCategoryFromEvent(eventName1); + const result2 = getCategoryFromEvent(eventName2); + expect(result1).toEqual(CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED); + expect(result2).toEqual(CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED); + }); + + // The function handles eventNames that are included in the OTHER_STANDARD_EVENTS list. + it('should correctly handle eventNames included in the OTHER_STANDARD_EVENTS list', () => { + const eventName = OTHER_STANDARD_EVENTS[0]; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.OTHER_STANDARD); + expect(result.eventName).toEqual(eventName); + }); + + // The function handles eventNames that are not recognized and not in the OTHER_STANDARD_EVENTS list. + it('should correctly handle unrecognized eventNames', () => { + const eventName = 'unrecognizedEvent'; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); + }); + }); +}); diff --git a/src/v0/util/facebookUtils/index.js b/src/v0/util/facebookUtils/index.js index 7fa1e898fe..c7753d255f 100644 --- a/src/v0/util/facebookUtils/index.js +++ b/src/v0/util/facebookUtils/index.js @@ -298,4 +298,5 @@ module.exports = { transformedPayloadData, formingFinalResponse, fetchUserData, + deduceFbcParam, }; diff --git a/src/v0/util/facebookUtils/index.test.js b/src/v0/util/facebookUtils/index.test.js index 98e4ccec40..20c4ee59f2 100644 --- a/src/v0/util/facebookUtils/index.test.js +++ b/src/v0/util/facebookUtils/index.test.js @@ -1,5 +1,11 @@ -const { transformedPayloadData } = require('./index'); +const { + transformedPayloadData, + fetchUserData, + deduceFbcParam, + getContentType, +} = require('./index'); const sha256 = require('sha256'); +const { MAPPING_CONFIG, CONFIG_CATEGORIES } = require('../../destinations/facebook_pixel/config'); describe('transformedPayloadData_function', () => { // Tests with default values for all parameters @@ -301,3 +307,335 @@ describe('transformedPayloadData_function', () => { expect(result).toEqual({}); }); }); + +describe('deduceFbcParam', () => { + // Should return undefined if message.context.page.url is undefined + it('should return undefined when message.context.page.url is undefined', () => { + const message = {}; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should return undefined if URL constructor throws an error + it('should return undefined when URL constructor throws an error', () => { + const message = { + context: { + page: { + url: 'invalid-url', + }, + }, + }; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should return undefined if fbclid is undefined + it('should return undefined when fbclid is undefined', () => { + const message = { + context: { + page: { + url: 'https://example.com', + }, + }, + }; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should handle message with empty context object + it('should handle message with empty context object', () => { + const message = { + context: {}, + }; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should handle message with empty page object + it('should handle message with empty page object', () => { + const message = { + context: { + page: {}, + }, + }; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should handle message with empty url string + it('should handle message with empty url string', () => { + const message = { + context: { + page: { + url: '', + }, + }, + }; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should return fbc parameter when all conditions are met + it('should return fbc parameter when all conditions are met', () => { + const message = { + context: { + page: { + url: 'https://example.com?fbclid=123456', + }, + }, + }; + const result = deduceFbcParam(message); + expect(result).toEqual(expect.stringContaining('fb.1.')); + }); +}); + +describe('fetchUserData', () => { + const message = { + channel: 'web', + context: { + traits: { + name: 'Rudder Test', + email: 'abc@gmail.com', + firstname: 'Rudder', + lastname: 'Test', + phone: 9000000000, + gender: 'female', + }, + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + properties: { + plan: 'standard plan', + name: 'rudder test', + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + originalTimestamp: '2023-10-14T15:46:51.693229+05:30', + anonymousId: '00000000000000000000000000', + userId: '123456', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }; + + const Config = { + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: true, + whitelistPiiProperties: [ + { + whitelistPiiProperties: '', + }, + ], + }; + + // Returns a valid user data object when given valid inputs. + it('should return a valid user data object when given valid inputs without integrations object', () => { + const mappingJson = MAPPING_CONFIG[CONFIG_CATEGORIES.USERDATA.name]; + const destinationName = 'fb_pixel'; + + const result = fetchUserData(message, Config, mappingJson, destinationName); + + expect(result).toEqual({ + external_id: '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + ph: '593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579', + ge: '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + ln: '532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25', + fn: '2c2ccf28d806f6f9a34b67aa874d2113b7ac1444f1a4092541b8b75b84771747', + client_ip_address: '0.0.0.0', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + fbc: undefined, + }); + }); + + it('should return a valid user data object when given valid inputs with integrations object', () => { + const mappingJson = MAPPING_CONFIG[CONFIG_CATEGORIES.USERDATA.name]; + const destinationName = 'fb_pixel'; + message.integrations.FacebookPixel = { hashed: true }; + + const result = fetchUserData(message, Config, mappingJson, destinationName); + + expect(result).toEqual({ + em: 'abc@gmail.com', + external_id: '123456', + ph: '9000000000', + ge: '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + ln: 'Test', + fn: 'Rudder', + client_ip_address: '0.0.0.0', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + fbc: undefined, + }); + }); + + it('should return null when mappingJson is undefined', () => { + const mappingJson = undefined; + const destinationName = 'fb_pixel'; + const result = fetchUserData(message, Config, mappingJson, destinationName); + + expect(result).toBeNull(); + }); + + it('should return hashed data when destinationName is undefined', () => { + const mappingJson = MAPPING_CONFIG[CONFIG_CATEGORIES.USERDATA.name]; + const destinationName = undefined; + + const result = fetchUserData(message, Config, mappingJson, destinationName); + + expect(result).toEqual({ + client_ip_address: '0.0.0.0', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + external_id: '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', + fbc: undefined, + fn: '2c2ccf28d806f6f9a34b67aa874d2113b7ac1444f1a4092541b8b75b84771747', + ge: '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + ln: '532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25', + ph: '593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579', + }); + }); +}); + +describe('getContentType', () => { + // Returns default value when no category or categoryToContent is provided + it('should return default value when no category or categoryToContent is provided', () => { + const message = { + properties: { + produtcs: [ + { + product_id: '123', + }, + ], + }, + }; + const defaultValue = 'product'; + const categoryToContent = []; + const destinationName = 'fb_pixel'; + + const result = getContentType(message, defaultValue, categoryToContent, destinationName); + + expect(result).toBe(defaultValue); + }); + + // Returns default value when categoryToContent is not an array + it('should return default value when categoryToContent is not an array', () => { + const message = { + properties: { + products: [ + { + product_id: '123', + }, + ], + }, + }; + const defaultValue = 'product'; + const categoryToContent = 'not an array'; + const destinationName = 'fb_pixel'; + + const result = getContentType(message, defaultValue, categoryToContent, destinationName); + + expect(result).toBe(defaultValue); + }); + + // Returns categoryToContent value when category is provided and matches with categoryToContent + it('should return categoryToContent value when category is provided and matches with categoryToContent', () => { + const message = { + properties: { + category: 'clothing', + }, + }; + const defaultValue = 'product'; + const categoryToContent = [{ from: 'clothing', to: 'garments' }]; + const destinationName = 'fb_pixel'; + + const result = getContentType(message, defaultValue, categoryToContent, destinationName); + + expect(result).toBe(categoryToContent[0].to); + }); + + // Returns integrationsObj.contentType when it exists + it('should return integrationsObj.contentType when it exists', () => { + const message = { + properties: { + products: [ + { + product_id: '123', + }, + ], + }, + integrations: { + fb_pixel: { + contentType: 'content_type_value', + }, + }, + }; + const defaultValue = 'product'; + const categoryToContent = []; + const destinationName = 'fb_pixel'; + const integrationsObj = { + contentType: 'content_type_value', + }; + + const result = getContentType(message, defaultValue, categoryToContent, destinationName); + + expect(result).toBe(integrationsObj.contentType); + }); + + // Returns 'product' when category is 'clothing' and categoryToContent is not provided + it("should return 'product' when category is 'clothing' and categoryToContent is not provided", () => { + const message = { + properties: { + category: 'clothing', + }, + }; + const defaultValue = 'product'; + const categoryToContent = []; + const destinationName = 'fb_pixel'; + + const result = getContentType(message, defaultValue, categoryToContent, destinationName); + + expect(result).toBe(defaultValue); + }); +}); diff --git a/test/integrations/destinations/facebook_pixel/processor/configLevelFeaturesTestData.ts b/test/integrations/destinations/facebook_pixel/processor/configLevelFeaturesTestData.ts new file mode 100644 index 0000000000..5a7beb4174 --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/configLevelFeaturesTestData.ts @@ -0,0 +1,766 @@ +import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { + overrideDestination, + generateTrackPayload, + generateMetadata, + transformResultBuilder, +} from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + limitedDataUSage: true, + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: 'ABC Started', + to: 'InitiateCheckout', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'email', + }, + ], + categoryToContent: [ + { + from: 'clothing', + to: 'newClothing', + }, + ], + }, + Enabled: true, +}; + +const commonUserTraits = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', +}; + +// the below object has properties that are used as whitelist and blacklist properties in below test cases +const piiPropertiesForAllowDeny = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', + firstName: 'John', + lastName: 'Doe', + whitelistProp1: 'val1', + blacklistProp2: 'val2', + blacklistProp3: 'val3', + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', +}; + +const commonPropertiesWithoutProductArray = { + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', +}; + +const commonTimestamp = new Date('2023-10-14'); + +export const configLevelFeaturesTestData: ProcessorTestData[] = [ + { + id: 'facebook_pixel-config-test-1', + name: 'facebook_pixel', + description: + 'config feature : limitedDataUSage switched on. Ref:https://developers.facebook.com/docs/marketing-apis/data-processing-options/#supported-tools-and-apis ', + scenario: 'configuration', + successCriteria: 'Response should contain limitedDataUSage related fields', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + dataProcessingOptions: ['val1', 'val2', 'val3'], + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + data_processing_options: 'val1', + data_processing_options_country: 'val2', + data_processing_options_state: 'val3', + custom_data: { + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['dummy'], + content_type: 'product_group', + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-2', + name: 'facebook_pixel', + scenario: 'configuration', + description: + 'config feature : While categoryToContent mapping is filled up in UI, but category is passed with message.properties as well. message.properties.category should be given priority over categoryToContent mapping', + successCriteria: 'Response should contain category mapped to newClothing', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...commonPropertiesWithoutProductArray, category: 'clothing' }, + context: { + traits: commonUserTraits, + dataProcessingOptions: ['val1', 'val2', 'val3'], + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + data_processing_options: 'val1', + data_processing_options_country: 'val2', + data_processing_options_state: 'val3', + custom_data: { + category: 'clothing', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['clothing'], + content_type: 'newClothing', + contents: [ + { + id: 'clothing', + quantity: 1, + }, + ], + content_category: 'clothing', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-3', + name: 'facebook_pixel', + description: + 'config feature : ContentCategoryMapping table is filled up, and category is passed with properties along with contentType via integrations object', + scenario: 'configuration', + successCriteria: 'contentType should be used from integrations object', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...commonPropertiesWithoutProductArray, category: 'clothing' }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + integrations: { + FacebookPixel: { + contentType: 'newClothingFromIntegrationObject', + }, + }, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'clothing', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['clothing'], + content_type: 'newClothingFromIntegrationObject', + contents: [ + { + id: 'clothing', + quantity: 1, + }, + ], + content_category: 'clothing', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-4', + name: 'facebook_pixel', + description: + 'config feature : Config mapped whiteList and blackListed properties with marked hashed within integrations object, along with default pii property email in the properties', + scenario: 'configuration', + successCriteria: + 'BlackListed properties should not be hashed and default pii property should be deleted from the properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...piiPropertiesForAllowDeny }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + integrations: { + FacebookPixel: { + hashed: true, + }, + }, + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'whitelistProp1', + }, + ], + blacklistPiiProperties: [ + { + blacklistPiiProperties: 'blacklistProp2', + }, + { + blacklistPiiProperties: 'blacklistProp3', + }, + ], + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: 'default-user-id', + em: 'abc@gmail.com', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + whitelistProp1: 'val1', + blacklistProp2: 'val2', + blacklistProp3: 'val3', + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['dummy'], + content_type: 'product_group', + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-5', + name: 'facebook_pixel', + description: + 'config feature : Config mapped whiteList and blackListed properties without marked hashed within integrations object but marked hashed true from UI, along with default pii property email in the properties', + scenario: 'configuration', + successCriteria: + 'BlackListed properties should be hashed and default pii property should be deleted from the properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...piiPropertiesForAllowDeny }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'whitelistProp1', + }, + ], + blacklistPiiProperties: [ + { + blacklistPiiProperties: 'blacklistProp2', + blacklistPiiHash: true, + }, + { + blacklistPiiProperties: 'blacklistProp3', + blacklistPiiHash: true, + }, + ], + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + whitelistProp1: 'val1', + blacklistProp2: + '528e5290f8ff0eb0325f0472b9c1a9ef4fac0b02ff6094b64d9382af4a10444b', + blacklistProp3: + 'bac8d4414984861d5199b7a97699c728bee36c4084299b2ca905434cf65d8944', + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['dummy'], + content_type: 'product_group', + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-6', + name: 'facebook_pixel', + description: + 'config feature : Config mapped whiteList and blackListed properties marked hashed within integrations object but marked hashed true from UI, along with default pii property email in the properties', + scenario: 'configuration', + successCriteria: + 'BlackListed properties should not be hashed again and default pii property should be deleted from the properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...piiPropertiesForAllowDeny }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + integrations: { + FacebookPixel: { + hashed: true, + }, + }, + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'whitelistProp1', + }, + ], + blacklistPiiProperties: [ + { + blacklistPiiProperties: 'blacklistProp2', + blacklistPiiHash: true, + }, + { + blacklistPiiProperties: 'blacklistProp3', + blacklistPiiHash: true, + }, + ], + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: 'default-user-id', + em: 'abc@gmail.com', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + whitelistProp1: 'val1', + blacklistProp2: 'val2', + blacklistProp3: 'val3', + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['dummy'], + content_type: 'product_group', + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-7', + name: 'facebook_pixel', + description: + 'properties.content_type is given priority over populating it from categoryToContent mapping.', + scenario: 'configuration', + successCriteria: 'Response should contain content_type as product_group and not newClothing', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...piiPropertiesForAllowDeny, content_type: 'product_group' }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + integrations: { + FacebookPixel: { + hashed: true, + }, + }, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: 'default-user-id', + em: 'abc@gmail.com', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + whitelistProp1: 'val1', + blacklistProp2: 'val2', + blacklistProp3: 'val3', + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_type: 'product_group', + content_ids: ['dummy'], + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/processor/data.ts b/test/integrations/destinations/facebook_pixel/processor/data.ts index f6a5cd1e20..6af7e3cd9b 100644 --- a/test/integrations/destinations/facebook_pixel/processor/data.ts +++ b/test/integrations/destinations/facebook_pixel/processor/data.ts @@ -1,4 +1,9 @@ -import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { identifyTestData } from './identifyTestData'; +import { trackTestData } from './trackTestData'; +import { validationTestData } from './validationTestData'; +import { pageScreenTestData } from './pageScreenTestData'; +import { ecommTestData } from './ecommTestData'; +import { configLevelFeaturesTestData } from './configLevelFeaturesTestData'; export const mockFns = (_) => { // @ts-ignore @@ -6,6565 +11,10 @@ export const mockFns = (_) => { }; export const data = [ - { - name: 'facebook_pixel', - description: 'Test 0', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - channel: 'mobile', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: ' aBc@gmail.com ', - address: { - zip: 1234, - }, - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T00:00:00.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - removeExternalId: true, - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"spin_result","event_time":1697221800,"action_source":"app","custom_data":{"additional_bet_index":0,"value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 1', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: ' aBc@gmail.com ', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'group', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: 'Message type group not supported', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 2', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - traits: { - name: 'Test', - }, - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - properties: { - plan: 'standard plan', - name: 'rudder test', - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: - 'For identify events, "Advanced Mapping" configuration must be enabled on the RudderStack dashboard', - statTags: { - errorCategory: 'dataValidation', - errorType: 'configuration', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 3', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - traits: { - name: 'Rudder Test', - email: 'abc@gmail.com', - firstname: 'Rudder', - lastname: 'Test', - phone: 9000000000, - gender: 'female', - }, - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - properties: { - plan: 'standard plan', - name: 'rudder test', - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: true, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","ph":"593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579","ge":"252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111","ln":"532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25","fn":"2c2ccf28d806f6f9a34b67aa874d2113b7ac1444f1a4092541b8b75b84771747","client_ip_address":"0.0.0.0","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"identify","event_time":1697278611,"event_id":"84e26acc-56a5-4835-8233-591137fca468","action_source":"website"}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 4', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - traits: { - name: 'Rudder Test', - email: 'abc@gmail.com', - phone: 9000000000, - address: { - postalCode: 1234, - }, - gender: 'female', - }, - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - properties: { - plan: 'standard plan', - name: 'rudder test', - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: true, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","ph":"593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579","ge":"252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4","client_ip_address":"0.0.0.0","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36","fn":"2c2ccf28d806f6f9a34b67aa874d2113b7ac1444f1a4092541b8b75b84771747","ln":"532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25"},"event_name":"identify","event_time":1697278611,"event_id":"84e26acc-56a5-4835-8233-591137fca468","action_source":"website"}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 5', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - email: 'abc@gmail.com', - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","custom_data":{"additional_bet_index":0,"value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 6', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - email: 'abc@gmail.com', - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","custom_data":{"additional_bet_index":0,"email":"abc@gmail.com","value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 7', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - email: 'abc@gmail.com', - phone: 9000000000, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","custom_data":{"additional_bet_index":0,"email":"abc@gmail.com","value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 8', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - email: 'abc@gmail.com', - phone: 9000000000, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","custom_data":{"additional_bet_index":0,"email":"abc@gmail.com","phone":"593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579","value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 9', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - timestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: 'xyz', - search: 'def', - title: 'ghi', - url: 'jkl', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_ip_address":"0.0.0.0","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Viewed page ApplicationLoaded","event_time":1697278611,"event_source_url":"jkl","event_id":"5e10d13a-bf9a-44bf-b884-43a9e591ea71","action_source":"website","custom_data":{"path":"/abc","referrer":"xyz","search":"def","title":"ghi","url":"jkl"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 10', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: 'xyz', - search: 'def', - title: 'ghi', - url: 'jkl', - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_ip_address":"0.0.0.0","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Viewed a page","event_time":1697278611,"event_source_url":"jkl","event_id":"5e10d13a-bf9a-44bf-b884-43a9e591ea71","action_source":"website","custom_data":{"path":"/abc","referrer":"xyz","search":"def","title":"ghi","url":"jkl"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 11', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product list viewed', - properties: { - phone: 9000000000, - email: 'abc@gmail.com', - category: 'cat 1', - list_id: '1234', - filters: [ - { - type: 'department', - value: 'beauty', - }, - { - type: 'price', - value: 'under', - }, - ], - sorts: [ - { - type: 'price', - value: 'desc', - }, - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"phone":"593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579","email":"abc@gmail.com","category":"cat 1","list_id":"1234","filters[0].type":"department","filters[0].value":"beauty","filters[1].type":"price","filters[1].value":"under","sorts[0].type":"price","sorts[0].value":"desc","testDimension":true,"testMetric":true,"content_ids":["cat 1"],"content_type":"product_group","contents":[{"id":"cat 1","quantity":1}],"content_category":"cat 1","value":0,"currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 12', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product list viewed', - properties: { - phone: 9000000000, - email: 'abc@gmail.com', - category: 'cat 1', - list_id: '1234', - filters: [ - { - type: 'department', - value: 'beauty', - }, - { - type: 'price', - value: 'under', - }, - ], - sorts: [ - { - type: 'price', - value: 'desc', - }, - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"category":"cat 1","list_id":"1234","filters[0].type":"department","filters[0].value":"beauty","filters[1].type":"price","filters[1].value":"under","sorts[0].type":"price","sorts[0].value":"desc","testDimension":true,"testMetric":true,"content_ids":["cat 1"],"content_type":"product_group","contents":[{"id":"cat 1","quantity":1}],"content_category":"cat 1","value":0,"currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 13', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product list viewed', - properties: { - email: 'abc@gmail.com', - quantity: 2, - category: 'cat 1', - list_id: '1234', - contentName: 'nutrition', - value: 18.9, - filters: [ - { - type: 'department', - value: 'beauty', - }, - { - type: 'price', - value: 'under', - }, - ], - sorts: [ - { - type: 'price', - value: 'desc', - }, - ], - products: [ - { - product_id: '507f1f77bcf86cd799439011', - productDimension: 'My Product Dimension', - productMetric: 'My Product Metric', - position: 10, - }, - { - product_id: '507f1f77bcf86cdef799439011', - productDimension: 'My Product Dimension1', - productMetric: 'My Product Metric1', - position: -10, - }, - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"quantity":2,"category":"cat 1","list_id":"1234","contentName":"nutrition","value":18.9,"filters[0].type":"department","filters[0].value":"beauty","filters[1].type":"price","filters[1].value":"under","sorts[0].type":"price","sorts[0].value":"desc","products[0].product_id":"507f1f77bcf86cd799439011","products[0].productDimension":"My Product Dimension","products[0].productMetric":"My Product Metric","products[0].position":10,"products[1].product_id":"507f1f77bcf86cdef799439011","products[1].productDimension":"My Product Dimension1","products[1].productMetric":"My Product Metric1","products[1].position":-10,"testDimension":true,"testMetric":true,"content_ids":["507f1f77bcf86cd799439011","507f1f77bcf86cdef799439011"],"content_type":"product","contents":[{"id":"507f1f77bcf86cd799439011","quantity":2},{"id":"507f1f77bcf86cdef799439011","quantity":2}],"content_category":"cat 1","content_name":"nutrition","currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 14', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'my product list', - properties: { - email: 'abc@gmail.com', - quantity: 2, - category: 'cat 1', - list_id: '1234', - filters: [ - { - type: 'department', - value: 'beauty', - }, - { - type: 'price', - value: 'under', - }, - ], - sorts: [ - { - type: 'price', - value: 'desc', - }, - ], - products: [ - { - product_id: '507f1f77bcf86cd799439011', - productDimension: 'My Product Dimension', - productMetric: 'My Product Metric', - position: 10, - }, - { - product_id: '507f1f77bcf86cdef799439011', - productDimension: 'My Product Dimension1', - productMetric: 'My Product Metric1', - position: -10, - }, - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - removeExternalId: false, - eventsToEvents: [ - { - from: 'My product list', - to: 'ViewContent', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: 'list_id', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"quantity":2,"category":"cat 1","list_id":"1234","filters[0].type":"department","filters[0].value":"beauty","filters[1].type":"price","filters[1].value":"under","sorts[0].type":"price","sorts[0].value":"desc","products[0].product_id":"507f1f77bcf86cd799439011","products[0].productDimension":"My Product Dimension","products[0].productMetric":"My Product Metric","products[0].position":10,"products[1].product_id":"507f1f77bcf86cdef799439011","products[1].productDimension":"My Product Dimension1","products[1].productMetric":"My Product Metric1","products[1].position":-10,"testDimension":true,"testMetric":true,"content_ids":["507f1f77bcf86cd799439011","507f1f77bcf86cdef799439011"],"content_type":"product","contents":[{"id":"507f1f77bcf86cd799439011","quantity":2},{"id":"507f1f77bcf86cdef799439011","quantity":2}],"content_category":"cat 1","value":0,"currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 15', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product viewed', - properties: { - currency: 'CAD', - quantity: 1, - price: 24.75, - name: 'my product 1', - category: 'clothing', - sku: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - removeExternalId: true, - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"price":24.75,"name":"my product 1","category":"clothing","sku":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"product","content_name":"my product 1","content_category":"clothing","value":24.75,"contents":[{"id":"p-298","quantity":1,"item_price":24.75}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 16', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product added', - properties: { - currency: 'CAD', - quantity: 1, - value: 24.75, - category: 'cat 1', - id: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - removeExternalId: false, - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.value', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"AddToCart","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"value":24.75,"category":"cat 1","id":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"product","content_name":"","content_category":"cat 1","contents":[{"id":"p-298","quantity":1}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 17', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - contentName: 'all about nutrition', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Purchase","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"order_id":"rudderstackorder1","total":99.99,"revenue":12.24,"shipping":13.99,"tax":20.99,"currency":"INR","contentName":"all about nutrition","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":3,"products[1].price":24.75,"products[1].name":"other product","products[1].sku":"p-299","content_ids":["p-298","p-299"],"content_type":"product","value":12.24,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":3,"item_price":24.75}],"num_items":2,"content_name":"all about nutrition"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 18', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'checkout started', - properties: { - currency: 'CAD', - category: 'clothing', - contentName: 'abc', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 1, - price: 24.75, - name: 'my product 2', - sku: 'p-299', - }, - ], - step: 1, - paymentMethod: 'Visa', - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: 'contentName', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"InitiateCheckout","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","category":"clothing","contentName":"abc","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":1,"products[1].price":24.75,"products[1].name":"my product 2","products[1].sku":"p-299","step":1,"paymentMethod":"Visa","testDimension":true,"testMetric":true,"content_category":"clothing","content_ids":["p-298","p-299"],"content_type":"product","value":0,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":1,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 19', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - dataProcessingOptions: [['LDU'], 1, 1000], - fbc: 'fb.1.1554763741205.AbCdEfGhIjKlMnOpQrStUvWxYz1234567890', - fbp: 'fb.1.1554763741205.234567890', - fb_login_id: 'fb_id', - lead_id: 'lead_id', - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUSage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","fbc":"fb.1.1554763741205.AbCdEfGhIjKlMnOpQrStUvWxYz1234567890","fbp":"fb.1.1554763741205.234567890","lead_id":"lead_id","fb_login_id":"fb_id"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","data_processing_options":["LDU"],"data_processing_options_country":1,"data_processing_options_state":1000,"custom_data":{"additional_bet_index":0,"value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 20', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - dataProcessingOptions: [['LDU'], 1, 1000], - fbc: 'fb.1.1554763741205.AbCdEfGhIjKlMnOpQrStUvWxYz1234567890', - fbp: 'fb.1.1554763741205.234567890', - fb_login_id: 'fb_id', - lead_id: 'lead_id', - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUSage: false, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","fbc":"fb.1.1554763741205.AbCdEfGhIjKlMnOpQrStUvWxYz1234567890","fbp":"fb.1.1554763741205.234567890","lead_id":"lead_id","fb_login_id":"fb_id"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","custom_data":{"additional_bet_index":0,"value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 21', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'products searched', - properties: { - product_id: 'p-298', - quantity: 2, - price: 18.9, - category: 'health', - value: 18.9, - query: 'HDMI cable', - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Search","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"product_id":"p-298","quantity":2,"price":18.9,"category":"health","value":18.9,"query":"HDMI cable","content_ids":["p-298"],"content_category":"health","contents":[{"id":"p-298","quantity":2,"item_price":18.9}],"search_string":"HDMI cable","currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 22', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'products searched', - properties: { - query: 'HDMI cable', - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - testDestination: true, - testEventCode: 'TEST1001', - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Search","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"query":"HDMI cable","content_ids":[],"content_category":"","value":0,"contents":[],"search_string":"HDMI cable","currency":"USD"}}', - ], - test_event_code: 'TEST1001', - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 23', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: 'xyz', - search: 'def', - title: 'ghi', - url: 'jkl', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - standardPageCall: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: 'url', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_ip_address":"0.0.0.0","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"PageView","event_time":1697278611,"event_source_url":"jkl","event_id":"5e10d13a-bf9a-44bf-b884-43a9e591ea71","action_source":"website","custom_data":{"path":"/abc","referrer":"xyz","search":"def","title":"ghi","url":"jkl"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 24', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'track page', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: 'track page', - to: 'PageView', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: 'additional_bet_index', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"PageView","event_time":1697278611,"action_source":"other","custom_data":{"revenue":400,"additional_bet_index":0}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 25', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'my product list', - properties: { - email: 'abc@gmail.com', - quantity: 2, - category: 'cat 1', - list_id: '1234', - filters: [ - { - type: 'department', - value: 'beauty', - }, - { - type: 'price', - value: 'under', - }, - ], - sorts: [ - { - type: 'price', - value: 'desc', - }, - ], - products: [ - { - product_id: '507f1f77bcf86cd799439011', - productDimension: 'My Product Dimension', - productMetric: 'My Product Metric', - position: 10, - }, - { - product_id: '507f1f77bcf86cdef799439011', - productDimension: 'My Product Dimension1', - productMetric: 'My Product Metric1', - position: -10, - }, - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: 'My product list', - to: 'Schedule', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: 'list_id', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Schedule","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"quantity":2,"category":"cat 1","list_id":"1234","filters[0].type":"department","filters[0].value":"beauty","filters[1].type":"price","filters[1].value":"under","sorts[0].type":"price","sorts[0].value":"desc","products[0].product_id":"507f1f77bcf86cd799439011","products[0].productDimension":"My Product Dimension","products[0].productMetric":"My Product Metric","products[0].position":10,"products[1].product_id":"507f1f77bcf86cdef799439011","products[1].productDimension":"My Product Dimension1","products[1].productMetric":"My Product Metric1","products[1].position":-10,"testDimension":true,"testMetric":true}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 26', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: "'event' is required", - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 27', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product added', - properties: { - currency: 'CAD', - quantity: 1, - value: '35.753', - category: 'cat 1', - id: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.value', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"AddToCart","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"value":35.75,"category":"cat 1","id":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"product","content_name":"","content_category":"cat 1","contents":[{"id":"p-298","quantity":1}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 28', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product added', - properties: { - currency: 'CAD', - quantity: 1, - value: '35.7A3', - category: 'cat 1', - id: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.value', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"AddToCart","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"value":35.7,"category":"cat 1","id":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"product","content_name":"","content_category":"cat 1","contents":[{"id":"p-298","quantity":1}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 29', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product added', - properties: { - currency: 'CAD', - quantity: 1, - value: 'ABC', - category: 'cat 1', - id: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.value', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: 'Revenue could not be converted to number', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 30', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - category: ['clothing', 'fishing'], - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Purchase","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"category[0]":"clothing","category[1]":"fishing","order_id":"rudderstackorder1","total":99.99,"revenue":12.24,"shipping":13.99,"tax":20.99,"currency":"INR","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":3,"products[1].price":24.75,"products[1].name":"other product","products[1].sku":"p-299","content_category":"clothing,fishing","content_ids":["p-298","p-299"],"content_type":"product","value":12.24,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":3,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 31', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - category: 100, - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Purchase","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"category":100,"order_id":"rudderstackorder1","total":99.99,"revenue":12.24,"shipping":13.99,"tax":20.99,"currency":"INR","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":3,"products[1].price":24.75,"products[1].name":"other product","products[1].sku":"p-299","content_category":"100","content_ids":["p-298","p-299"],"content_type":"product","value":12.24,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":3,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 32', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - category: { - category1: '1', - }, - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: "'properties.category' must be either be a string or an array", - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 33', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: 'spin_result', - to: 'Schedule', - }, - { - to: 'Schedule', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"Schedule","event_time":1697278611,"action_source":"other","custom_data":{"revenue":400,"additional_bet_index":0}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 34', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2019-08-24T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: 'spin_result', - to: 'Schedule', - }, - { - to: 'Schedule', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: - 'Events must be sent within seven days of their occurrence or up to one minute in the future.', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 35', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - originalTimestamp: '2019-04-16T15:50:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: 'validToken', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: true, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: - 'Events must be sent within seven days of their occurrence or up to one minute in the future.', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 36', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'products searched', - properties: { - query: { - key1: 'HDMI cable', - }, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: "'query' should be in string format only", - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 37', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'products searched', - properties: { - query: 50, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Search","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"query":50,"content_ids":[],"content_category":"","value":0,"contents":[],"search_string":50,"currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 38', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - page: { - url: 'https://theminimstory.com/collections/summer-of-pearls?utm_source=facebook&utm_medium=paidsocial&utm_campaign=carousel&utm_content=ad1-jul&fbclid=IwAR2SsDcjzd_TLZN-e93kxOeGBYO4pQ3AiyeXSheHW5emDeLw8uTvo6lTMPI', - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'checkout started', - properties: { - currency: 'CAD', - category: 'clothing', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 1, - price: 24.75, - name: 'my product 2', - sku: 'p-299', - }, - ], - step: 1, - paymentMethod: 'Visa', - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36","fbc":"fb.1.1697278611693.IwAR2SsDcjzd_TLZN-e93kxOeGBYO4pQ3AiyeXSheHW5emDeLw8uTvo6lTMPI"},"event_name":"InitiateCheckout","event_time":1697278611,"event_source_url":"https://theminimstory.com/collections/summer-of-pearls?utm_source=facebook&utm_medium=paidsocial&utm_campaign=carousel&utm_content=ad1-jul&fbclid=IwAR2SsDcjzd_TLZN-e93kxOeGBYO4pQ3AiyeXSheHW5emDeLw8uTvo6lTMPI","event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","category":"clothing","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":1,"products[1].price":24.75,"products[1].name":"my product 2","products[1].sku":"p-299","step":1,"paymentMethod":"Visa","testDimension":true,"testMetric":true,"content_category":"clothing","content_ids":["p-298","p-299"],"content_type":"product","value":0,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":1,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 39', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - page: { - url: 'https://theminimstory.com/collections/summer-of-pearls?utm_source=facebook&utm_medium=paidsocial&utm_campaign=carousel&utm_content=ad1-jul&fbclid=IwAR2SsDcjzd_TLZN-e93kxOeGBYO4pQ3AiyeXSheHW5emDeLw8uTvo6lTMPI', - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: { - name: 'checkout started', - }, - properties: { - currency: 'CAD', - category: 'clothing', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 1, - price: 24.75, - name: 'my product 2', - sku: 'p-299', - }, - ], - step: 1, - paymentMethod: 'Visa', - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: 'event name should be string', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 40', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - page: { - url: 'url in wrong format', - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'checkout started', - properties: { - currency: 'CAD', - category: 'clothing', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 1, - price: 24.75, - name: 'my product 2', - sku: 'p-299', - }, - ], - step: 1, - paymentMethod: 'Visa', - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"InitiateCheckout","event_time":1697278611,"event_source_url":"url in wrong format","event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","category":"clothing","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":1,"products[1].price":24.75,"products[1].name":"my product 2","products[1].sku":"p-299","step":1,"paymentMethod":"Visa","testDimension":true,"testMetric":true,"content_category":"clothing","content_ids":["p-298","p-299"],"content_type":"product","value":0,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":1,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 41', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product viewed', - properties: { - currency: 'CAD', - quantity: 1, - price: 24.75, - name: 'my product 1', - category: 'clothing', - sku: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - Facebook_Pixel: { - contentType: 'sending dedicated content type for this particular payload', - }, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - removeExternalId: true, - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"price":24.75,"name":"my product 1","category":"clothing","sku":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"sending dedicated content type for this particular payload","content_name":"my product 1","content_category":"clothing","value":24.75,"contents":[{"id":"p-298","quantity":1,"item_price":24.75}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 42', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product viewed', - properties: { - currency: 'CAD', - quantity: 1, - price: 24.75, - value: 18.9, - name: 'my product 1', - category: 'clothing', - sku: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - removeExternalId: true, - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.value', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"price":24.75,"value":18.9,"name":"my product 1","category":"clothing","sku":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"product","content_name":"my product 1","content_category":"clothing","contents":[{"id":"p-298","quantity":1,"item_price":24.75}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 43', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - contentName: 'all about nutrition', - products: { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: "'properties.products' is not sent as an Array", - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 44', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product list viewed', - properties: { - email: 'abc@gmail.com', - quantity: 2, - category: 'cat 1', - list_id: '1234', - contentName: 'nutrition', - value: 18.9, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - productDimension: 'My Product Dimension', - productMetric: 'My Product Metric', - position: 10, - }, - [ - { - product_id: '507f1f77bcf86cdef799439011', - productDimension: 'My Product Dimension1', - productMetric: 'My Product Metric1', - position: -10, - }, - ], - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: "'properties.products[1]' is not an object", - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 45', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'custom', - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - pixelId: 'dummyPixelId', - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: 'Access token not found. Aborting', - statTags: { - errorCategory: 'dataValidation', - errorType: 'configuration', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 46', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'custom', - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: 'Pixel Id not found. Aborting', - statTags: { - errorCategory: 'dataValidation', - errorType: 'configuration', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 47', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - metadata: { - jobId: 12, - }, - destination: { - secretConfig: {}, - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [], - limitedDataUSage: false, - accessToken: 'dummyAccessToken', - testDestination: false, - testEventCode: '', - standardPageCall: false, - blacklistedEvents: [], - whitelistedEvents: [], - eventFilteringOption: 'disable', - removeExternalId: false, - useUpdatedMapping: false, - oneTrustCookieCategories: [], - useNativeSDK: false, - eventDelivery: false, - eventDeliveryTS: 1686748039135, - }, - liveEventsConfig: { - eventDelivery: false, - eventDeliveryTS: 1686748039135, - }, - id: 'destId1', - workspaceId: 'wsp2', - transformations: [], - isConnectionEnabled: true, - isProcessorEnabled: true, - name: 'san-fb_pixel', - enabled: true, - deleted: false, - createdAt: '2023-06-06T13:36:08.579Z', - updatedAt: '2023-06-14T13:07:19.136Z', - revisionId: 'revId2', - secretVersion: 3, - }, - message: { - type: 'page', - sentAt: '2023-10-14T15:46:51.000Z', - userId: 'user@19', - channel: 'web', - context: { - os: { - name: '', - version: '', - }, - app: { - name: 'RudderLabs JavaScript SDK', - version: 'dev-snapshot', - namespace: 'com.rudderlabs.javascript', - }, - page: { - url: 'http://127.0.0.1:8888/', - path: '/', - title: 'Document', - search: '', - tab_url: 'http://127.0.0.1:8888/', - referrer: 'http://127.0.0.1:8888/', - initial_referrer: '$direct', - referring_domain: '127.0.0.1:8888', - initial_referring_domain: '', - }, - locale: 'en-GB', - screen: { - width: 1728, - height: 1117, - density: 2, - innerWidth: 547, - innerHeight: 915, - }, - traits: { - name: false, - source: 'rudderstack', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: 'dev-snapshot', - }, - campaign: {}, - sessionId: 1687769234506, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', - }, - rudderId: '6bbfd003-c074-4ee9-8674-c132ded9ff04', - timestamp: '2023-10-14T15:46:51.000Z', - properties: { - url: 'http://127.0.0.1:8888/', - path: '/', - title: 'Document', - search: '', - tab_url: 'http://127.0.0.1:8888/', - referrer: 'http://127.0.0.1:8888/', - initial_referrer: '$direct', - referring_domain: '127.0.0.1:8888', - initial_referring_domain: '', - }, - receivedAt: '2023-10-14T15:46:51.000Z', - request_ip: '49.206.54.243', - anonymousId: '700ab220-faad-4cdf-8484-63e4c6bce6fe', - integrations: { - All: true, - }, - originalTimestamp: '2023-10-14T15:46:51.000Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=dummyAccessToken`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"72fd46c9ecb386f6747664a3e1d524294a3d7a2c8ae4aeb22b1e578b75093635","client_ip_address":"49.206.54.243","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"},"event_name":"PageView","event_time":1697298411,"event_source_url":"http://127.0.0.1:8888/","action_source":"website","custom_data":{"url":"http://127.0.0.1:8888/","path":"/","title":"Document","search":"","tab_url":"http://127.0.0.1:8888/","referrer":"http://127.0.0.1:8888/","initial_referrer":"$direct","referring_domain":"127.0.0.1:8888","initial_referring_domain":""}}', - ], - }, - }, - files: {}, - userId: '', - }, - metadata: { - jobId: 12, - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 48', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - category: ['clothing', 'fishing'], - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - delivery_category: 'home_delivery', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - delivery_category: 'home_delivery', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Purchase","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"category[0]":"clothing","category[1]":"fishing","order_id":"rudderstackorder1","total":99.99,"revenue":12.24,"shipping":13.99,"tax":20.99,"currency":"INR","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[0].delivery_category":"home_delivery","products[1].quantity":3,"products[1].price":24.75,"products[1].name":"other product","products[1].sku":"p-299","products[1].delivery_category":"home_delivery","content_category":"clothing,fishing","content_ids":["p-298","p-299"],"content_type":"product","value":12.24,"contents":[{"id":"p-298","quantity":1,"item_price":24.75,"delivery_category":"home_delivery"},{"id":"p-299","quantity":3,"item_price":24.75,"delivery_category":"home_delivery"}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 49', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - channel: 'mobile', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: ' aBc@gmail.com ', - address: { - zip: 1234, - }, - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - content_ids: ['prod1', 'prod2'], - }, - timestamp: '2023-10-14T00:00:00.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - removeExternalId: true, - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"spin_result","event_time":1697221800,"action_source":"app","custom_data":{"additional_bet_index":0,"value":400,"content_ids":["prod1","prod2"]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 50', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - channel: 'mobile', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: ' aBc@gmail.com ', - address: { - zip: 1234, - }, - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - contents: [ - { - id: 'prod1', - quantity: 5, - item_price: 55, - }, - ], - }, - timestamp: '2023-10-14T00:00:00.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - removeExternalId: true, - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"spin_result","event_time":1697221800,"action_source":"app","custom_data":{"additional_bet_index":0,"value":400,"contents":[{"id":"prod1","quantity":5,"item_price":55}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: - 'Test 51: properties.content_type is given priority over populating it from categoryToContent mapping.', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - content_type: 'product_group', - category: ['clothing', 'fishing'], - order_id: 'rudderstackorder1', - revenue: 12.24, - currency: 'INR', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5"},"event_name":"Purchase","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"content_type":"product_group","category[0]":"clothing","category[1]":"fishing","order_id":"rudderstackorder1","revenue":12.24,"currency":"INR","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":3,"products[1].price":24.75,"products[1].name":"other product","products[1].sku":"p-299","content_category":"clothing,fishing","content_ids":["p-298","p-299"],"value":12.24,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":3,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, + ...identifyTestData, + ...trackTestData, + ...validationTestData, + ...pageScreenTestData, + ...ecommTestData, + ...configLevelFeaturesTestData, ].map((d) => ({ ...d, mockFns })); diff --git a/test/integrations/destinations/facebook_pixel/processor/ecommTestData.ts b/test/integrations/destinations/facebook_pixel/processor/ecommTestData.ts new file mode 100644 index 0000000000..5d429d297d --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/ecommTestData.ts @@ -0,0 +1,1120 @@ +import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { generateTrackPayload, generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: 'ABC Started', + to: 'InitiateCheckout', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'email', + }, + ], + }, + Enabled: true, +}; + +const commonUserTraits = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', +}; + +const commonPropertiesWithoutProductArray = { + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', +}; + +const commonPropertiesWithProductArray = { + products: [ + { + product_id: '017c6f5d5cf86a4b22432066', + sku: '8732-98', + name: 'Just Another Game', + price: 22, + position: 2, + category: 'Games and Entertainment', + url: 'https://www.myecommercewebsite.com/product', + image_url: 'https://www.myecommercewebsite.com/product/path.jpg', + }, + { + product_id: '89ac6f5d5cf86a4b64eac145', + sku: '1267-01', + name: 'Wrestling Trump Cards', + price: 4, + position: 21, + category: 'Card Games', + }, + ], + category: 'dummy', + quantity: 10, + revenue: 100, + price: 50, + product_id: '12345', + order_id: '23456', +}; +const commonTimestamp = new Date('2023-10-14'); +const commonStatTags = { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'FACEBOOK_PIXEL', + module: 'destination', + implementation: 'native', + feature: 'processor', +}; + +export const ecommTestData: ProcessorTestData[] = [ + { + id: 'facebook_pixel-ecomm-test-1', + name: 'facebook_pixel', + description: + 'Track call : product list viewed event call with properties without product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to ViewContent, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['dummy'], + content_type: 'product_group', + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-2', + name: 'facebook_pixel', + description: 'Track call : product list viewed event call with properties with product array', + successCriteria: + 'It should be internally mapped to ViewContent, with necessary mapping from message.properties and from products array and should be sent to the destination', + scenario: 'ecommerce', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: commonPropertiesWithProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/dummyPixelId/events?access_token=09876', + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + 'products[0].product_id': '017c6f5d5cf86a4b22432066', + 'products[0].sku': '8732-98', + 'products[0].name': 'Just Another Game', + 'products[0].price': 22, + 'products[0].position': 2, + 'products[0].category': 'Games and Entertainment', + 'products[0].url': 'https://www.myecommercewebsite.com/product', + 'products[0].image_url': + 'https://www.myecommercewebsite.com/product/path.jpg', + 'products[1].product_id': '89ac6f5d5cf86a4b64eac145', + 'products[1].sku': '1267-01', + 'products[1].name': 'Wrestling Trump Cards', + 'products[1].price': 4, + 'products[1].position': 21, + 'products[1].category': 'Card Games', + category: 'dummy', + quantity: 10, + revenue: 100, + price: 50, + product_id: '12345', + order_id: '23456', + content_ids: ['017c6f5d5cf86a4b22432066', '89ac6f5d5cf86a4b64eac145'], + content_type: 'product', + contents: [ + { + id: '017c6f5d5cf86a4b22432066', + quantity: 10, + item_price: 22, + }, + { + id: '89ac6f5d5cf86a4b64eac145', + quantity: 10, + item_price: 4, + }, + ], + content_category: 'dummy', + value: 0, + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-3', + name: 'facebook_pixel', + description: 'Track call : product viewed event call with properties without product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to ViewContent, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product viewed', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 0, + product_id: '12345', + content_ids: ['12345'], + content_type: 'product', + content_name: '', + content_category: 'dummy', + currency: 'USD', + contents: [ + { + id: '12345', + quantity: 10, + }, + ], + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-4', + name: 'facebook_pixel', + description: 'Track call : product added event call with properties without product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to AddToCart, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product added', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'AddToCart', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 0, + product_id: '12345', + content_ids: ['12345'], + content_type: 'product', + content_name: '', + content_category: 'dummy', + currency: 'USD', + contents: [ + { + id: '12345', + quantity: 10, + }, + ], + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-5', + name: 'facebook_pixel', + description: 'Track call : order completed event call with properties without product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to purchase, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'order completed', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'Purchase', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 0, + product_id: '12345', + content_category: 'dummy', + content_ids: [], + content_type: 'product', + currency: 'USD', + contents: [], + num_items: 0, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-6', + name: 'facebook_pixel', + description: 'Track call : order completed event call with properties with product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to purchase, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'order completed', + properties: commonPropertiesWithProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/dummyPixelId/events?access_token=09876', + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'Purchase', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + 'products[0].product_id': '017c6f5d5cf86a4b22432066', + 'products[0].sku': '8732-98', + 'products[0].name': 'Just Another Game', + 'products[0].price': 22, + 'products[0].position': 2, + 'products[0].category': 'Games and Entertainment', + 'products[0].url': 'https://www.myecommercewebsite.com/product', + 'products[0].image_url': + 'https://www.myecommercewebsite.com/product/path.jpg', + 'products[1].product_id': '89ac6f5d5cf86a4b64eac145', + 'products[1].sku': '1267-01', + 'products[1].name': 'Wrestling Trump Cards', + 'products[1].price': 4, + 'products[1].position': 21, + 'products[1].category': 'Card Games', + category: 'dummy', + quantity: 10, + revenue: 100, + price: 50, + product_id: '12345', + order_id: '23456', + content_category: 'dummy', + content_ids: ['017c6f5d5cf86a4b22432066', '89ac6f5d5cf86a4b64eac145'], + content_type: 'product', + currency: 'USD', + value: 100, + contents: [ + { + id: '017c6f5d5cf86a4b22432066', + quantity: 10, + item_price: 22, + }, + { + id: '89ac6f5d5cf86a4b64eac145', + quantity: 10, + item_price: 4, + }, + ], + num_items: 2, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-7', + name: 'facebook_pixel', + description: 'Track call : products searched event call with properties', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to Search, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'products searched', + properties: { ...commonPropertiesWithoutProductArray, query: 'dummy' }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'Search', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + query: 'dummy', + content_ids: ['12345'], + content_category: 'dummy', + contents: [ + { + id: '12345', + quantity: 10, + }, + ], + search_string: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-8', + name: 'facebook_pixel', + description: + 'Track call : products searched event call with properties and unsupported query type', + scenario: 'ecommerce', + successCriteria: + 'Error : It should throw an error as the query is not a string or an array of strings', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'products searched', + properties: { ...commonPropertiesWithoutProductArray, query: ['dummy'] }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: "'query' should be in string format only", + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-9', + name: 'facebook_pixel', + description: 'Track call : checkout started event call with properties without product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to InitiateCheckout, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'checkout started', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'InitiateCheckout', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 0, + product_id: '12345', + content_category: 'dummy', + content_ids: [], + content_type: 'product', + currency: 'USD', + contents: [], + num_items: 0, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-10', + name: 'facebook_pixel', + description: 'Track call : checkout started event call with properties with product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to InitiateCheckout, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'checkout started', + properties: commonPropertiesWithProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/dummyPixelId/events?access_token=09876', + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'InitiateCheckout', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + 'products[0].product_id': '017c6f5d5cf86a4b22432066', + 'products[0].sku': '8732-98', + 'products[0].name': 'Just Another Game', + 'products[0].price': 22, + 'products[0].position': 2, + 'products[0].category': 'Games and Entertainment', + 'products[0].url': 'https://www.myecommercewebsite.com/product', + 'products[0].image_url': + 'https://www.myecommercewebsite.com/product/path.jpg', + 'products[1].product_id': '89ac6f5d5cf86a4b64eac145', + 'products[1].sku': '1267-01', + 'products[1].name': 'Wrestling Trump Cards', + 'products[1].price': 4, + 'products[1].position': 21, + 'products[1].category': 'Card Games', + category: 'dummy', + quantity: 10, + revenue: 100, + price: 50, + product_id: '12345', + order_id: '23456', + content_category: 'dummy', + content_ids: ['017c6f5d5cf86a4b22432066', '89ac6f5d5cf86a4b64eac145'], + content_type: 'product', + currency: 'USD', + value: 100, + contents: [ + { + id: '017c6f5d5cf86a4b22432066', + quantity: 10, + item_price: 22, + }, + { + id: '89ac6f5d5cf86a4b64eac145', + quantity: 10, + item_price: 4, + }, + ], + num_items: 2, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-11', + name: 'facebook_pixel', + description: + 'Track call : custom event ABC Started event call with properties with product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to InitiateCheckout, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'ABC Started', + properties: commonPropertiesWithProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'InitiateCheckout', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + 'products[0].product_id': '017c6f5d5cf86a4b22432066', + 'products[0].sku': '8732-98', + 'products[0].name': 'Just Another Game', + 'products[0].price': 22, + 'products[0].position': 2, + 'products[0].category': 'Games and Entertainment', + 'products[0].url': 'https://www.myecommercewebsite.com/product', + 'products[0].image_url': + 'https://www.myecommercewebsite.com/product/path.jpg', + 'products[1].product_id': '89ac6f5d5cf86a4b64eac145', + 'products[1].sku': '1267-01', + 'products[1].name': 'Wrestling Trump Cards', + 'products[1].price': 4, + 'products[1].position': 21, + 'products[1].category': 'Card Games', + category: 'dummy', + quantity: 10, + revenue: 100, + price: 50, + product_id: '12345', + order_id: '23456', + content_category: 'dummy', + content_ids: ['017c6f5d5cf86a4b22432066', '89ac6f5d5cf86a4b64eac145'], + content_type: 'product', + currency: 'USD', + value: 100, + contents: [ + { + id: '017c6f5d5cf86a4b22432066', + quantity: 10, + item_price: 22, + }, + { + id: '89ac6f5d5cf86a4b64eac145', + quantity: 10, + item_price: 4, + }, + ], + num_items: 2, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-12', + name: 'facebook_pixel', + description: + 'Track call : product list viewed event call with properties without product array and revenue as string', + scenario: 'ecommerce', + successCriteria: + 'Error : It should throw an error as revenue is not a number and should not be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...commonPropertiesWithoutProductArray, value: '$20' }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Revenue could not be converted to number', + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/processor/identifyTestData.ts b/test/integrations/destinations/facebook_pixel/processor/identifyTestData.ts new file mode 100644 index 0000000000..d315b03cea --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/identifyTestData.ts @@ -0,0 +1,209 @@ +import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { Destination } from '../../../../../src/types'; +import { generateMetadata, transformResultBuilder, overrideDestination } from '../../../testUtils'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: true, + whitelistPiiProperties: [ + { + whitelistPiiProperties: '', + }, + ], + }, + Enabled: true, +}; +const commonMessage = { + channel: 'web', + context: { + traits: { + name: 'Rudder Test', + email: 'abc@gmail.com', + firstname: 'Rudder', + lastname: 'Test', + phone: 9000000000, + gender: 'female', + }, + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + properties: { + plan: 'standard plan', + name: 'rudder test', + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + originalTimestamp: '2023-10-14T15:46:51.693229+05:30', + anonymousId: '00000000000000000000000000', + userId: '123456', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', +}; +const commonStatTags = { + errorCategory: 'dataValidation', + errorType: 'configuration', + destType: 'FACEBOOK_PIXEL', + module: 'destination', + implementation: 'native', + feature: 'processor', +}; + +export const identifyTestData: ProcessorTestData[] = [ + { + id: 'fbPixel-identify-test-1', + name: 'facebook_pixel', + description: '[Error]: Check if advancedMapping configuration is enabled', + scenario: 'Framework', + successCriteria: + 'Response should contain error message and status code should be 400, we are sending identify event with advancedMapping disabled', + module: 'destination', + feature: 'processor', + version: 'v0', + input: { + request: { + body: [ + { + message: commonMessage, + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { advancedMapping: false }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'For identify events, "Advanced Mapping" configuration must be enabled on the RudderStack dashboard', + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'fbPixel-identify-test-2', + name: 'facebook_pixel', + description: 'Identify event happy flow : without integrations object hashed true', + scenario: 'Business', + successCriteria: + ' Response should contain status code 200 and body should contain unhashed user traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: commonMessage, + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + ph: '593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579', + ge: '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + ln: '532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25', + fn: '2c2ccf28d806f6f9a34b67aa874d2113b7ac1444f1a4092541b8b75b84771747', + client_ip_address: '0.0.0.0', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + }, + event_name: 'identify', + event_time: 1697278611, + event_id: '84e26acc-56a5-4835-8233-591137fca468', + action_source: 'website', + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/processor/pageScreenTestData.ts b/test/integrations/destinations/facebook_pixel/processor/pageScreenTestData.ts new file mode 100644 index 0000000000..dee772522a --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/pageScreenTestData.ts @@ -0,0 +1,721 @@ +import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { + generateSimplifiedPageOrScreenPayload, + overrideDestination, + generateMetadata, + transformResultBuilder, +} from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + blacklistPiiProperties: [ + { + blacklistPiiProperties: 'phone', + blacklistPiiHash: true, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'email', + }, + ], + }, + Enabled: true, +}; +const commonMessage = { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + type: 'page', + messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', + timestamp: '2023-10-14T15:46:51.693229+05:30', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + integrations: { + All: true, + }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', +}; + +const commonPageMessage = { ...commonMessage, type: 'page' }; + +const commonScreenMessage = { ...commonMessage, type: 'screen' }; + +const commonStatTags = { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'FACEBOOK_PIXEL', + module: 'destination', + implementation: 'native', + feature: 'processor', +}; + +export const pageScreenTestData: ProcessorTestData[] = [ + { + id: 'facebook_pixel-page-test-1', + name: 'facebook_pixel', + description: + 'Page call : Happy flow without standard page switched on and with name and properties', + scenario: 'Page', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + email: 'abc@example.com', + }, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + name: 'ApplicationLoaded', + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'page', + ), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + em: '9eceb13483d7f187ec014fd6d4854d1420cfc634328af85f51d0323ba8622e21', + }, + event_name: 'Viewed page ApplicationLoaded', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-page-test-2', + name: 'facebook_pixel', + description: 'Page call : with standard page switched on and no properties and no name', + scenario: 'Page', + successCriteria: + 'Response should contain error message and status code should be 400, as we are not sending any other properties other than standard page properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + email: 'abc@example.com', + }, + }, + properties: {}, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'page', + ), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { standardPageCall: true }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + "After excluding opt_out,event_id,action_source, no fields are present in 'properties' for a standard event", + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-page-test-3', + name: 'facebook_pixel', + description: 'Page call : with standard page switched on and properties', + scenario: 'Page', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + email: 'abc@example.com', + }, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'page', + ), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { standardPageCall: true }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + em: '9eceb13483d7f187ec014fd6d4854d1420cfc634328af85f51d0323ba8622e21', + }, + event_name: 'PageView', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-page-test-4', + name: 'facebook_pixel', + description: 'Page call : with standard page switched off and with properties but no page name', + scenario: 'Page', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: {}, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'page', + ), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + }, + event_name: 'PageView', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-screen-test-1', + name: 'facebook_pixel', + description: + 'Screen call : Happy flow without standard page switched on and with name and properties', + scenario: 'Screen', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: {}, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + anonymousId: '9c6bd77ea9da3e68', + name: 'ApplicationLoaded', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'screen', + ), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + }, + event_name: 'PageView', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-screen-test-2', + name: 'facebook_pixel', + description: 'Screen call : with standard page switched on and no properties and no name', + scenario: 'Screen', + successCriteria: + 'Response should contain error message and status code should be 400, as we are not sending any other properties other than standard page properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: {}, + }, + properties: {}, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'screen', + ), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { standardPageCall: true }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + "After excluding opt_out,event_id,action_source, no fields are present in 'properties' for a standard event", + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-screen-test-3', + name: 'facebook_pixel', + description: 'Screen call : with standard page switched on and properties', + scenario: 'Screen', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: {}, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'screen', + ), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { standardPageCall: true }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + }, + event_name: 'PageView', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-screen-test-4', + name: 'facebook_pixel', + description: + 'Screen call : with standard page switched off and with properties but no page name', + scenario: 'Screen', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: {}, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'screen', + ), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + }, + event_name: 'PageView', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/processor/trackTestData.ts b/test/integrations/destinations/facebook_pixel/processor/trackTestData.ts new file mode 100644 index 0000000000..9fd65945c4 --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/trackTestData.ts @@ -0,0 +1,208 @@ +import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { generateMetadata, generateTrackPayload, transformResultBuilder } from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'email', + }, + ], + }, + Enabled: true, +}; + +const commonUserTraits = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', +}; + +const commonUserProperties = { + revenue: 400, + additional_bet_index: 0, + email: 'abc@gmail.com', +}; + +const commonTimestamp = new Date('2023-10-14'); + +export const trackTestData: ProcessorTestData[] = [ + { + id: 'fbPixel-track-test-1', + name: 'facebook_pixel', + description: 'Track call : custom event calls with simple user properties and traits', + scenario: 'Business', + successCriteria: + 'event not respecting the internal mapping and as well as UI mapping should be considered as a custom event and should be sent as it is', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'spin_result', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + additional_bet_index: 0, + email: 'abc@gmail.com', + value: 400, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'fbPixel-track-test-2', + name: 'facebook_pixel', + description: + 'Track call : other standard type event calls with simple user properties and traits', + scenario: 'Business', + successCriteria: + 'event not respecting the internal mapping and as well as UI mapping but falls under other standard events, should be considered as a simple track event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'AddToWishlist', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'AddToWishlist', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + additional_bet_index: 0, + email: 'abc@gmail.com', + value: 400, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/processor/validationTestData.ts b/test/integrations/destinations/facebook_pixel/processor/validationTestData.ts new file mode 100644 index 0000000000..8e24801464 --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/validationTestData.ts @@ -0,0 +1,373 @@ +import { Destination } from '../../../../../src/types'; +import { + generateMetadata, + generateSimplifiedGroupPayload, + generateSimplifiedTrackPayload, + generateTrackPayload, + overrideDestination, +} from '../../../testUtils'; +const commonTimestamp = new Date('2023-10-12'); +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + limitedDataUsage: true, + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + removeExternalId: true, + valueFieldIdentifier: '', + advancedMapping: true, + whitelistPiiProperties: [ + { + whitelistPiiProperties: '', + }, + ], + }, + Enabled: true, +}; + +const commonStatTags = { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'FACEBOOK_PIXEL', + module: 'destination', + implementation: 'native', + feature: 'processor', +}; + +export const validationTestData = [ + { + id: 'fbPixel-validation-test-1', + name: 'facebook_pixel', + description: '[Error]: Check for unsupported message type', + scenario: 'Framework', + successCriteria: + 'Response should contain error message and status code should be 400, as we are sending a message type which is not supported by facebook pixel destination and the error message should be Event type random is not supported', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(commonDestination, { + accessToken: '09876', + pixelId: 'dummyPixelId', + }), + message: generateSimplifiedGroupPayload({ + userId: 'user123', + groupId: 'XUepkK', + traits: { + subscribe: true, + }, + context: { + traits: { + email: 'test@rudderstack.com', + phone: '+12 345 678 900', + consent: ['email'], + }, + }, + timestamp: '2023-10-14T00:21:34.208Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Message type group not supported', + statTags: commonStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-2', + name: 'facebook_pixel', + description: + 'Track call : error in instrumentation as pixel id is not mentioned in destination object', + scenario: 'Business', + successCriteria: + 'Error: Pixel Id not found. Aborting, as we are sending an event without pixel id and the status code should be 400', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: { + revenue: 400, + additional_bet_index: 0, + }, + context: { + traits: { + email: 'abc@gmail.com', + }, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Pixel Id not found. Aborting', + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + errorType: 'configuration', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-3', + name: 'facebook_pixel', + description: 'Track call : custom event calls with simple user properties and traits', + scenario: 'Business', + successCriteria: + 'event not respecting the internal mapping and as well as UI mapping should be considered as a custom event and should be sent as it is', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: { + revenue: 400, + additional_bet_index: 0, + }, + context: { + traits: { + email: 'abc@gmail.com', + }, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + pixelId: 'dummyPixelId', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Access token not found. Aborting', + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + errorType: 'configuration', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-3', + name: 'facebook_pixel', + description: '[Error]: validate event date and time', + scenario: 'Framework + business', + successCriteria: + 'Response should contain error message and status code should be 400, as we are sending an event which is older than 7 days and the error message should be Events must be sent within seven days of their occurrence or up to one minute in the future.', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + email: 'test@rudderstack.com', + phone: '9112340375', + plan_details: { + plan_type: 'gold', + duration: '3 months', + }, + }, + }, + properties: { + revenue: 400, + additional_bet_index: 0, + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }), + destination: overrideDestination(commonDestination, { + accessToken: '09876', + pixelId: 'dummyPixelId', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'Events must be sent within seven days of their occurrence or up to one minute in the future.', + statTags: commonStatTags, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-4', + name: 'facebook_pixel', + description: + 'Track call : error in instrumentation as event name is not mentioned in track call', + scenario: 'Business', + successCriteria: + 'event not respecting the internal mapping and as well as UI mapping should be considered as a custom event and should be sent as it is', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + properties: { + revenue: 400, + additional_bet_index: 0, + }, + }, + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + pixelId: 'dummyPixelId', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: "'event' is required", + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-4', + name: 'facebook_pixel', + description: 'Track call : error in instrumentation as event name is not a string', + scenario: 'Business', + successCriteria: + 'Error message should be event name should be string and status code should be 400, as we are sending an event which is not a string', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 1234, + properties: { + revenue: 400, + additional_bet_index: 0, + }, + }, + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + pixelId: 'dummyPixelId', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'event name should be string', + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/testUtils.ts b/test/integrations/testUtils.ts index 7aede97cf7..13a76702f9 100644 --- a/test/integrations/testUtils.ts +++ b/test/integrations/testUtils.ts @@ -216,6 +216,7 @@ export const generateTrackPayload: any = (parametersOverride: any) => { campaign: {}, userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + dataProcessingOptions: parametersOverride.context.dataProcessingOptions, }), rudderId: parametersOverride.rudderId || generateAlphanumericId(36), messageId: parametersOverride.messageId || generateAlphanumericId(36), @@ -310,6 +311,7 @@ export const generateSimplifiedPageOrScreenPayload: any = ( userId: parametersOverride.userId || 'default-userId', type: eventType || 'page', event: parametersOverride.event, + name: parametersOverride.name, properties: parametersOverride.properties, integrations: parametersOverride.integrations, rudderId: parametersOverride.rudderId || generateAlphanumericId(36), From a31d93cb239b3a44b9b4ff2a9d32bd5387ff7424 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Thu, 14 Mar 2024 13:17:34 +0530 Subject: [PATCH 036/240] chore: move pardot to new component structure (#3171) * chore: move pardot to new component structure --- src/v0/destinations/pardot/networkHandler.js | 13 + .../pardot/dataDelivery/business.ts | 372 ++++++++++++++++++ .../pardot/dataDelivery/constant.ts | 27 ++ .../destinations/pardot/dataDelivery/data.ts | 10 + .../destinations/pardot/dataDelivery/oauth.ts | 53 +++ .../destinations/pardot/dataDelivery/other.ts | 205 ++++++++++ .../destinations/pardot/network.ts | 143 +++---- 7 files changed, 736 insertions(+), 87 deletions(-) create mode 100644 test/integrations/destinations/pardot/dataDelivery/business.ts create mode 100644 test/integrations/destinations/pardot/dataDelivery/constant.ts create mode 100644 test/integrations/destinations/pardot/dataDelivery/data.ts create mode 100644 test/integrations/destinations/pardot/dataDelivery/oauth.ts create mode 100644 test/integrations/destinations/pardot/dataDelivery/other.ts diff --git a/src/v0/destinations/pardot/networkHandler.js b/src/v0/destinations/pardot/networkHandler.js index edf713ce97..60d2f7ee23 100644 --- a/src/v0/destinations/pardot/networkHandler.js +++ b/src/v0/destinations/pardot/networkHandler.js @@ -46,6 +46,19 @@ const getStatus = (code) => { const pardotRespHandler = (destResponse, stageMsg) => { const { status, response } = destResponse; const respAttributes = response['@attributes']; + + // to handle errors like service unavilable, wrong url, no response + if (!respAttributes) { + throw new NetworkError( + `${JSON.stringify(response)} ${stageMsg}`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + response, + ); + } + const { stat, err_code: errorCode } = respAttributes; if (isHttpStatusSuccess(status) && stat !== 'fail') { diff --git a/test/integrations/destinations/pardot/dataDelivery/business.ts b/test/integrations/destinations/pardot/dataDelivery/business.ts new file mode 100644 index 0000000000..f6baefbc8f --- /dev/null +++ b/test/integrations/destinations/pardot/dataDelivery/business.ts @@ -0,0 +1,372 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; +import { commonRequestParameters, retryStatTags } from './constant'; + +const pardotResponseRogerEmail = { + '@attributes': { stat: 'ok', version: 1 }, + prospect: { + id: 123435, + campaign_id: 42213, + salutation: null, + first_name: 'Roger_12', + last_name: 'Federer_12', + email: 'Roger_12@federer.io', + password: null, + company: null, + website: 'https://rudderstack.com', + job_title: null, + department: null, + country: 'AU', + address_one: null, + address_two: null, + city: null, + state: null, + territory: null, + zip: null, + phone: null, + fax: null, + source: null, + annual_revenue: null, + employees: null, + industry: null, + years_in_business: null, + comments: null, + notes: null, + score: 14, + grade: null, + last_activity_at: null, + recent_interaction: 'Never active.', + crm_lead_fid: '00Q6r000002LKhTPVR', + crm_contact_fid: null, + crm_owner_fid: '00G2v000004WYXaEAO', + crm_account_fid: null, + salesforce_fid: '00Q6r000002LKhTPVR', + crm_last_sync: '2022-01-21 18:47:37', + crm_url: 'https://testcompany.my.salesforce.com/00Q6r000002LKhTPVR', + is_do_not_email: null, + is_do_not_call: null, + opted_out: null, + is_reviewed: 1, + is_starred: null, + created_at: '2022-01-21 18:21:46', + updated_at: '2022-01-21 18:48:41', + campaign: { id: 42113, name: 'Test', crm_fid: '7012y000000MNOCLL4' }, + assigned_to: { + user: { + id: 38443703, + email: 'test_rudderstack@testcompany.com', + first_name: 'Rudderstack', + last_name: 'User', + job_title: null, + role: 'Administrator', + account: 489853, + created_at: '2021-02-26 06:25:17', + updated_at: '2021-02-26 06:25:17', + }, + }, + Are_you_shipping_large_fragile_or_bulky_items: false, + Calendly: false, + Country_Code: 'AU', + Currency: 'AUD', + Inventory_or_Warehouse_Management_System: false, + Lead_Status: 'New', + Marketing_Stage: 'SAL', + Record_Type_ID: 'TestCompany Lead', + profile: { + id: 304, + name: 'Default', + profile_criteria: [ + { id: 1500, name: 'Shipping Volume', matches: 'Unknown' }, + { id: 1502, name: 'Industry', matches: 'Unknown' }, + { id: 1506, name: 'Job Title', matches: 'Unknown' }, + { id: 1508, name: 'Department', matches: 'Unknown' }, + ], + }, + visitors: null, + visitor_activities: null, + lists: null, + }, +}; + +const pardotResponseWalterEmail = { + '@attributes': { stat: 'ok', version: 1 }, + prospect: { + id: 123435, + campaign_id: 42213, + salutation: null, + first_name: 'Roger_12', + last_name: 'Federer_12', + email: 'Roger_12@waltair.io', + password: null, + company: null, + website: 'https://rudderstack.com', + job_title: null, + department: null, + country: 'AU', + address_one: null, + address_two: null, + city: null, + state: null, + territory: null, + zip: null, + phone: null, + fax: null, + source: null, + annual_revenue: null, + employees: null, + industry: null, + years_in_business: null, + comments: null, + notes: null, + score: 14, + grade: null, + last_activity_at: null, + recent_interaction: 'Never active.', + crm_lead_fid: null, + crm_contact_fid: null, + crm_owner_fid: '00G2v000004WYXaEAO', + crm_account_fid: null, + salesforce_fid: null, + crm_last_sync: null, + crm_url: null, + is_do_not_email: null, + is_do_not_call: null, + opted_out: null, + is_reviewed: 1, + is_starred: null, + created_at: '2022-01-21 18:21:46', + updated_at: '2022-01-21 18:48:41', + campaign: { id: 42113, name: 'Test', crm_fid: '7012y000000MNOCLL4' }, + assigned_to: { + user: { + id: 38443703, + email: 'test_rudderstack@testcompany.com', + first_name: 'Rudderstack', + last_name: 'User', + job_title: null, + role: 'Administrator', + account: 489853, + created_at: '2021-02-26 06:25:17', + updated_at: '2021-02-26 06:25:17', + }, + }, + Are_you_shipping_large_fragile_or_bulky_items: false, + Calendly: false, + Country_Code: 'AU', + Currency: 'AUD', + Inventory_or_Warehouse_Management_System: false, + Lead_Status: 'New', + Marketing_Stage: 'SAL', + Record_Type_ID: 'TestCompany Lead', + profile: { + id: 304, + name: 'Default', + profile_criteria: [ + { id: 1500, name: 'Shipping Volume', matches: 'Unknown' }, + { id: 1502, name: 'Industry', matches: 'Unknown' }, + { id: 1506, name: 'Job Title', matches: 'Unknown' }, + { id: 1508, name: 'Department', matches: 'Unknown' }, + ], + }, + visitors: null, + visitor_activities: null, + lists: null, + }, +}; + +export const businessV0TestScenarios = [ + { + id: 'pardot_v0_bussiness_scenario_1', + name: 'pardot', + description: '[Proxy v0 API] :: pardot email upsert', + successCriteria: 'Proper response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: + 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/Roger_12@waltair.io', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 201, + message: 'Request Processed Successfully', + destinationResponse: { + response: pardotResponseWalterEmail, + status: 201, + }, + }, + }, + }, + }, + }, + { + id: 'pardot_v0_bussiness_scenario_2', + name: 'pardot', + description: '[Proxy v0 API] :: pardot fid type upsert', + successCriteria: 'Proper response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/fid/00Q6r000002LKhTPVR', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + destinationResponse: { + response: pardotResponseRogerEmail, + status: 200, + }, + }, + }, + }, + }, + }, +]; + +export const businessV1TestScenarios: ProxyV1TestData[] = [ + { + id: 'pardot_v1_bussiness_scenario_1', + name: 'pardot', + description: '[Proxy v1 API] :: pardot email type upsert', + successCriteria: 'Proper response from destination is received', + scenario: 'business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: + 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/Roger_12@waltair.io', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 201, + message: 'Request Processed Successfully', + response: [ + { + statusCode: 201, + metadata: generateMetadata(1), + error: JSON.stringify(pardotResponseWalterEmail), + }, + ], + }, + }, + }, + }, + }, + { + id: 'pardot_v1_bussiness_scenario_2', + name: 'pardot', + description: '[Proxy v1 API] :: pardot fid type upsert', + successCriteria: 'Proper response from destination is received', + scenario: 'business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/fid/00Q6r000002LKhTPVR', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: JSON.stringify(pardotResponseRogerEmail), + }, + ], + }, + }, + }, + }, + }, + { + id: 'pardot_v1_Business_scenario_2', + name: 'pardot', + description: '[Proxy v1 API] :: Response with other retryable codes', + successCriteria: 'the proxy should return 500 with retryable tag', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: + 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/rolex_waltair@test.com', + params: { + destination: 'pardot', + }, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + 'Unable to verify Salesforce connector during Pardot response transformation', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: retryStatTags, + message: 'Unable to verify Salesforce connector during Pardot response transformation', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/pardot/dataDelivery/constant.ts b/test/integrations/destinations/pardot/dataDelivery/constant.ts new file mode 100644 index 0000000000..97e456998e --- /dev/null +++ b/test/integrations/destinations/pardot/dataDelivery/constant.ts @@ -0,0 +1,27 @@ +export const retryStatTags = { + destType: 'PARDOT', + errorCategory: 'network', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', +}; +const commonHeaders = { + Authorization: 'Bearer myToken', + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + 'Pardot-Business-Unit-Id': '0Uv2v000000k9tHCAQ', + 'User-Agent': 'RudderLabs', +}; + +export const commonRequestParameters = { + headers: commonHeaders, + FORM: { + first_name: 'Roger12', + last_name: 'Federer12', + website: 'https://rudderstack.com', + score: 14, + campaign_id: 42213, + }, +}; diff --git a/test/integrations/destinations/pardot/dataDelivery/data.ts b/test/integrations/destinations/pardot/dataDelivery/data.ts new file mode 100644 index 0000000000..752ef22cb1 --- /dev/null +++ b/test/integrations/destinations/pardot/dataDelivery/data.ts @@ -0,0 +1,10 @@ +import { businessV0TestScenarios, businessV1TestScenarios } from './business'; +import { v1OauthScenarios } from './oauth'; +import { otherScenariosV1 } from './other'; + +export const data = [ + ...v1OauthScenarios, + ...businessV1TestScenarios, + ...businessV0TestScenarios, + ...otherScenariosV1, +]; diff --git a/test/integrations/destinations/pardot/dataDelivery/oauth.ts b/test/integrations/destinations/pardot/dataDelivery/oauth.ts new file mode 100644 index 0000000000..4a0116a951 --- /dev/null +++ b/test/integrations/destinations/pardot/dataDelivery/oauth.ts @@ -0,0 +1,53 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; +import { commonRequestParameters, retryStatTags } from './constant'; + +export const v1OauthScenarios: ProxyV1TestData[] = [ + { + id: 'pardot_v1_oauth_scenario_1', + name: 'pardot', + description: + '[Proxy v1 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: + 'Since the error from the destination is 401 - the proxy should return 500 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: + 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/rolex_waltair@mywebsite.io', + params: { + destination: 'pardot', + }, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + response: [ + { + error: + 'access_token is invalid, unknown, or malformed: Inactive token during Pardot response transformation', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: retryStatTags, + authErrorCategory: 'REFRESH_TOKEN', + message: + 'access_token is invalid, unknown, or malformed: Inactive token during Pardot response transformation', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/pardot/dataDelivery/other.ts b/test/integrations/destinations/pardot/dataDelivery/other.ts new file mode 100644 index 0000000000..b7454e691c --- /dev/null +++ b/test/integrations/destinations/pardot/dataDelivery/other.ts @@ -0,0 +1,205 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; + +const expectedStatTags = { + destType: 'PARDOT', + errorCategory: 'network', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', +}; + +export const otherScenariosV1: ProxyV1TestData[] = [ + { + id: 'pardot_v1_other_scenario_1', + name: 'pardot', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}} during Pardot response transformation', + statusCode: 503, + metadata: generateMetadata(1), + }, + ], + statTags: expectedStatTags, + status: 503, + message: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}} during Pardot response transformation', + }, + }, + }, + }, + }, + { + id: 'pardot_v1_other_scenario_2', + name: 'pardot', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error" during Pardot response transformation', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: expectedStatTags, + status: 500, + message: '"Internal Server Error" during Pardot response transformation', + }, + }, + }, + }, + }, + { + id: 'pardot_v1_other_scenario_3', + name: 'pardot', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout" during Pardot response transformation', + statusCode: 504, + metadata: generateMetadata(1), + }, + ], + statTags: expectedStatTags, + status: 504, + message: '"Gateway Timeout" during Pardot response transformation', + }, + }, + }, + }, + }, + { + id: 'pardot_v1_other_scenario_4', + name: 'pardot', + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_response', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"" during Pardot response transformation', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: expectedStatTags, + status: 500, + message: '"" during Pardot response transformation', + }, + }, + }, + }, + }, + { + id: 'pardot_v1_other_scenario_5', + name: 'pardot', + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"" during Pardot response transformation', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: expectedStatTags, + status: 500, + message: '"" during Pardot response transformation', + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/pardot/network.ts b/test/integrations/destinations/pardot/network.ts index bbbe0d70f9..9493aab01f 100644 --- a/test/integrations/destinations/pardot/network.ts +++ b/test/integrations/destinations/pardot/network.ts @@ -1,25 +1,9 @@ -import { enhanceRequestOptions, getFormData } from '../../../../src/adapters/network'; +import { getFormData } from '../../../../src/adapters/network'; export const networkCallsData = [ - // 2nd proxy test-case { httpReq: { url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/Roger_12@waltair.io', - data: getFormData({ - first_name: 'Roger_12', - last_name: 'Federer_12', - website: 'https://rudderstack.com', - score: 14, - campaign_id: 42213, - format: 'json', - }).toString(), - params: { destination: 'pardot' }, - headers: { - Authorization: 'Bearer myToken', - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - 'Pardot-Business-Unit-Id': '0Uv2v000000k9tHCAQ', - 'User-Agent': 'RudderLabs', - }, method: 'POST', }, httpRes: { @@ -135,59 +119,9 @@ export const networkCallsData = [ statusText: 'Created', }, }, - // 4th proxy test-case - { - httpReq: { - url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/rolex_waltair@mywebsite.io', - data: getFormData({ - first_name: 'Rolex', - last_name: 'Waltair', - website: 'https://rudderstack.com', - score: 15, - campaign_id: 42213, - format: 'json', - }).toString(), - params: { destination: 'pardot' }, - headers: { - Authorization: 'Bearer myExpiredToken', - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - 'Pardot-Business-Unit-Id': '0Uv2v000000k9tHCAQ', - 'User-Agent': 'RudderLabs', - }, - method: 'POST', - }, - httpRes: { - data: { - '@attributes': { - stat: 'fail', - version: 1, - err_code: 184, - }, - err: 'access_token is invalid, unknown, or malformed: Inactive token', - }, - status: 401, - statusText: 'Unauthorized', - }, - }, - // 1st proxy test-case { httpReq: { - url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/id/123435', - data: getFormData({ - first_name: 'Roger12', - last_name: 'Federer12', - website: 'https://rudderstack.com', - score: 14, - campaign_id: 42213, - format: 'json', - }).toString(), - params: { destination: 'pardot' }, - headers: { - Authorization: 'Bearer myToken', - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - 'Pardot-Business-Unit-Id': '0Uv2v000000k9tHCAQ', - 'User-Agent': 'RudderLabs', - }, + url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/fid/00Q6r000002LKhTPVR', method: 'POST', }, httpRes: { @@ -200,9 +134,9 @@ export const networkCallsData = [ id: 123435, campaign_id: 42213, salutation: null, - first_name: 'Roger12', - last_name: 'Federer12', - email: 'Roger12@waltair.io', + first_name: 'Roger_12', + last_name: 'Federer_12', + email: 'Roger_12@federer.io', password: null, company: null, website: 'https://rudderstack.com', @@ -228,13 +162,13 @@ export const networkCallsData = [ grade: null, last_activity_at: null, recent_interaction: 'Never active.', - crm_lead_fid: null, + crm_lead_fid: '00Q6r000002LKhTPVR', crm_contact_fid: null, crm_owner_fid: '00G2v000004WYXaEAO', crm_account_fid: null, - salesforce_fid: null, - crm_last_sync: null, - crm_url: null, + salesforce_fid: '00Q6r000002LKhTPVR', + crm_last_sync: '2022-01-21 18:47:37', + crm_url: 'https://testcompany.my.salesforce.com/00Q6r000002LKhTPVR', is_do_not_email: null, is_do_not_call: null, opted_out: null, @@ -303,15 +237,32 @@ export const networkCallsData = [ statusText: 'OK', }, }, - // 3rd proxy test-case { httpReq: { - url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/fid/00Q6r000002LKhTPVR', + url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/rolex_waltair@mywebsite.io', + method: 'POST', + }, + httpRes: { + data: { + '@attributes': { + stat: 'fail', + version: 1, + err_code: 184, + }, + err: 'access_token is invalid, unknown, or malformed: Inactive token', + }, + status: 401, + statusText: 'Unauthorized', + }, + }, + { + httpReq: { + url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/id/123435', data: getFormData({ - first_name: 'Nick', - last_name: 'Kyrgios', + first_name: 'Roger12', + last_name: 'Federer12', website: 'https://rudderstack.com', - score: 12, + score: 14, campaign_id: 42213, format: 'json', }).toString(), @@ -334,9 +285,9 @@ export const networkCallsData = [ id: 123435, campaign_id: 42213, salutation: null, - first_name: 'Roger_12', - last_name: 'Federer_12', - email: 'Roger_12@federer.io', + first_name: 'Roger12', + last_name: 'Federer12', + email: 'Roger12@waltair.io', password: null, company: null, website: 'https://rudderstack.com', @@ -362,13 +313,13 @@ export const networkCallsData = [ grade: null, last_activity_at: null, recent_interaction: 'Never active.', - crm_lead_fid: '00Q6r000002LKhTPVR', + crm_lead_fid: null, crm_contact_fid: null, crm_owner_fid: '00G2v000004WYXaEAO', crm_account_fid: null, - salesforce_fid: '00Q6r000002LKhTPVR', - crm_last_sync: '2022-01-21 18:47:37', - crm_url: 'https://testcompany.my.salesforce.com/00Q6r000002LKhTPVR', + salesforce_fid: null, + crm_last_sync: null, + crm_url: null, is_do_not_email: null, is_do_not_call: null, opted_out: null, @@ -437,4 +388,22 @@ export const networkCallsData = [ statusText: 'OK', }, }, + { + httpReq: { + url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/rolex_waltair@test.com', + method: 'POST', + }, + httpRes: { + data: { + '@attributes': { + stat: 'fail', + version: 1, + err_code: 120, + }, + err: 'Unable to verify Salesforce connector', + }, + status: 500, + statusText: 'Invalid action', + }, + }, ]; From 6330888ad5c67e3a800037b56501fc08da09e4d1 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Thu, 14 Mar 2024 14:25:26 +0530 Subject: [PATCH 037/240] fix: fixed 500 status for algolia dontBatch (#3178) * fix: fixed 500 for algolia dontBatch --- src/v1/destinations/algolia/networkHandler.js | 31 +++++-------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/src/v1/destinations/algolia/networkHandler.js b/src/v1/destinations/algolia/networkHandler.js index d953251050..21f415197f 100644 --- a/src/v1/destinations/algolia/networkHandler.js +++ b/src/v1/destinations/algolia/networkHandler.js @@ -13,12 +13,6 @@ const responseHandler = (responseParams) => { const { destinationResponse, rudderJobMetadata } = responseParams; const message = `[ALGOLIA Response V1 Handler] - Request Processed Successfully`; const responseWithIndividualEvents = []; - // response: - // {status: 200, message: 'OK'} - // {response:'[ENOTFOUND] :: DNS lookup failed', status: 400} - // destinationResponse = { - // response: {"status": 422, "message": "EventType must be one of \"click\", \"conversion\" or \"view\""}, status: 422 - // } const { response, status } = destinationResponse; if (isHttpStatusSuccess(status)) { @@ -41,30 +35,19 @@ const responseHandler = (responseParams) => { // in case of non 2xx status sending 500 for every event, populate response and update dontBatch to true const errorMessage = response?.error?.message || response?.message || 'unknown error format'; - let serverStatus = 400; for (const metadata of rudderJobMetadata) { - // handling case if dontBatch is true, and again we got invalid from destination - if (metadata.dontBatch && status === 422) { - responseWithIndividualEvents.push({ - statusCode: 400, - metadata, - error: errorMessage, - }); - } else { - serverStatus = 500; - metadata.dontBatch = true; - responseWithIndividualEvents.push({ - statusCode: 500, - metadata, - error: errorMessage, - }); - } + metadata.dontBatch = true; + responseWithIndividualEvents.push({ + statusCode: 500, + metadata, + error: errorMessage, + }); } // sending back 500 for retry throw new TransformerProxyError( `ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation`, - serverStatus, + 500, { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), }, From 4d9bddeaef0cb90c074e9f7e017c8394bcfd6d36 Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Fri, 15 Mar 2024 12:53:48 +0530 Subject: [PATCH 038/240] chore: resolve sql injection vulnerabilities (#3172) --- package-lock.json | 9 +++++++++ package.json | 1 + .../networkHandler.js | 7 ++++++- .../google_adwords_offline_conversions/utils.js | 7 ++++++- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 700207e021..d708860537 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,6 +63,7 @@ "rudder-transformer-cdk": "^1.4.11", "set-value": "^4.1.0", "sha256": "^0.2.0", + "sqlstring": "^2.3.3", "stacktrace-parser": "^0.1.10", "statsd-client": "^0.4.7", "truncate-utf8-bytes": "^1.0.2", @@ -19087,6 +19088,14 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/stack-generator": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", diff --git a/package.json b/package.json index 070510029b..ec3ffbf4e6 100644 --- a/package.json +++ b/package.json @@ -108,6 +108,7 @@ "rudder-transformer-cdk": "^1.4.11", "set-value": "^4.1.0", "sha256": "^0.2.0", + "sqlstring": "^2.3.3", "stacktrace-parser": "^0.1.10", "statsd-client": "^0.4.7", "truncate-utf8-bytes": "^1.0.2", diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js index 3ea985e773..feedcf8975 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js +++ b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js @@ -1,6 +1,7 @@ const { get, set } = require('lodash'); const sha256 = require('sha256'); const { NetworkError, NetworkInstrumentationError } = require('@rudderstack/integrations-lib'); +const SqlString = require('sqlstring'); const { prepareProxyRequest, handleHttpRequest } = require('../../../adapters/network'); const { isHttpStatusSuccess, getAuthErrCategoryFromStCode } = require('../../util/index'); const { CONVERSION_ACTION_ID_CACHE_TTL } = require('./config'); @@ -29,8 +30,12 @@ const ERROR_MSG_PATH = 'response[0].error.message'; const getConversionActionId = async (method, headers, params) => { const conversionActionIdKey = sha256(params.event + params.customerId).toString(); return conversionActionIdCache.get(conversionActionIdKey, async () => { + const queryString = SqlString.format( + 'SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = ?', + [params.event], + ); const data = { - query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = '${params.event}'`, + query: queryString, }; const requestBody = { url: `${BASE_ENDPOINT}/${params.customerId}/googleAds:searchStream`, diff --git a/src/v0/destinations/google_adwords_offline_conversions/utils.js b/src/v0/destinations/google_adwords_offline_conversions/utils.js index ee677373a3..67c0ef31c8 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/utils.js +++ b/src/v0/destinations/google_adwords_offline_conversions/utils.js @@ -1,4 +1,5 @@ const sha256 = require('sha256'); +const SqlString = require('sqlstring'); const { get, set, cloneDeep } = require('lodash'); const { AbortedError, @@ -53,8 +54,12 @@ const validateDestinationConfig = ({ Config }) => { const getConversionActionId = async (headers, params) => { const conversionActionIdKey = sha256(params.event + params.customerId).toString(); return conversionActionIdCache.get(conversionActionIdKey, async () => { + const queryString = SqlString.format( + 'SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = ?', + [params.event], + ); const data = { - query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = '${params.event}'`, + query: queryString, }; const endpoint = SEARCH_STREAM.replace(':customerId', params.customerId); const requestOptions = { From f51e6b9d2b7324db14ff221ed70efa6484c1f496 Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:27:07 +0530 Subject: [PATCH 039/240] chore: gaec proxy test refactor (#3177) * chore: gaec proxy test refactor * chore: code review changes * chore: code review changes * chore: code review changes --- .../networkHandler.js | 2 +- .../dataDelivery/business.ts | 284 ++++++++++++++++ .../dataDelivery/data.ts | 312 +----------------- .../dataDelivery/oauth.ts | 276 ++++++++++++++++ .../network.ts | 142 ++++++++ 5 files changed, 710 insertions(+), 306 deletions(-) create mode 100644 test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/business.ts create mode 100644 test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/oauth.ts diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js index feedcf8975..f7ac660f53 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js +++ b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js @@ -122,7 +122,7 @@ const responseHandler = (responseParams) => { // Ref - https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto if (partialFailureError && partialFailureError.code !== 0) { throw new NetworkError( - `[Google Ads Offline Conversions]:: partialFailureError - ${JSON.stringify( + `[Google Adwords Enhanced Conversions]:: partialFailureError - ${JSON.stringify( partialFailureError, )}`, 400, diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/business.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/business.ts new file mode 100644 index 0000000000..0cee1418e1 --- /dev/null +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/business.ts @@ -0,0 +1,284 @@ +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; +import { ProxyV1TestData } from '../../../testTypes'; + +const headers = { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '0987654321', +}; + +const params = { + event: 'Product Added', + customerId: '1234567899', + destination: 'google_adwords_enhanced_conversions', +}; + +const validRequestPaylod = { + partialFailure: true, + conversionAdjustments: [ + { + gclidDateTimePair: { + gclid: 'gclid1234', + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + restatementValue: { + adjustedValue: 10, + currency: 'INR', + }, + order_id: '10000', + adjustmentDateTime: '2022-01-01 12:32:45-08:00', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + userIdentifiers: [ + { + addressInfo: { + hashedFirstName: 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', + hashedLastName: '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', + state: 'UK', + city: 'London', + hashedStreetAddress: '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', + }, + }, + ], + adjustmentType: 'ENHANCEMENT', + }, + ], +}; + +const commonRequestParameters = { + headers, + params, + JSON: validRequestPaylod, +}; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_ENHANCED_CONVERSIONS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const testScenariosForV0API = [ + { + id: 'gaec_v0_scenario_1', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v0 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567899:uploadConversionAdjustments', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + destinationResponse: { + response: [ + { + results: [ + { + adjustmentDateTime: '2021-01-01 12:32:45-08:00', + adjustmentType: 'ENHANCEMENT', + conversionAction: 'customers/7693729833/conversionActions/874224905', + gclidDateTimePair: { + conversionDateTime: '2021-01-01 12:32:45-08:00', + gclid: '1234', + }, + orderId: '12345', + }, + ], + }, + ], + status: 200, + }, + message: 'Request Processed Successfully', + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaec_v0_scenario_2', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v0 API] :: Test for a partial failure request with a 200 response from the destination', + successCriteria: 'Should return 400 with partial failure error', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + params: { + event: 'Product Added', + customerId: '1234567888', + destination: 'google_adwords_enhanced_conversions', + }, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567888:uploadConversionAdjustments', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + destinationResponse: { + code: 3, + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v15.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + conversionAdjustmentUploadError: 'CONVERSION_ALREADY_ENHANCED', + }, + location: { + fieldPathElements: [ + { + fieldName: 'conversion_adjustments', + index: 0, + }, + ], + }, + message: + 'Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again.', + }, + ], + }, + ], + message: + 'Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again., at conversion_adjustments[0]', + }, + message: + '[Google Adwords Enhanced Conversions]:: partialFailureError - {"code":3,"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again., at conversion_adjustments[0]","details":[{"@type":"type.googleapis.com/google.ads.googleads.v15.errors.GoogleAdsFailure","errors":[{"errorCode":{"conversionAdjustmentUploadError":"CONVERSION_ALREADY_ENHANCED"},"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again.","location":{"fieldPathElements":[{"fieldName":"conversion_adjustments","index":0}]}}]}]}', + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'gaec_v1_scenario_1', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v1 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567899:uploadConversionAdjustments', + }, + [generateMetadata(1)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '[{"results":[{"adjustmentType":"ENHANCEMENT","conversionAction":"customers/7693729833/conversionActions/874224905","adjustmentDateTime":"2021-01-01 12:32:45-08:00","gclidDateTimePair":{"gclid":"1234","conversionDateTime":"2021-01-01 12:32:45-08:00"},"orderId":"12345"}]}]', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaec_v1_scenario_2', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v1 API] :: Test for a partial failure request with a 200 response from the destination', + successCriteria: 'Should return 400 with partial failure error', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + params: { + event: 'Product Added', + customerId: '1234567888', + destination: 'google_adwords_enhanced_conversions', + }, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567888:uploadConversionAdjustments', + }, + [generateMetadata(1)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Adwords Enhanced Conversions]:: partialFailureError - {"code":3,"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again., at conversion_adjustments[0]","details":[{"@type":"type.googleapis.com/google.ads.googleads.v15.errors.GoogleAdsFailure","errors":[{"errorCode":{"conversionAdjustmentUploadError":"CONVERSION_ALREADY_ENHANCED"},"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again.","location":{"fieldPathElements":[{"fieldName":"conversion_adjustments","index":0}]}}]}]}', + response: [ + { + error: + '[Google Adwords Enhanced Conversions]:: partialFailureError - {"code":3,"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again., at conversion_adjustments[0]","details":[{"@type":"type.googleapis.com/google.ads.googleads.v15.errors.GoogleAdsFailure","errors":[{"errorCode":{"conversionAdjustmentUploadError":"CONVERSION_ALREADY_ENHANCED"},"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again.","location":{"fieldPathElements":[{"fieldName":"conversion_adjustments","index":0}]}}]}]}', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/data.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/data.ts index b544baaebd..709ab6d2a8 100644 --- a/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/data.ts +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/data.ts @@ -1,307 +1,9 @@ +import { v0oauthScenarios, v1oauthScenarios } from './oauth'; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + export const data = [ - { - name: 'google_adwords_enhanced_conversions', - description: 'Test 0', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v15/customers/1234567890:uploadConversionAdjustments', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': '0987654321', - }, - params: { - event: 'Product Added', - customerId: '1234567890', - destination: 'google_adwords_enhanced_conversions', - }, - body: { - JSON: { - partialFailure: true, - conversionAdjustments: [ - { - gclidDateTimePair: { - gclid: 'gclid1234', - conversionDateTime: '2022-01-01 12:32:45-08:00', - }, - restatementValue: { - adjustedValue: 10, - currency: 'INR', - }, - order_id: '10000', - adjustmentDateTime: '2022-01-01 12:32:45-08:00', - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - userIdentifiers: [ - { - addressInfo: { - hashedFirstName: - 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', - hashedLastName: - '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', - state: 'UK', - city: 'London', - hashedStreetAddress: - '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', - }, - }, - ], - adjustmentType: 'ENHANCEMENT', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 401, - body: { - output: { - message: - '""Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project." during Google_adwords_enhanced_conversions response transformation"', - authErrorCategory: 'REFRESH_TOKEN', - destinationResponse: [ - { - error: { - code: 401, - message: - 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', - status: 'UNAUTHENTICATED', - }, - }, - ], - statTags: { - destType: 'GOOGLE_ADWORDS_ENHANCED_CONVERSIONS', - errorCategory: 'network', - destinationId: 'Non-determininable', - workspaceId: 'Non-determininable', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - }, - status: 401, - }, - }, - }, - }, - }, - { - name: 'google_adwords_enhanced_conversions', - description: 'Test 1', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v15/customers/1234567899:uploadConversionAdjustments', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': '0987654321', - }, - params: { - event: 'Product Added', - customerId: '1234567899', - destination: 'google_adwords_enhanced_conversions', - }, - body: { - JSON: { - partialFailure: true, - conversionAdjustments: [ - { - gclidDateTimePair: { - gclid: 'gclid1234', - conversionDateTime: '2022-01-01 12:32:45-08:00', - }, - restatementValue: { - adjustedValue: 10, - currency: 'INR', - }, - order_id: '10000', - adjustmentDateTime: '2022-01-01 12:32:45-08:00', - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - userIdentifiers: [ - { - addressInfo: { - hashedFirstName: - 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', - hashedLastName: - '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', - state: 'UK', - city: 'London', - hashedStreetAddress: - '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', - }, - }, - ], - adjustmentType: 'ENHANCEMENT', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: { - destinationResponse: { - response: [ - { - results: [ - { - adjustmentDateTime: '2021-01-01 12:32:45-08:00', - adjustmentType: 'ENHANCEMENT', - conversionAction: 'customers/7693729833/conversionActions/874224905', - gclidDateTimePair: { - conversionDateTime: '2021-01-01 12:32:45-08:00', - gclid: '1234', - }, - orderId: '12345', - }, - ], - }, - ], - status: 200, - }, - message: 'Request Processed Successfully', - status: 200, - }, - }, - }, - }, - }, - { - name: 'google_adwords_enhanced_conversions', - description: 'Test 2', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v15/customers/1234567891:uploadConversionAdjustments', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': '0987654321', - }, - params: { - event: 'Product Added', - customerId: '1234567891', - destination: 'google_adwords_enhanced_conversions', - }, - body: { - JSON: { - partialFailure: true, - conversionAdjustments: [ - { - gclidDateTimePair: { - gclid: 'gclid1234', - conversionDateTime: '2022-01-01 12:32:45-08:00', - }, - restatementValue: { - adjustedValue: 10, - currency: 'INR', - }, - order_id: '10000', - adjustmentDateTime: '2022-01-01 12:32:45-08:00', - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - userIdentifiers: [ - { - addressInfo: { - hashedFirstName: - 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', - hashedLastName: - '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', - state: 'UK', - city: 'London', - hashedStreetAddress: - '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', - }, - }, - ], - adjustmentType: 'ENHANCEMENT', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - destinationResponse: [ - { - results: [ - { - conversionAction: { - id: 123434342, - }, - }, - ], - }, - ], - message: '" during Google_adwords_enhanced_conversions response transformation', - statTags: { - destType: 'GOOGLE_ADWORDS_ENHANCED_CONVERSIONS', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - status: 400, - }, - }, - }, - }, - }, + ...v0oauthScenarios, + ...v1oauthScenarios, + ...testScenariosForV0API, + ...testScenariosForV1API, ]; diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/oauth.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/oauth.ts new file mode 100644 index 0000000000..70d9eeaf33 --- /dev/null +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/oauth.ts @@ -0,0 +1,276 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { + generateProxyV1Payload, + generateProxyV0Payload, + generateMetadata, +} from '../../../testUtils'; + +const requestPayload = { + partialFailure: true, + conversionAdjustments: [ + { + gclidDateTimePair: { + gclid: 'gclid1234', + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + restatementValue: { + adjustedValue: 10, + currency: 'INR', + }, + order_id: '10000', + adjustmentDateTime: '2022-01-01 12:32:45-08:00', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + userIdentifiers: [ + { + addressInfo: { + hashedFirstName: 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', + hashedLastName: '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', + state: 'UK', + city: 'London', + hashedStreetAddress: '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', + }, + }, + ], + adjustmentType: 'ENHANCEMENT', + }, + ], +}; + +const headers = { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '0987654321', +}; + +const params = { + event: 'Product Added', + customerId: '1234567890', + destination: 'google_adwords_enhanced_conversions', +}; + +const commonRequestParameters = { + params, + headers, + JSON: requestPayload, +}; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_ENHANCED_CONVERSIONS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const v0oauthScenarios = [ + { + id: 'gaec_v0_oauth_scenario_1', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v0 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: + 'Since the error from the destination is 401 - the proxy should return 500 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567890:uploadConversionAdjustments', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + authErrorCategory: 'REFRESH_TOKEN', + destinationResponse: [ + { + error: { + code: 401, + message: + 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', + status: 'UNAUTHENTICATED', + }, + }, + ], + message: + '""Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project." during Google_adwords_enhanced_conversions response transformation"', + statTags: expectedStatTags, + status: 401, + }, + }, + }, + }, + }, + { + id: 'gaec_v0_oauth_scenario_2', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v0 API] :: Oauth where caller does not have permission mock response from destination', + successCriteria: + 'Since the error from the destination is 403 - the proxy should return 403 with error', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + JSON: { + query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Product Added'`, + }, + headers, + params: { + event: 'Product Added', + customerId: '1234567910', + destination: 'google_adwords_enhanced_conversions', + }, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567910/googleAds:searchStream', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + authErrorCategory: 'AUTH_STATUS_INACTIVE', + destinationResponse: [ + { + error: { + code: 403, + errors: [ + { + domain: 'global', + message: 'The caller does not have permission', + reason: 'forbidden', + }, + ], + message: 'The caller does not have permission', + status: 'PERMISSION_DENIED', + }, + }, + ], + message: + '""The caller does not have permission" during Google_adwords_enhanced_conversions response transformation"', + statTags: expectedStatTags, + status: 403, + }, + }, + }, + }, + }, +]; + +export const v1oauthScenarios = [ + { + id: 'gaec_v1_oauth_scenario_1', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v1 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: + 'Since the error from the destination is 401 - the proxy should return 500 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567890:uploadConversionAdjustments', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + authErrorCategory: 'REFRESH_TOKEN', + message: + '""Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project." during Google_adwords_enhanced_conversions response transformation"', + response: [ + { + error: + '""Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project." during Google_adwords_enhanced_conversions response transformation"', + metadata: generateMetadata(1), + statusCode: 401, + }, + ], + statTags: expectedStatTags, + status: 401, + }, + }, + }, + }, + }, + { + id: 'gaec_v1_oauth_scenario_2', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v1 API] :: Oauth where caller does not have permission mock response from destination', + successCriteria: + 'Since the error from the destination is 403 - the proxy should return 403 with error', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + JSON: { + query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Product Added'`, + }, + headers, + params: { + event: 'Product Added', + customerId: '1234567910', + destination: 'google_adwords_enhanced_conversions', + }, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567910/googleAds:searchStream', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + authErrorCategory: 'AUTH_STATUS_INACTIVE', + message: + '""The caller does not have permission" during Google_adwords_enhanced_conversions response transformation"', + response: [ + { + error: + '""The caller does not have permission" during Google_adwords_enhanced_conversions response transformation"', + metadata: generateMetadata(1), + statusCode: 403, + }, + ], + statTags: expectedStatTags, + status: 403, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/network.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/network.ts index 672cd73bf7..69b3a6103a 100644 --- a/test/integrations/destinations/google_adwords_enhanced_conversions/network.ts +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/network.ts @@ -273,4 +273,146 @@ export const networkCallsData = [ status: 400, }, }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v15/customers/1234567888/googleAds:searchStream', + data: { + query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Product Added'`, + }, + params: { destination: 'google_adwords_enhanced_conversion' }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '0987654321', + }, + method: 'POST', + }, + httpRes: { + data: [ + { + results: [ + { + conversionAction: { + id: 123434345, + }, + }, + ], + }, + ], + status: 200, + }, + }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v15/customers/1234567888:uploadConversionAdjustments', + data: { + conversionAdjustments: [ + { + adjustmentDateTime: '2022-01-01 12:32:45-08:00', + adjustmentType: 'ENHANCEMENT', + conversionAction: 'customers/1234567888/conversionActions/123434345', + gclidDateTimePair: { + conversionDateTime: '2022-01-01 12:32:45-08:00', + gclid: 'gclid1234', + }, + order_id: '10000', + restatementValue: { adjustedValue: 10, currency: 'INR' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + userIdentifiers: [ + { + addressInfo: { + hashedFirstName: + 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', + hashedLastName: + '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', + state: 'UK', + city: 'London', + hashedStreetAddress: + '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', + }, + }, + ], + }, + ], + partialFailure: true, + }, + params: { destination: 'google_adwords_enhanced_conversion' }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '0987654321', + }, + method: 'POST', + }, + httpRes: { + status: 200, + data: { + partialFailureError: { + code: 3, + message: + 'Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again., at conversion_adjustments[0]', + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v15.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + conversionAdjustmentUploadError: 'CONVERSION_ALREADY_ENHANCED', + }, + message: + 'Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again.', + location: { + fieldPathElements: [ + { + fieldName: 'conversion_adjustments', + index: 0, + }, + ], + }, + }, + ], + }, + ], + }, + }, + }, + }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v15/customers/1234567910/googleAds:searchStream', + data: { + query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Product Added'`, + }, + params: { destination: 'google_adwords_enhanced_conversion' }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '0987654321', + }, + method: 'POST', + }, + httpRes: { + data: [ + { + error: { + code: 403, + message: 'The caller does not have permission', + errors: [ + { + message: 'The caller does not have permission', + domain: 'global', + reason: 'forbidden', + }, + ], + status: 'PERMISSION_DENIED', + }, + }, + ], + status: 403, + }, + }, ]; From f00d411c254d69f0155c7aa267f187bf6f59f6d4 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Fri, 15 Mar 2024 15:43:15 +0530 Subject: [PATCH 040/240] chore: algolia component tests (#3183) --- src/v1/destinations/algolia/networkHandler.js | 22 +- .../algolia/dataDelivery/business.ts | 331 +++++++++++ .../algolia/dataDelivery/constant.ts | 118 ++++ .../destinations/algolia/dataDelivery/data.ts | 9 + .../algolia/dataDelivery/other.ts | 524 ++++++++++++++++++ .../destinations/algolia/network.ts | 117 ++++ 6 files changed, 1117 insertions(+), 4 deletions(-) create mode 100644 test/integrations/destinations/algolia/dataDelivery/business.ts create mode 100644 test/integrations/destinations/algolia/dataDelivery/constant.ts create mode 100644 test/integrations/destinations/algolia/dataDelivery/data.ts create mode 100644 test/integrations/destinations/algolia/dataDelivery/other.ts create mode 100644 test/integrations/destinations/algolia/network.ts diff --git a/src/v1/destinations/algolia/networkHandler.js b/src/v1/destinations/algolia/networkHandler.js index 21f415197f..de25993fb1 100644 --- a/src/v1/destinations/algolia/networkHandler.js +++ b/src/v1/destinations/algolia/networkHandler.js @@ -1,7 +1,7 @@ /* eslint-disable no-restricted-syntax */ const { TransformerProxyError } = require('../../../v0/util/errorTypes'); const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); -const { isHttpStatusSuccess, getAuthErrCategoryFromStCode } = require('../../../v0/util/index'); +const { isHttpStatusSuccess } = require('../../../v0/util/index'); const { processAxiosResponse, @@ -44,15 +44,29 @@ const responseHandler = (responseParams) => { }); } - // sending back 500 for retry + // At least one event in the batch is invalid. + if (status === 422) { + // sending back 500 for retry + throw new TransformerProxyError( + `ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation`, + 500, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(500), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); + } + throw new TransformerProxyError( `ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation`, - 500, + status, { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), }, destinationResponse, - getAuthErrCategoryFromStCode(status), + '', responseWithIndividualEvents, ); }; diff --git a/test/integrations/destinations/algolia/dataDelivery/business.ts b/test/integrations/destinations/algolia/dataDelivery/business.ts new file mode 100644 index 0000000000..8ba964e2dd --- /dev/null +++ b/test/integrations/destinations/algolia/dataDelivery/business.ts @@ -0,0 +1,331 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV0Payload, generateProxyV1Payload } from '../../../testUtils'; +import { abortStatTags, commonRequestProperties, metadataArray, retryStatTags } from './constant'; +const proxyMetdata3 = { + jobId: 3, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; +export const testScenariosForV0API = [ + { + id: 'algolia_v0_bussiness_scenario_1', + name: 'algolia', + description: '[Proxy v0 API] :: algolia all valid events', + successCriteria: 'Proper response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: + '[Generic Response Handler] Request for destination: algolia Processed Successfully', + destinationResponse: { + response: { + message: 'OK', + status: 200, + }, + status: 200, + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_bussiness_scenario_2', + name: 'algolia', + description: '[Proxy v0 API] :: algolia with invalid event', + successCriteria: 'Error Response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + JSON: commonRequestProperties.singleInValidEvent, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 422, + body: { + output: { + status: 422, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 422', + destinationResponse: { + response: { + status: 422, + message: 'EventType must be one of "click", "conversion" or "view"', + }, + status: 422, + }, + statTags: abortStatTags, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_bussiness_scenario_3', + name: 'algolia', + description: '[Proxy v0 API] :: algolia with invalid events in batch', + successCriteria: 'Error Response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + JSON: commonRequestProperties.combinedValidInvalidEvents, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 422, + body: { + output: { + status: 422, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 422', + destinationResponse: { + response: { + status: 422, + message: 'EventType must be one of "click", "conversion" or "view"', + }, + status: 422, + }, + statTags: abortStatTags, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'algolia_v1_bussiness_scenario_1', + name: 'algolia', + description: '[Proxy v1 API] :: algolia all valid events in batch', + successCriteria: 'Success response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + JSON: commonRequestProperties.multipleValidEvent, + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[ALGOLIA Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + message: 'OK', + status: 200, + }, + status: 200, + }, + response: [ + { + error: 'success', + metadata: metadataArray[0], + statusCode: 200, + }, + { + error: 'success', + metadata: metadataArray[1], + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'algolia_v1_bussiness_scenario_2', + name: 'algolia', + description: '[Proxy v1 API] :: algolia all invalid events in batch', + successCriteria: 'Send response with dontBatch as true', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + JSON: commonRequestProperties.singleInValidEvent, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + response: [ + { + error: + '{"status":422,"message":"EventType must be one of \\"click\\", \\"conversion\\" or \\"view\\""}', + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + statusCode: 500, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v1_bussiness_scenario_3', + name: 'algolia', + description: '[Proxy v1 API] :: algolia combination of valid and invalid events in batch', + successCriteria: 'Should use dontBatch true and proper response returned', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + JSON: commonRequestProperties.combinedValidInvalidEvents, + }, + [...metadataArray, proxyMetdata3], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + response: [ + { + error: + '{"status":422,"message":"EventType must be one of \\"click\\", \\"conversion\\" or \\"view\\""}', + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: true, + }, + statusCode: 500, + }, + { + error: + '{"status":422,"message":"EventType must be one of \\"click\\", \\"conversion\\" or \\"view\\""}', + metadata: { + jobId: 2, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: true, + }, + statusCode: 500, + }, + { + error: + '{"status":422,"message":"EventType must be one of \\"click\\", \\"conversion\\" or \\"view\\""}', + metadata: { + jobId: 3, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: true, + }, + statusCode: 500, + }, + ], + statTags: retryStatTags, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/algolia/dataDelivery/constant.ts b/test/integrations/destinations/algolia/dataDelivery/constant.ts new file mode 100644 index 0000000000..e8d0817a7f --- /dev/null +++ b/test/integrations/destinations/algolia/dataDelivery/constant.ts @@ -0,0 +1,118 @@ +const proxyMetdata1 = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; + +const proxyMetdata2 = { + jobId: 2, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; + +export const metadataArray = [proxyMetdata1, proxyMetdata2]; + +export const abortStatTags = { + errorCategory: 'network', + errorType: 'aborted', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +export const commonRequestProperties = { + commonHeaders: { + 'X-Algolia-API-Key': 'dummyApiKey', + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'User-Agent': 'RudderLabs', + }, + singleValidEvent: { + events: [ + { + eventName: 'product clicked', + eventType: 'click', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + singleInValidEvent: { + events: [ + { + eventName: 'product clicked', + eventType: 'abc', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + multipleValidEvent: { + events: [ + { + eventName: 'product clicked', + eventType: 'click', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'view', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + combinedValidInvalidEvents: { + events: [ + { + eventName: 'product clicked', + eventType: 'click', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'view', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'abc', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, +}; + +export const retryStatTags = { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', +}; diff --git a/test/integrations/destinations/algolia/dataDelivery/data.ts b/test/integrations/destinations/algolia/dataDelivery/data.ts new file mode 100644 index 0000000000..feb4eb46c5 --- /dev/null +++ b/test/integrations/destinations/algolia/dataDelivery/data.ts @@ -0,0 +1,9 @@ +import { testScenariosForV0API, testScenariosForV1API } from './business'; +import { otherScenariosV0, otherScenariosV1 } from './other'; + +export const data = [ + ...testScenariosForV0API, + ...testScenariosForV1API, + ...otherScenariosV0, + ...otherScenariosV1, +]; diff --git a/test/integrations/destinations/algolia/dataDelivery/other.ts b/test/integrations/destinations/algolia/dataDelivery/other.ts new file mode 100644 index 0000000000..f5ccc70337 --- /dev/null +++ b/test/integrations/destinations/algolia/dataDelivery/other.ts @@ -0,0 +1,524 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV0Payload, generateProxyV1Payload } from '../../../testUtils'; + +export const otherScenariosV0 = [ + { + id: 'algolia_v0_other_scenario_1', + name: 'algolia', + description: + '[Proxy v0 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + endpoint: 'https://random_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 503, + body: { + output: { + status: 503, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 503', + destinationResponse: { + response: { + error: { + message: 'Service Unavailable', + description: + 'The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later.', + }, + }, + status: 503, + }, + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_other_scenario_2', + name: 'algolia', + description: '[Proxy v0 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 500', + destinationResponse: { + response: 'Internal Server Error', + status: 500, + }, + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_other_scenario_3', + name: 'algolia', + description: '[Proxy v0 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 504, + body: { + output: { + status: 504, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 504', + destinationResponse: { + response: 'Gateway Timeout', + status: 504, + }, + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_other_scenario_4', + name: 'algolia', + description: '[Proxy v0 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + endpoint: 'https://random_test_url/test_for_null_response', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 500', + destinationResponse: { + response: '', + status: 500, + }, + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_other_scenario_5', + name: 'algolia', + description: + '[Proxy v0 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 500', + destinationResponse: { + response: '', + status: 500, + }, + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, +]; + +export const otherScenariosV1: ProxyV1TestData[] = [ + { + id: 'algolia_v1_other_scenario_1', + name: 'algolia', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 503, + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + status: 503, + }, + }, + }, + }, + }, + { + id: 'algolia_v1_other_scenario_2', + name: 'algolia', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + status: 500, + }, + }, + }, + }, + }, + { + id: 'algolia_v1_other_scenario_3', + name: 'algolia', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout"', + statusCode: 504, + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + status: 504, + }, + }, + }, + }, + }, + { + id: 'algolia_v1_other_scenario_4', + name: 'algolia', + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_response', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + status: 500, + }, + }, + }, + }, + }, + { + id: 'algolia_v1_other_scenario_5', + name: 'algolia', + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/algolia/network.ts b/test/integrations/destinations/algolia/network.ts new file mode 100644 index 0000000000..84e932bbdb --- /dev/null +++ b/test/integrations/destinations/algolia/network.ts @@ -0,0 +1,117 @@ +export const networkCallsData = [ + { + httpReq: { + url: 'https://insights.algolia.io/1/events', + data: { + events: [ + { + eventName: 'product clicked', + eventType: 'abc', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + params: {}, + headers: { 'User-Agent': 'RudderLabs' }, + method: 'POST', + }, + httpRes: { + data: { + status: 422, + message: 'EventType must be one of "click", "conversion" or "view"', + }, + status: 422, + }, + }, + { + httpReq: { + url: 'https://insights.algolia.io/1/events', + data: { + events: [ + { + eventName: 'product clicked', + eventType: 'abc', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'click', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + params: {}, + headers: { 'User-Agent': 'RudderLabs' }, + method: 'POST', + }, + httpRes: { + data: { + status: 422, + message: 'EventType must be one of "click", "conversion" or "view"', + }, + status: 422, + }, + }, + { + httpReq: { + url: 'https://insights.algolia.io/1/events', + data: { + events: [ + { + eventName: 'product clicked', + eventType: 'click', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'view', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'abc', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + params: {}, + headers: { 'User-Agent': 'RudderLabs' }, + method: 'POST', + }, + httpRes: { + data: { + status: 422, + message: 'EventType must be one of "click", "conversion" or "view"', + }, + status: 422, + }, + }, + { + httpReq: { + url: 'https://insights.algolia.io/1/events', + method: 'POST', + headers: { + 'User-Agent': 'RudderLabs', + }, + }, + httpRes: { + data: { + status: 200, + message: 'OK', + }, + status: 200, + }, + }, +]; From 5befa02848b66bcd89827d7e33728999ea243d0c Mon Sep 17 00:00:00 2001 From: Jayachand Date: Fri, 15 Mar 2024 16:36:56 +0530 Subject: [PATCH 041/240] chore: adding a metric to capture event batch size DAT-909 (#3070) * chore: adding a metric to capture event batch size --- src/controllers/userTransform.ts | 3 ++- src/services/userTransform.ts | 14 +++++++++----- src/util/prometheus.js | 20 ++++++++++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/controllers/userTransform.ts b/src/controllers/userTransform.ts index c344bd072a..3e01686a52 100644 --- a/src/controllers/userTransform.ts +++ b/src/controllers/userTransform.ts @@ -15,9 +15,10 @@ export class UserTransformController { '(User transform - router:/customTransform ):: Request to transformer', JSON.stringify(ctx.request.body), ); + const requestSize = Number(ctx.request.get('content-length')); const events = ctx.request.body as ProcessorTransformationRequest[]; const processedRespone: UserTransformationServiceResponse = - await UserTransformService.transformRoutine(events, ctx.state.features); + await UserTransformService.transformRoutine(events, ctx.state.features, requestSize); ctx.body = processedRespone.transformedEvents; ControllerUtility.postProcess(ctx, processedRespone.retryStatus); logger.debug( diff --git a/src/services/userTransform.ts b/src/services/userTransform.ts index bae833c86a..18c47ddc83 100644 --- a/src/services/userTransform.ts +++ b/src/services/userTransform.ts @@ -14,7 +14,7 @@ import { RetryRequestError, extractStackTraceUptoLastSubstringMatch, } from '../util/utils'; -import { getMetadata, isNonFuncObject } from '../v0/util'; +import { getMetadata, getTransformationMetadata, isNonFuncObject } from '../v0/util'; import { SUPPORTED_FUNC_NAMES } from '../util/ivmFactory'; import logger from '../logger'; import stats from '../util/stats'; @@ -28,6 +28,7 @@ export class UserTransformService { public static async transformRoutine( events: ProcessorTransformationRequest[], features: FeatureFlags = {}, + requestSize = 0, ): Promise { let retryStatus = 200; const groupedEvents: NonNullable = groupBy( @@ -162,16 +163,19 @@ export class UserTransformService { ), ); stats.counter('user_transform_errors', eventsToProcess.length, { - transformationId: eventsToProcess[0]?.metadata?.transformationId, - workspaceId: eventsToProcess[0]?.metadata?.workspaceId, status, ...metaTags, + ...getTransformationMetadata(eventsToProcess[0]?.metadata), }); } finally { stats.timing('user_transform_request_latency', userFuncStartTime, { - workspaceId: eventsToProcess[0]?.metadata?.workspaceId, - transformationId: eventsToProcess[0]?.metadata?.transformationId, ...metaTags, + ...getTransformationMetadata(eventsToProcess[0]?.metadata), + }); + + stats.histogram('user_transform_batch_size', requestSize, { + ...metaTags, + ...getTransformationMetadata(eventsToProcess[0]?.metadata), }); } diff --git a/src/util/prometheus.js b/src/util/prometheus.js index b502681987..5de7ac899d 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -593,6 +593,10 @@ class Prometheus { name: 'tp_batch_size', help: 'Size of batch of events for tracking plan validation', type: 'histogram', + buckets: [ + 1024, 102400, 524288, 1048576, 10485760, 20971520, 52428800, 104857600, 209715200, + 524288000, + ], labelNames: [ 'sourceType', 'destinationType', @@ -670,6 +674,22 @@ class Prometheus { 'k8_namespace', ], }, + { + name: 'user_transform_batch_size', + help: 'user_transform_batch_size', + type: 'histogram', + labelNames: [ + 'workspaceId', + 'transformationId', + 'sourceType', + 'destinationType', + 'k8_namespace', + ], + buckets: [ + 1024, 102400, 524288, 1048576, 10485760, 20971520, 52428800, 104857600, 209715200, + 524288000, + ], // 1KB, 100KB, 0.5MB, 1MB, 10MB, 20MB, 50MB, 100MB, 200MB, 500MB + }, { name: 'source_transform_request_latency', help: 'source_transform_request_latency', From 2ad12399040cbdc15453c2bb84f3aa9ab87e5c1f Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Mon, 18 Mar 2024 09:57:03 +0530 Subject: [PATCH 042/240] chore: onboard adobe to proxy v1 tests (#3163) * chore: onboard adobe to proxy v1 tests * chore: fix lint * chore: fix lintx2 --- .../adobe_analytics/dataDelivery/business.ts | 181 ++++++++++++++++++ .../adobe_analytics/dataDelivery/data.ts | 8 +- .../destinations/adobe_analytics/network.ts | 2 +- 3 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 test/integrations/destinations/adobe_analytics/dataDelivery/business.ts diff --git a/test/integrations/destinations/adobe_analytics/dataDelivery/business.ts b/test/integrations/destinations/adobe_analytics/dataDelivery/business.ts new file mode 100644 index 0000000000..76e07690cf --- /dev/null +++ b/test/integrations/destinations/adobe_analytics/dataDelivery/business.ts @@ -0,0 +1,181 @@ +import { ProxyMetdata } from '../../../../../src/types'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const statTags = { + aborted: { + destType: 'ADOBE_ANALYTICS', + destinationId: 'dummyDestinationId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, +}; + +export const proxyMetdata: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; +const headers = { + 'Content-Type': 'application/xml', +}; + +export const reqMetadataArray = [proxyMetdata]; + +const failureRequestParameters = { + XML: { + payload: + '17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerprodViewGames;;11;148.39failureReport', + }, + params: {}, +}; + +const successRequestParameters = { + XML: { + payload: + '127.0.1.0www.google.co.inGoogleid1110011prodViewGames;Monopoly;1;14.00,Games;UNO;2;6.90successreport', + }, + params: {}, +}; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'adobe_analytics_v1_scenario_1', + name: 'adobe_analytics', + description: '[Proxy v1 API] :: Test for Failure response from Adobe Analytics with reason', + successCriteria: 'Should return a 400 status code with reason', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...failureRequestParameters, + headers, + endpoint: 'https://adobe.failure.omtrdc.net/b/ss//6', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: + '[ADOBE_ANALYTICS Response Handler] Request failed for destination adobe_analytics : NO pagename OR pageurl', + response: [ + { + error: + '[ADOBE_ANALYTICS Response Handler] Request failed for destination adobe_analytics : NO pagename OR pageurl', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, + { + id: 'adobe_analytics_v1_scenario_2', + name: 'adobe_analytics', + description: + '[Proxy v1 API] :: Test for Failure response from Adobe Analytics without reason (Generic error)', + successCriteria: 'Should return a 400 status code with a general error', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...failureRequestParameters, + headers, + endpoint: 'https://adobe.failure2.omtrdc.net/b/ss//6', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: + '[ADOBE_ANALYTICS Response Handler] Request failed for destination adobe_analytics with a general error', + response: [ + { + error: + '[ADOBE_ANALYTICS Response Handler] Request failed for destination adobe_analytics with a general error', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, + { + id: 'adobe_analytics_v1_scenario_3', + name: 'adobe_analytics', + description: '[Proxy v1 API] :: Test for Success response from Adobe Analytics', + successCriteria: 'Should return a 200 status code with status SUCCESS', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...successRequestParameters, + headers, + endpoint: 'https://adobe.success.omtrdc.net/b/ss//6', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[ADOBE_ANALYTICS] - Request Processed Successfully', + response: [ + { + error: '"SUCCESS"', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/adobe_analytics/dataDelivery/data.ts b/test/integrations/destinations/adobe_analytics/dataDelivery/data.ts index 182969da73..2535a0639e 100644 --- a/test/integrations/destinations/adobe_analytics/dataDelivery/data.ts +++ b/test/integrations/destinations/adobe_analytics/dataDelivery/data.ts @@ -1,4 +1,6 @@ -export const data = [ +import { testScenariosForV1API } from './business'; + +const legacyTests = [ { name: 'adobe_analytics', description: 'Test 0: Failure response from Adobe Analytics with reason', @@ -72,7 +74,7 @@ export const data = [ JSON_ARRAY: {}, XML: { payload: - '17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerprodViewGames;;11;148.39failureReportgeneric', + '17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerprodViewGames;;11;148.39failureReport', }, FORM: {}, }, @@ -140,3 +142,5 @@ export const data = [ }, }, ]; + +export const data = [...testScenariosForV1API, ...legacyTests]; diff --git a/test/integrations/destinations/adobe_analytics/network.ts b/test/integrations/destinations/adobe_analytics/network.ts index 2fe4f0204e..7e32c5f10b 100644 --- a/test/integrations/destinations/adobe_analytics/network.ts +++ b/test/integrations/destinations/adobe_analytics/network.ts @@ -17,7 +17,7 @@ export const networkCallsData = [ { httpReq: { url: 'https://adobe.failure2.omtrdc.net/b/ss//6', - data: '17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerprodViewGames;;11;148.39failureReportgeneric', + data: '17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerprodViewGames;;11;148.39failureReport', params: {}, headers: { 'Content-Type': 'application/xml', From 7018b1e5e7f37ae177191c5ecf3a71cfe2f3d147 Mon Sep 17 00:00:00 2001 From: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Date: Mon, 18 Mar 2024 10:36:32 +0530 Subject: [PATCH 043/240] feat: onboard destination movable ink (#3167) * feat: onboard destination movable ink * test: update common.ts * feat: add batching support * feat: batching on max size in bytes * docs: added comments --- src/cdk/v2/destinations/movable_ink/config.js | 3 + .../movable_ink/procWorkflow.yaml | 72 +++++++ .../destinations/movable_ink/rtWorkflow.yaml | 74 +++++++ src/features.json | 1 + .../destinations/movable_ink/common.ts | 128 ++++++++++++ .../movable_ink/processor/data.ts | 4 + .../movable_ink/processor/identify.ts | 64 ++++++ .../movable_ink/processor/track.ts | 189 ++++++++++++++++++ .../movable_ink/processor/validation.ts | 131 ++++++++++++ .../destinations/movable_ink/router/data.ts | 162 +++++++++++++++ 10 files changed, 828 insertions(+) create mode 100644 src/cdk/v2/destinations/movable_ink/config.js create mode 100644 src/cdk/v2/destinations/movable_ink/procWorkflow.yaml create mode 100644 src/cdk/v2/destinations/movable_ink/rtWorkflow.yaml create mode 100644 test/integrations/destinations/movable_ink/common.ts create mode 100644 test/integrations/destinations/movable_ink/processor/data.ts create mode 100644 test/integrations/destinations/movable_ink/processor/identify.ts create mode 100644 test/integrations/destinations/movable_ink/processor/track.ts create mode 100644 test/integrations/destinations/movable_ink/processor/validation.ts create mode 100644 test/integrations/destinations/movable_ink/router/data.ts diff --git a/src/cdk/v2/destinations/movable_ink/config.js b/src/cdk/v2/destinations/movable_ink/config.js new file mode 100644 index 0000000000..673e94620e --- /dev/null +++ b/src/cdk/v2/destinations/movable_ink/config.js @@ -0,0 +1,3 @@ +module.exports = { + MAX_REQUEST_SIZE_IN_BYTES: 13500, +}; diff --git a/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml b/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml new file mode 100644 index 0000000000..25270058c5 --- /dev/null +++ b/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml @@ -0,0 +1,72 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + - name: defaultRequestConfig + path: ../../../../v0/util + - name: toUnixTimestampInMS + path: ../../../../v0/util + - name: base64Convertor + path: ../../../../v0/util + - path: ./utils + +steps: + - name: messageType + template: | + .message.type.toLowerCase(); + + - name: validateInput + template: | + let messageType = $.outputs.messageType; + $.assert(messageType, "message Type is not present. Aborting"); + $.assert(messageType in {{$.EventType.([.IDENTIFY,.TRACK])}}, "message type " + messageType + " is not supported"); + $.assertConfig(.destination.Config.endpoint, "Movable Ink Endpoint is not present. Aborting"); + $.assertConfig(.destination.Config.accessKey, "Access key is not present . Aborting"); + $.assertConfig(.destination.Config.accessSecret, "Access Secret is not present. Aborting"); + $.assert(.message.timestamp ?? .message.originalTimestamp, "Timestamp is not present. Aborting"); + + const userId = .message.().( + {{{{$.getGenericPaths("userIdOnly")}}}}; + ); + const email = .message.().( + {{{{$.getGenericPaths("email")}}}}; + ); + + $.assert(userId ?? email ?? .message.anonymousId, "Either one of userId or email or anonymousId is required. Aborting"); + + - name: preparePayload + description: Prepare payload for identify and track. This payload schema needs to be configured in the Movable Ink dashboard. Movable Ink will discard any additional fields from the input payload. + template: | + const userId = .message.().( + {{{{$.getGenericPaths("userIdOnly")}}}}; + ); + const email = .message.().( + {{{{$.getGenericPaths("email")}}}}; + ); + const timestampInUnix = $.toUnixTimestampInMS(.message.().( + {{{{$.getGenericPaths("timestamp")}}}}; + )); + $.context.payload = { + ...(.message), + userId: userId ?? email, + timestamp: timestampInUnix, + anonymousId: .message.anonymousId + } + + - name: buildResponse + description: In batchMode we return payload directly + condition: $.batchMode + template: | + $.context.payload + else: + name: buildResponseForProcessTransformation + template: | + const response = $.defaultRequestConfig(); + response.body.JSON = $.context.payload; + response.endpoint = .destination.Config.endpoint; + response.method = "POST"; + response.headers = { + "Content-Type": "application/json", + "Authorization": "Basic " + $.base64Convertor(.destination.Config.accessKey + ":" + .destination.Config.accessSecret) + } + response; diff --git a/src/cdk/v2/destinations/movable_ink/rtWorkflow.yaml b/src/cdk/v2/destinations/movable_ink/rtWorkflow.yaml new file mode 100644 index 0000000000..46afb34d53 --- /dev/null +++ b/src/cdk/v2/destinations/movable_ink/rtWorkflow.yaml @@ -0,0 +1,74 @@ +bindings: + - name: handleRtTfSingleEventError + path: ../../../../v0/util/index + - path: ./utils + exportAll: true + - name: base64Convertor + path: ../../../../v0/util + - name: BatchUtils + path: '@rudderstack/workflow-engine' + - path: ./config + +steps: + - name: validateInput + template: | + $.assert(Array.isArray(^) && ^.length > 0, "Invalid event array") + + - name: transform + externalWorkflow: + path: ./procWorkflow.yaml + bindings: + - name: batchMode + value: true + loopOverInput: true + + - name: successfulEvents + template: | + $.outputs.transform#idx.output.({ + "batchedRequest": ., + "batched": false, + "destination": ^[idx].destination, + "metadata": ^[idx].metadata, + "statusCode": 200 + })[] + + - name: failedEvents + template: | + $.outputs.transform#idx.error.( + $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) + )[] + + - name: batchSuccessfulEvents + description: Batches the successfulEvents + template: | + let batches = $.BatchUtils.chunkArrayBySizeAndLength( + $.outputs.successfulEvents, {maxSizeInBytes: $.MAX_REQUEST_SIZE_IN_BYTES}).items; + + batches@batch.({ + "batchedRequest": { + "body": { + "JSON": {"events": ~r batch.batchedRequest[]}, + "JSON_ARRAY": {}, + "XML": {}, + "FORM": {} + }, + "version": "1", + "type": "REST", + "method": "POST", + "endpoint": batch[0].destination.Config.().(.endpoint), + "headers": batch[0].destination.Config.().({ + "Content-Type": "application/json", + "Authorization": "Basic " + $.base64Convertor(.accessKey + ":" + .accessSecret) + }), + "params": {}, + "files": {} + }, + "metadata": ~r batch.metadata[], + "batched": true, + "statusCode": 200, + "destination": batch[0].destination + })[]; + + - name: finalPayload + template: | + [...$.outputs.batchSuccessfulEvents, ...$.outputs.failedEvents] diff --git a/src/features.json b/src/features.json index dc52044048..76b562a825 100644 --- a/src/features.json +++ b/src/features.json @@ -67,6 +67,7 @@ "THE_TRADE_DESK": true, "INTERCOM": true, "NINETAILED": true, + "MOVABLE_INK": true, "KOALA": true }, "regulations": [ diff --git a/test/integrations/destinations/movable_ink/common.ts b/test/integrations/destinations/movable_ink/common.ts new file mode 100644 index 0000000000..f7eaa7af39 --- /dev/null +++ b/test/integrations/destinations/movable_ink/common.ts @@ -0,0 +1,128 @@ +import { Destination } from '../../../../src/types'; + +const destType = 'movable_ink'; +const destTypeInUpperCase = 'MOVABLE_INK'; +const displayName = 'Movable Ink'; +const channel = 'web'; +const destination: Destination = { + Config: { + endpoint: 'https://collector.movableink-dmz.com/behavioral/abc123', + accessKey: 'test-access-key', + accessSecret: 'test_access_secret', + }, + DestinationDefinition: { + DisplayName: displayName, + ID: '123', + Name: destTypeInUpperCase, + Config: { cdkV2Enabled: true }, + }, + Enabled: true, + ID: '123', + Name: destTypeInUpperCase, + Transformations: [], + WorkspaceID: 'test-workspace-id', +}; + +const processorInstrumentationErrorStatTags = { + destType: destTypeInUpperCase, + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +const RouterInstrumentationErrorStatTags = { + ...processorInstrumentationErrorStatTags, + feature: 'router', +}; + +const traits = { + email: 'test@example.com', + firstName: 'John', + lastName: 'Doe', + phone: '1234567890', +}; + +const headers = { + 'Content-Type': 'application/json', + Authorization: 'Basic dGVzdC1hY2Nlc3Mta2V5OnRlc3RfYWNjZXNzX3NlY3JldA==', +}; + +const commonProperties = { + product_id: '622c6f5d5cf86a4c77358033', + sku: '8472-998-0112', + categories: [ + { url: 'https://example1', id: '1' }, + { url: 'https://example2', id: '2' }, + ], + name: 'Cones of Dunshire', + brand: 'Wyatt Games', + variant: 'expansion pack', + price: 49.99, + quantity: 5, + coupon: 'PREORDER15', + position: 1, + url: 'https://www.website.com/product/path', + image_url: 'https://www.website.com/product/path.webp', +}; + +const customProperties = { + key1: 'value1', + key2: true, + key3: ['value3'], + key4: { key5: { key6: 'value6' } }, +}; + +const trackTestProperties = { + 'Product Added': { ...commonProperties, ...customProperties }, + 'Product Viewed': { ...commonProperties, ...customProperties }, + 'Order Completed': { + checkout_id: '70324a1f0eaf000000000000', + order_id: '40684e8f0eaf000000000000', + affiliation: 'Vandelay Games', + total: 52, + subtotal: 45, + revenue: 50, + shipping: 4, + tax: 3, + discount: 5, + coupon: 'NEWCUST5', + currency: 'USD', + products: [ + { + product_id: '622c6f5d5cf86a4c77358033', + sku: '8472-998-0112', + name: 'Cones of Dunshire', + price: 40, + position: 1, + category: 'Games', + url: 'https://www.website.com/product/path', + image_url: 'https://www.website.com/product/path.jpg', + }, + { + product_id: '577c6f5d5cf86a4c7735ba03', + sku: '3309-483-2201', + name: 'Five Crowns', + price: 5, + position: 2, + category: 'Games', + }, + ], + }, + 'Products Searched': { query: 'HDMI cable', url: 'https://www.website.com/product/path' }, + 'Custom event': { ...commonProperties, key1: 'value1', key2: true }, +}; + +export { + destType, + channel, + destination, + processorInstrumentationErrorStatTags, + RouterInstrumentationErrorStatTags, + traits, + headers, + trackTestProperties, +}; diff --git a/test/integrations/destinations/movable_ink/processor/data.ts b/test/integrations/destinations/movable_ink/processor/data.ts new file mode 100644 index 0000000000..45453c74cd --- /dev/null +++ b/test/integrations/destinations/movable_ink/processor/data.ts @@ -0,0 +1,4 @@ +import { validation } from './validation'; +import { identify } from './identify'; +import { track } from './track'; +export const data = [...identify, ...track, ...validation]; diff --git a/test/integrations/destinations/movable_ink/processor/identify.ts b/test/integrations/destinations/movable_ink/processor/identify.ts new file mode 100644 index 0000000000..27186da05c --- /dev/null +++ b/test/integrations/destinations/movable_ink/processor/identify.ts @@ -0,0 +1,64 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, channel, destination, traits, headers } from '../common'; + +export const identify: ProcessorTestData[] = [ + { + id: 'MovableInk-identify-test-1', + name: destType, + description: 'Identify call with traits and anonymousId', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with few additional mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint: destination.Config.endpoint, + headers, + JSON: { + type: 'identify', + userId: traits.email, + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/movable_ink/processor/track.ts b/test/integrations/destinations/movable_ink/processor/track.ts new file mode 100644 index 0000000000..5f30a3de83 --- /dev/null +++ b/test/integrations/destinations/movable_ink/processor/track.ts @@ -0,0 +1,189 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, channel, destination, headers, trackTestProperties } from '../common'; + +export const track: ProcessorTestData[] = [ + { + id: 'MovableInk-track-test-1', + name: destType, + description: 'Track call: Product Added event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with few additional mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + properties: trackTestProperties['Product Added'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint: destination.Config.endpoint, + headers, + JSON: { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + properties: trackTestProperties['Product Added'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'MovableInk-track-test-2', + name: destType, + description: 'Track call: Order Completed event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with few additional mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + properties: trackTestProperties['Order Completed'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint: destination.Config.endpoint, + headers, + JSON: { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + properties: trackTestProperties['Order Completed'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'MovableInk-track-test-3', + name: destType, + description: 'Track call: Custom event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with few additional mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + properties: trackTestProperties['Custom Event'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint: destination.Config.endpoint, + headers, + JSON: { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + properties: trackTestProperties['Custom Event'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/movable_ink/processor/validation.ts b/test/integrations/destinations/movable_ink/processor/validation.ts new file mode 100644 index 0000000000..f9f6c6a927 --- /dev/null +++ b/test/integrations/destinations/movable_ink/processor/validation.ts @@ -0,0 +1,131 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata } from '../../../testUtils'; +import { destType, destination, processorInstrumentationErrorStatTags } from '../common'; + +export const validation: ProcessorTestData[] = [ + { + id: 'MovableInk-validation-test-1', + name: destType, + description: 'All of the required fields — userId, email, and anonymousId — are missing.', + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Either one of userId or email or anonymousId is required. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Either one of userId or email or anonymousId is required. Aborting', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'MovableInk-validation-test-2', + name: destType, + description: 'Unsupported message type -> group', + scenario: 'Framework', + successCriteria: 'Instrumentation Error for Unsupported message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'group', + userId: 'userId123', + channel: 'mobile', + anonymousId: 'anon_123', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'message type group is not supported: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message type group is not supported', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'MovableInk-validation-test-3', + name: destType, + description: 'Missing required field -> timestamp', + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + integrations: { + All: true, + }, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Timestamp is not present. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Timestamp is not present. Aborting', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/movable_ink/router/data.ts b/test/integrations/destinations/movable_ink/router/data.ts new file mode 100644 index 0000000000..72df3d7074 --- /dev/null +++ b/test/integrations/destinations/movable_ink/router/data.ts @@ -0,0 +1,162 @@ +import { RouterTestData } from '../../../testTypes'; +import { RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; +import { + destType, + channel, + destination, + traits, + headers, + trackTestProperties, + RouterInstrumentationErrorStatTags, +} from '../common'; + +const routerRequest: RouterTransformationRequest = { + input: [ + { + message: { + type: 'identify', + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + destination, + }, + { + message: { + type: 'identify', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(2), + destination, + }, + { + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + properties: trackTestProperties['Product Added'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(3), + destination, + }, + { + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + properties: trackTestProperties['Custom Event'], + integrations: { + All: true, + }, + }, + metadata: generateMetadata(4), + destination, + }, + ], + destType, +}; + +export const data: RouterTestData[] = [ + { + id: 'MovableInk-router-test-1', + name: destType, + description: 'Basic Router Test to test multiple payloads', + scenario: 'Framework', + successCriteria: + 'Some events should be transformed successfully and some should fail for missing fields and status code should be 200', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: routerRequest, + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: destination.Config.endpoint, + headers, + params: {}, + body: { + JSON: { + events: [ + { + type: 'identify', + userId: traits.email, + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + properties: trackTestProperties['Product Added'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [generateMetadata(1), generateMetadata(3)], + batched: true, + statusCode: 200, + destination, + }, + { + metadata: [generateMetadata(2)], + batched: false, + statusCode: 400, + error: 'Either one of userId or email or anonymousId is required. Aborting', + statTags: RouterInstrumentationErrorStatTags, + destination, + }, + { + metadata: [generateMetadata(4)], + batched: false, + statusCode: 400, + error: 'Timestamp is not present. Aborting', + statTags: RouterInstrumentationErrorStatTags, + destination, + }, + ], + }, + }, + }, + }, +]; From c5105302f5d8f518b7caaf3e8e1012ca59cddde5 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 18 Mar 2024 07:22:12 +0000 Subject: [PATCH 044/240] chore(release): 1.59.0 --- CHANGELOG.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c143851091..7390317ab5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,57 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.59.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.59.0) (2024-03-18) + + +### Features + +* add Koala destination ([#3122](https://github.com/rudderlabs/rudder-transformer/issues/3122)) ([1ca039d](https://github.com/rudderlabs/rudder-transformer/commit/1ca039d64ebb1a18a0fc6b78ed5ee08528ad6b48)) +* add support for interaction events in sfmc ([#3109](https://github.com/rudderlabs/rudder-transformer/issues/3109)) ([0486049](https://github.com/rudderlabs/rudder-transformer/commit/0486049ba2ad96b50d8f29e96b46b96a8a5c9f76)) +* add support of ([c0ad214](https://github.com/rudderlabs/rudder-transformer/commit/c0ad21463981ef66154c8157083924f76825762d)) +* add support of custom page/screen event name in mixpanel ([#3098](https://github.com/rudderlabs/rudder-transformer/issues/3098)) ([0eb2393](https://github.com/rudderlabs/rudder-transformer/commit/0eb2393939fba2452ef7f07a1d149d87f18290c3)) +* add support of skip_user_properties_sync on Amplitude ([#3181](https://github.com/rudderlabs/rudder-transformer/issues/3181)) ([5e4ddbd](https://github.com/rudderlabs/rudder-transformer/commit/5e4ddbd8a591341a581a5721505d6dcb010f2eec)) +* adding zod validations ([#3066](https://github.com/rudderlabs/rudder-transformer/issues/3066)) ([325433b](https://github.com/rudderlabs/rudder-transformer/commit/325433b9188c8d1dbe740c7e193cdc2e58fdd751)) +* consent mode support for google adwords remarketing list ([#3143](https://github.com/rudderlabs/rudder-transformer/issues/3143)) ([7532c90](https://github.com/rudderlabs/rudder-transformer/commit/7532c90d7e1feac00f12961c56da18757010f44a)) +* **facebook:** update content_type mapping logic for fb pixel and fb conversions ([#3113](https://github.com/rudderlabs/rudder-transformer/issues/3113)) ([aea417c](https://github.com/rudderlabs/rudder-transformer/commit/aea417cd2691547399010c034cadbc5db6b0c6ee)) +* klaviyo profile mapping ([#3105](https://github.com/rudderlabs/rudder-transformer/issues/3105)) ([2761786](https://github.com/rudderlabs/rudder-transformer/commit/2761786ff3fc99ed6d4d3b7a6c2400226b1cfb12)) +* onboard destination movable ink ([#3167](https://github.com/rudderlabs/rudder-transformer/issues/3167)) ([7018b1e](https://github.com/rudderlabs/rudder-transformer/commit/7018b1e5e7f37ae177191c5ecf3a71cfe2f3d147)) +* onboard new destination ninetailed ([#3106](https://github.com/rudderlabs/rudder-transformer/issues/3106)) ([0e2588e](https://github.com/rudderlabs/rudder-transformer/commit/0e2588ecd87f3b2c6877a099aa1cbf2d5325966c)) +* update proxy data type for response handler input ([7d6ea12](https://github.com/rudderlabs/rudder-transformer/commit/7d6ea123e08b793a87f35290e740cbef547c3862)) +* update proxy tests for cm360 ([9dd8625](https://github.com/rudderlabs/rudder-transformer/commit/9dd862540cc8e4e56b9bc638cc1da62e5f19c45f)) +* update proxy tests for cm360 ([#3039](https://github.com/rudderlabs/rudder-transformer/issues/3039)) ([0504ffa](https://github.com/rudderlabs/rudder-transformer/commit/0504ffa898956f5b61771fb32ecfd0e0bf15248f)) +* update proxy v1 test cases ([b1327eb](https://github.com/rudderlabs/rudder-transformer/commit/b1327ebdb049163b3c5f046cb4605518e99481f3)) +* use dontBatch directive in algolia ([#3169](https://github.com/rudderlabs/rudder-transformer/issues/3169)) ([916aaec](https://github.com/rudderlabs/rudder-transformer/commit/916aaecb1939160620d5fd3c4c0c0e33f2a371b2)) + + +### Bug Fixes + +* add error handling for tiktok ads ([#3144](https://github.com/rudderlabs/rudder-transformer/issues/3144)) ([e93e47f](https://github.com/rudderlabs/rudder-transformer/commit/e93e47f33e098104fb532916932fe38bbfeaa4a1)) +* **algolia:** added check for objectIds or filters to be non empty ([#3126](https://github.com/rudderlabs/rudder-transformer/issues/3126)) ([d619c97](https://github.com/rudderlabs/rudder-transformer/commit/d619c9769cd270cb2d16dad0865683ff4beb2d19)) +* am formatting issues ([4653b74](https://github.com/rudderlabs/rudder-transformer/commit/4653b74522cc917230c211ce1df1b57e8a607ad7)) +* api contract for v1 proxy ([76e0284](https://github.com/rudderlabs/rudder-transformer/commit/76e02848c58a6630c36f724dc4ccbac3d29a8007)) +* api contract for v1 proxy ([#3049](https://github.com/rudderlabs/rudder-transformer/issues/3049)) ([93947db](https://github.com/rudderlabs/rudder-transformer/commit/93947db35cdaf1ca7ed87ec5f73567754af312ab)) +* clevertap remove stringification of array object properties ([#3048](https://github.com/rudderlabs/rudder-transformer/issues/3048)) ([69e43b6](https://github.com/rudderlabs/rudder-transformer/commit/69e43b6ffadeaec87b7440da34a341890ceba252)) +* convert to string from null in hs ([#3136](https://github.com/rudderlabs/rudder-transformer/issues/3136)) ([75e9f46](https://github.com/rudderlabs/rudder-transformer/commit/75e9f462b0ff9b9a8abab3c78dc7d147926e9e5e)) +* email mapping for clevertap ([c1b3736](https://github.com/rudderlabs/rudder-transformer/commit/c1b3736ab60c9582bdf1c4b07a761976de0da16f)) +* email mapping for clevertap ([#3173](https://github.com/rudderlabs/rudder-transformer/issues/3173)) ([04eab92](https://github.com/rudderlabs/rudder-transformer/commit/04eab92e1c383f9e8cdd5c845530a42a0af2932a)) +* event fix and added utility ([#3142](https://github.com/rudderlabs/rudder-transformer/issues/3142)) ([9b705b7](https://github.com/rudderlabs/rudder-transformer/commit/9b705b71a9d3a595ea0fbf532602c3941b0a18db)) +* fb pixel test case refactor ([#3075](https://github.com/rudderlabs/rudder-transformer/issues/3075)) ([cff7d1c](https://github.com/rudderlabs/rudder-transformer/commit/cff7d1c4578087a37614c0ef4529058481873479)) +* fixed 500 status for algolia dontBatch ([#3178](https://github.com/rudderlabs/rudder-transformer/issues/3178)) ([6330888](https://github.com/rudderlabs/rudder-transformer/commit/6330888ad5c67e3a800037b56501fc08da09e4d1)) +* label not present in prometheus metrics ([#3176](https://github.com/rudderlabs/rudder-transformer/issues/3176)) ([01d460c](https://github.com/rudderlabs/rudder-transformer/commit/01d460c3edaf39b35c4686516c9e9140be46aa5e)) +* metadata structure correction ([#3119](https://github.com/rudderlabs/rudder-transformer/issues/3119)) ([8351b5c](https://github.com/rudderlabs/rudder-transformer/commit/8351b5cbbf81bbc14b2f884feaae4ad3ca59a39a)) +* one_signal: Encode external_id in endpoint ([#3140](https://github.com/rudderlabs/rudder-transformer/issues/3140)) ([8a20886](https://github.com/rudderlabs/rudder-transformer/commit/8a2088608d6da4b35bbb506db2fc3df1e4d41f3b)) +* prepare-for-staging-deploy.yml ([afb2f45](https://github.com/rudderlabs/rudder-transformer/commit/afb2f450ddee0522e802327dce68ac33a04c9639)) +* prepare-for-staging-deploy.yml ([05ffe82](https://github.com/rudderlabs/rudder-transformer/commit/05ffe820e5c5a3b346f39c268dd49fca47568461)) +* rakuten: sync property mapping sourcekeys to rudderstack standard spec ([#3129](https://github.com/rudderlabs/rudder-transformer/issues/3129)) ([2ebff95](https://github.com/rudderlabs/rudder-transformer/commit/2ebff956ff2aa74b008a8de832a31d8774d2d47e)) +* reddit revenue mapping for floating point values ([#3118](https://github.com/rudderlabs/rudder-transformer/issues/3118)) ([41f4078](https://github.com/rudderlabs/rudder-transformer/commit/41f4078011ef54334bb9ecc11a7b2ccc8831a4aa)) +* release action git ([#3166](https://github.com/rudderlabs/rudder-transformer/issues/3166)) ([dff7eb9](https://github.com/rudderlabs/rudder-transformer/commit/dff7eb9b8072016a16e7083c60507a9d03302f17)) +* release fix feat, bug order ([#3165](https://github.com/rudderlabs/rudder-transformer/issues/3165)) ([17da0a9](https://github.com/rudderlabs/rudder-transformer/commit/17da0a9cd2efb7b3ae061db081c737cb38d30df2)) +* send proper status to server in cm360 ([#3127](https://github.com/rudderlabs/rudder-transformer/issues/3127)) ([229ce47](https://github.com/rudderlabs/rudder-transformer/commit/229ce473af1ddd62d946bea1b018c882b142a5ef)) +* typo ([650911e](https://github.com/rudderlabs/rudder-transformer/commit/650911e44c5c99f346f4bcfd8145fcd6993d7759)) +* upload js coverage to codecov ([#3179](https://github.com/rudderlabs/rudder-transformer/issues/3179)) ([d2eba21](https://github.com/rudderlabs/rudder-transformer/commit/d2eba21191dc4f7b610414158af68e5533016014)) +* version deprecation failure false positive ([#3104](https://github.com/rudderlabs/rudder-transformer/issues/3104)) ([657b780](https://github.com/rudderlabs/rudder-transformer/commit/657b7805eb01da25a007d978198d5debf03917fd)) + ## [1.58.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.58.0) (2024-03-04) diff --git a/package-lock.json b/package-lock.json index d708860537..63f19b12c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.58.0", + "version": "1.59.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.58.0", + "version": "1.59.0", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index ec3ffbf4e6..51b8b43a54 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.58.0", + "version": "1.59.0", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From ac7e8879fcd230dae09f757a759fb42e4cdc09d0 Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Mon, 18 Mar 2024 16:00:25 +0530 Subject: [PATCH 045/240] fix: update correct staging deployment file (#3189) --- .github/workflows/prepare-for-staging-deploy.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/prepare-for-staging-deploy.yml b/.github/workflows/prepare-for-staging-deploy.yml index 1bd7e276f4..3e0b3aac19 100644 --- a/.github/workflows/prepare-for-staging-deploy.yml +++ b/.github/workflows/prepare-for-staging-deploy.yml @@ -112,9 +112,9 @@ jobs: yq eval -i ".user-transformer.image.tag=\"$TAG_NAME\"" staging.yaml git add staging.yaml - cd ../../../../config-be-rudder-transformer - yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" values.staging.yaml - yq eval -i ".config-be-user-transformer.image.tag=\"$TAG_NAME\"" values.staging.yaml + cd ../../../../config-be-rudder-transformer/environment/staging + yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" base.yaml + yq eval -i ".config-be-user-transformer.image.tag=\"$TAG_NAME\"" base.yaml git add values.staging.yaml git commit -m "chore: upgrade staging env transformers to \"$TAG_NAME\"" From 750fe8f73b153601e3c503b611d617236efb39a6 Mon Sep 17 00:00:00 2001 From: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Date: Mon, 18 Mar 2024 18:18:58 +0530 Subject: [PATCH 046/240] chore: add event validation for movable ink destination (#3190) chore: add validation for movable ink destination --- .../movable_ink/procWorkflow.yaml | 1 + src/cdk/v2/destinations/movable_ink/utils.js | 21 +++++ .../movable_ink/processor/validation.ts | 86 +++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 src/cdk/v2/destinations/movable_ink/utils.js diff --git a/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml b/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml index 25270058c5..43dbb3cbce 100644 --- a/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml +++ b/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml @@ -33,6 +33,7 @@ steps: ); $.assert(userId ?? email ?? .message.anonymousId, "Either one of userId or email or anonymousId is required. Aborting"); + $.validateEventPayload(.message); - name: preparePayload description: Prepare payload for identify and track. This payload schema needs to be configured in the Movable Ink dashboard. Movable Ink will discard any additional fields from the input payload. diff --git a/src/cdk/v2/destinations/movable_ink/utils.js b/src/cdk/v2/destinations/movable_ink/utils.js new file mode 100644 index 0000000000..04d7046b1a --- /dev/null +++ b/src/cdk/v2/destinations/movable_ink/utils.js @@ -0,0 +1,21 @@ +const { InstrumentationError } = require('@rudderstack/integrations-lib'); + +const validateEventPayload = (message) => { + const { event } = message; + const { properties } = message; + if (event === 'Products Searched' && !properties?.query) { + throw new InstrumentationError("Missing 'query' property in properties. Aborting"); + } + + if ( + (event === 'Product Added' || + event === 'Product Removed' || + event === 'Product Viewed' || + event === 'Category Viewed') && + !properties?.product_id + ) { + throw new InstrumentationError("Missing 'product_id' property in properties. Aborting"); + } +}; + +module.exports = { validateEventPayload }; diff --git a/test/integrations/destinations/movable_ink/processor/validation.ts b/test/integrations/destinations/movable_ink/processor/validation.ts index f9f6c6a927..ab6b123eb7 100644 --- a/test/integrations/destinations/movable_ink/processor/validation.ts +++ b/test/integrations/destinations/movable_ink/processor/validation.ts @@ -128,4 +128,90 @@ export const validation: ProcessorTestData[] = [ }, }, }, + { + id: 'MovableInk-validation-test-4', + name: destType, + description: "Products Searched event - Missing 'query' property", + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + userId: 'user123', + integrations: { + All: true, + }, + event: 'Products Searched', + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + "Missing 'query' property in properties. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Missing 'query' property in properties. Aborting", + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'MovableInk-validation-test-5', + name: destType, + description: "Products Added event - Missing 'product_id' property", + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + userId: 'user123', + integrations: { + All: true, + }, + event: 'Product Added', + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + "Missing 'product_id' property in properties. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Missing 'product_id' property in properties. Aborting", + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, ]; From 5876441004da398c635baca393c9acfb99794704 Mon Sep 17 00:00:00 2001 From: sandeepdigumarty Date: Tue, 19 Mar 2024 14:45:27 +0530 Subject: [PATCH 047/240] chore: updated CHANGELOG --- CHANGELOG.md | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7390317ab5..b624ed9ef7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,50 +8,27 @@ All notable changes to this project will be documented in this file. See [standa ### Features * add Koala destination ([#3122](https://github.com/rudderlabs/rudder-transformer/issues/3122)) ([1ca039d](https://github.com/rudderlabs/rudder-transformer/commit/1ca039d64ebb1a18a0fc6b78ed5ee08528ad6b48)) -* add support for interaction events in sfmc ([#3109](https://github.com/rudderlabs/rudder-transformer/issues/3109)) ([0486049](https://github.com/rudderlabs/rudder-transformer/commit/0486049ba2ad96b50d8f29e96b46b96a8a5c9f76)) -* add support of ([c0ad214](https://github.com/rudderlabs/rudder-transformer/commit/c0ad21463981ef66154c8157083924f76825762d)) -* add support of custom page/screen event name in mixpanel ([#3098](https://github.com/rudderlabs/rudder-transformer/issues/3098)) ([0eb2393](https://github.com/rudderlabs/rudder-transformer/commit/0eb2393939fba2452ef7f07a1d149d87f18290c3)) * add support of skip_user_properties_sync on Amplitude ([#3181](https://github.com/rudderlabs/rudder-transformer/issues/3181)) ([5e4ddbd](https://github.com/rudderlabs/rudder-transformer/commit/5e4ddbd8a591341a581a5721505d6dcb010f2eec)) * adding zod validations ([#3066](https://github.com/rudderlabs/rudder-transformer/issues/3066)) ([325433b](https://github.com/rudderlabs/rudder-transformer/commit/325433b9188c8d1dbe740c7e193cdc2e58fdd751)) -* consent mode support for google adwords remarketing list ([#3143](https://github.com/rudderlabs/rudder-transformer/issues/3143)) ([7532c90](https://github.com/rudderlabs/rudder-transformer/commit/7532c90d7e1feac00f12961c56da18757010f44a)) -* **facebook:** update content_type mapping logic for fb pixel and fb conversions ([#3113](https://github.com/rudderlabs/rudder-transformer/issues/3113)) ([aea417c](https://github.com/rudderlabs/rudder-transformer/commit/aea417cd2691547399010c034cadbc5db6b0c6ee)) -* klaviyo profile mapping ([#3105](https://github.com/rudderlabs/rudder-transformer/issues/3105)) ([2761786](https://github.com/rudderlabs/rudder-transformer/commit/2761786ff3fc99ed6d4d3b7a6c2400226b1cfb12)) * onboard destination movable ink ([#3167](https://github.com/rudderlabs/rudder-transformer/issues/3167)) ([7018b1e](https://github.com/rudderlabs/rudder-transformer/commit/7018b1e5e7f37ae177191c5ecf3a71cfe2f3d147)) -* onboard new destination ninetailed ([#3106](https://github.com/rudderlabs/rudder-transformer/issues/3106)) ([0e2588e](https://github.com/rudderlabs/rudder-transformer/commit/0e2588ecd87f3b2c6877a099aa1cbf2d5325966c)) -* update proxy data type for response handler input ([7d6ea12](https://github.com/rudderlabs/rudder-transformer/commit/7d6ea123e08b793a87f35290e740cbef547c3862)) -* update proxy tests for cm360 ([9dd8625](https://github.com/rudderlabs/rudder-transformer/commit/9dd862540cc8e4e56b9bc638cc1da62e5f19c45f)) * update proxy tests for cm360 ([#3039](https://github.com/rudderlabs/rudder-transformer/issues/3039)) ([0504ffa](https://github.com/rudderlabs/rudder-transformer/commit/0504ffa898956f5b61771fb32ecfd0e0bf15248f)) -* update proxy v1 test cases ([b1327eb](https://github.com/rudderlabs/rudder-transformer/commit/b1327ebdb049163b3c5f046cb4605518e99481f3)) * use dontBatch directive in algolia ([#3169](https://github.com/rudderlabs/rudder-transformer/issues/3169)) ([916aaec](https://github.com/rudderlabs/rudder-transformer/commit/916aaecb1939160620d5fd3c4c0c0e33f2a371b2)) ### Bug Fixes -* add error handling for tiktok ads ([#3144](https://github.com/rudderlabs/rudder-transformer/issues/3144)) ([e93e47f](https://github.com/rudderlabs/rudder-transformer/commit/e93e47f33e098104fb532916932fe38bbfeaa4a1)) -* **algolia:** added check for objectIds or filters to be non empty ([#3126](https://github.com/rudderlabs/rudder-transformer/issues/3126)) ([d619c97](https://github.com/rudderlabs/rudder-transformer/commit/d619c9769cd270cb2d16dad0865683ff4beb2d19)) * am formatting issues ([4653b74](https://github.com/rudderlabs/rudder-transformer/commit/4653b74522cc917230c211ce1df1b57e8a607ad7)) -* api contract for v1 proxy ([76e0284](https://github.com/rudderlabs/rudder-transformer/commit/76e02848c58a6630c36f724dc4ccbac3d29a8007)) * api contract for v1 proxy ([#3049](https://github.com/rudderlabs/rudder-transformer/issues/3049)) ([93947db](https://github.com/rudderlabs/rudder-transformer/commit/93947db35cdaf1ca7ed87ec5f73567754af312ab)) -* clevertap remove stringification of array object properties ([#3048](https://github.com/rudderlabs/rudder-transformer/issues/3048)) ([69e43b6](https://github.com/rudderlabs/rudder-transformer/commit/69e43b6ffadeaec87b7440da34a341890ceba252)) -* convert to string from null in hs ([#3136](https://github.com/rudderlabs/rudder-transformer/issues/3136)) ([75e9f46](https://github.com/rudderlabs/rudder-transformer/commit/75e9f462b0ff9b9a8abab3c78dc7d147926e9e5e)) -* email mapping for clevertap ([c1b3736](https://github.com/rudderlabs/rudder-transformer/commit/c1b3736ab60c9582bdf1c4b07a761976de0da16f)) * email mapping for clevertap ([#3173](https://github.com/rudderlabs/rudder-transformer/issues/3173)) ([04eab92](https://github.com/rudderlabs/rudder-transformer/commit/04eab92e1c383f9e8cdd5c845530a42a0af2932a)) -* event fix and added utility ([#3142](https://github.com/rudderlabs/rudder-transformer/issues/3142)) ([9b705b7](https://github.com/rudderlabs/rudder-transformer/commit/9b705b71a9d3a595ea0fbf532602c3941b0a18db)) * fb pixel test case refactor ([#3075](https://github.com/rudderlabs/rudder-transformer/issues/3075)) ([cff7d1c](https://github.com/rudderlabs/rudder-transformer/commit/cff7d1c4578087a37614c0ef4529058481873479)) * fixed 500 status for algolia dontBatch ([#3178](https://github.com/rudderlabs/rudder-transformer/issues/3178)) ([6330888](https://github.com/rudderlabs/rudder-transformer/commit/6330888ad5c67e3a800037b56501fc08da09e4d1)) * label not present in prometheus metrics ([#3176](https://github.com/rudderlabs/rudder-transformer/issues/3176)) ([01d460c](https://github.com/rudderlabs/rudder-transformer/commit/01d460c3edaf39b35c4686516c9e9140be46aa5e)) -* metadata structure correction ([#3119](https://github.com/rudderlabs/rudder-transformer/issues/3119)) ([8351b5c](https://github.com/rudderlabs/rudder-transformer/commit/8351b5cbbf81bbc14b2f884feaae4ad3ca59a39a)) -* one_signal: Encode external_id in endpoint ([#3140](https://github.com/rudderlabs/rudder-transformer/issues/3140)) ([8a20886](https://github.com/rudderlabs/rudder-transformer/commit/8a2088608d6da4b35bbb506db2fc3df1e4d41f3b)) * prepare-for-staging-deploy.yml ([afb2f45](https://github.com/rudderlabs/rudder-transformer/commit/afb2f450ddee0522e802327dce68ac33a04c9639)) * prepare-for-staging-deploy.yml ([05ffe82](https://github.com/rudderlabs/rudder-transformer/commit/05ffe820e5c5a3b346f39c268dd49fca47568461)) -* rakuten: sync property mapping sourcekeys to rudderstack standard spec ([#3129](https://github.com/rudderlabs/rudder-transformer/issues/3129)) ([2ebff95](https://github.com/rudderlabs/rudder-transformer/commit/2ebff956ff2aa74b008a8de832a31d8774d2d47e)) -* reddit revenue mapping for floating point values ([#3118](https://github.com/rudderlabs/rudder-transformer/issues/3118)) ([41f4078](https://github.com/rudderlabs/rudder-transformer/commit/41f4078011ef54334bb9ecc11a7b2ccc8831a4aa)) * release action git ([#3166](https://github.com/rudderlabs/rudder-transformer/issues/3166)) ([dff7eb9](https://github.com/rudderlabs/rudder-transformer/commit/dff7eb9b8072016a16e7083c60507a9d03302f17)) * release fix feat, bug order ([#3165](https://github.com/rudderlabs/rudder-transformer/issues/3165)) ([17da0a9](https://github.com/rudderlabs/rudder-transformer/commit/17da0a9cd2efb7b3ae061db081c737cb38d30df2)) * send proper status to server in cm360 ([#3127](https://github.com/rudderlabs/rudder-transformer/issues/3127)) ([229ce47](https://github.com/rudderlabs/rudder-transformer/commit/229ce473af1ddd62d946bea1b018c882b142a5ef)) -* typo ([650911e](https://github.com/rudderlabs/rudder-transformer/commit/650911e44c5c99f346f4bcfd8145fcd6993d7759)) * upload js coverage to codecov ([#3179](https://github.com/rudderlabs/rudder-transformer/issues/3179)) ([d2eba21](https://github.com/rudderlabs/rudder-transformer/commit/d2eba21191dc4f7b610414158af68e5533016014)) -* version deprecation failure false positive ([#3104](https://github.com/rudderlabs/rudder-transformer/issues/3104)) ([657b780](https://github.com/rudderlabs/rudder-transformer/commit/657b7805eb01da25a007d978198d5debf03917fd)) ## [1.58.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.58.0) (2024-03-04) From 05ebcc8269667cb2be9437d9779aec7328fe2bda Mon Sep 17 00:00:00 2001 From: sandeepdigumarty Date: Tue, 19 Mar 2024 15:42:38 +0530 Subject: [PATCH 048/240] chore: updated CHANGELOG --- CHANGELOG.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b624ed9ef7..d3f2b57d19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,18 +17,12 @@ All notable changes to this project will be documented in this file. See [standa ### Bug Fixes -* am formatting issues ([4653b74](https://github.com/rudderlabs/rudder-transformer/commit/4653b74522cc917230c211ce1df1b57e8a607ad7)) * api contract for v1 proxy ([#3049](https://github.com/rudderlabs/rudder-transformer/issues/3049)) ([93947db](https://github.com/rudderlabs/rudder-transformer/commit/93947db35cdaf1ca7ed87ec5f73567754af312ab)) * email mapping for clevertap ([#3173](https://github.com/rudderlabs/rudder-transformer/issues/3173)) ([04eab92](https://github.com/rudderlabs/rudder-transformer/commit/04eab92e1c383f9e8cdd5c845530a42a0af2932a)) * fb pixel test case refactor ([#3075](https://github.com/rudderlabs/rudder-transformer/issues/3075)) ([cff7d1c](https://github.com/rudderlabs/rudder-transformer/commit/cff7d1c4578087a37614c0ef4529058481873479)) * fixed 500 status for algolia dontBatch ([#3178](https://github.com/rudderlabs/rudder-transformer/issues/3178)) ([6330888](https://github.com/rudderlabs/rudder-transformer/commit/6330888ad5c67e3a800037b56501fc08da09e4d1)) * label not present in prometheus metrics ([#3176](https://github.com/rudderlabs/rudder-transformer/issues/3176)) ([01d460c](https://github.com/rudderlabs/rudder-transformer/commit/01d460c3edaf39b35c4686516c9e9140be46aa5e)) -* prepare-for-staging-deploy.yml ([afb2f45](https://github.com/rudderlabs/rudder-transformer/commit/afb2f450ddee0522e802327dce68ac33a04c9639)) -* prepare-for-staging-deploy.yml ([05ffe82](https://github.com/rudderlabs/rudder-transformer/commit/05ffe820e5c5a3b346f39c268dd49fca47568461)) -* release action git ([#3166](https://github.com/rudderlabs/rudder-transformer/issues/3166)) ([dff7eb9](https://github.com/rudderlabs/rudder-transformer/commit/dff7eb9b8072016a16e7083c60507a9d03302f17)) -* release fix feat, bug order ([#3165](https://github.com/rudderlabs/rudder-transformer/issues/3165)) ([17da0a9](https://github.com/rudderlabs/rudder-transformer/commit/17da0a9cd2efb7b3ae061db081c737cb38d30df2)) * send proper status to server in cm360 ([#3127](https://github.com/rudderlabs/rudder-transformer/issues/3127)) ([229ce47](https://github.com/rudderlabs/rudder-transformer/commit/229ce473af1ddd62d946bea1b018c882b142a5ef)) -* upload js coverage to codecov ([#3179](https://github.com/rudderlabs/rudder-transformer/issues/3179)) ([d2eba21](https://github.com/rudderlabs/rudder-transformer/commit/d2eba21191dc4f7b610414158af68e5533016014)) ## [1.58.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.58.0) (2024-03-04) From c5cc69b423b4b83a69b0568e837b9b0d074028a7 Mon Sep 17 00:00:00 2001 From: Utsab Chowdhury Date: Tue, 19 Mar 2024 18:24:27 +0530 Subject: [PATCH 049/240] chore: enable proxy v1 --- src/features.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features.json b/src/features.json index 76b562a825..267923fdb4 100644 --- a/src/features.json +++ b/src/features.json @@ -85,5 +85,5 @@ "SPRIG" ], "supportSourceTransformV1": true, - "supportTransformerProxyV1": false + "supportTransformerProxyV1": true } From 91fc0fb3e9eeb127298a0ce305ef6d1d7b72a39f Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Wed, 20 Mar 2024 15:23:48 +0530 Subject: [PATCH 050/240] feat: ninetailed: add default value for context.location as {} (#3197) feat: ninetailed: add a default value to context.location --- .../ninetailed/data/contextMapping.json | 5 +- .../destinations/ninetailed/commonConfig.ts | 36 +++++++- .../ninetailed/processor/identify.ts | 89 +++++++++++++++++++ 3 files changed, 128 insertions(+), 2 deletions(-) diff --git a/src/cdk/v2/destinations/ninetailed/data/contextMapping.json b/src/cdk/v2/destinations/ninetailed/data/contextMapping.json index 3d6392dd1e..f2373b61c1 100644 --- a/src/cdk/v2/destinations/ninetailed/data/contextMapping.json +++ b/src/cdk/v2/destinations/ninetailed/data/contextMapping.json @@ -37,7 +37,10 @@ }, { "sourceKeys": "location", - "required": true, + "required": false, + "metadata": { + "defaultValue": {} + }, "destKey": "location" } ] diff --git a/test/integrations/destinations/ninetailed/commonConfig.ts b/test/integrations/destinations/ninetailed/commonConfig.ts index 3b5d4149f2..4baf72dee1 100644 --- a/test/integrations/destinations/ninetailed/commonConfig.ts +++ b/test/integrations/destinations/ninetailed/commonConfig.ts @@ -71,7 +71,41 @@ export const context = { timezone: 'America/Los_Angeles', }, }; - +export const contextWithNoLocation = { + app: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + campaign: { + name: 'campign_123', + source: 'social marketing', + medium: 'facebook', + term: '1 year', + }, + library: { + name: 'RudderstackSDK', + version: 'Ruddderstack SDK version', + }, + locale: 'en-US', + page: { + path: '/signup', + referrer: 'https://rudderstack.medium.com/', + search: '?type=freetrial', + url: 'https://app.rudderstack.com/signup?type=freetrial', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', +}; +export const commonInputWithNoLocation = { + anonymousId: 'anon_123', + messageId: 'dummy_msg_id', + context: contextWithNoLocation, + channel: 'web', + integrations: { + All: true, + }, + originalTimestamp: '2021-01-25T15:32:56.409Z', +}; export const commonInput = { anonymousId: 'anon_123', messageId: 'dummy_msg_id', diff --git a/test/integrations/destinations/ninetailed/processor/identify.ts b/test/integrations/destinations/ninetailed/processor/identify.ts index fbd7379e19..3bb333c160 100644 --- a/test/integrations/destinations/ninetailed/processor/identify.ts +++ b/test/integrations/destinations/ninetailed/processor/identify.ts @@ -2,6 +2,7 @@ import { destination, traits, commonInput, + commonInputWithNoLocation, metadata, processInstrumentationErrorStatTags, } from '../commonConfig'; @@ -152,4 +153,92 @@ export const identify = [ }, }, }, + { + id: 'ninetailed-test-identify-success-3', + name: 'ninetailed', + description: 'identify call with no context.location present and {} is used as default', + scenario: 'Framework+Buisness', + successCriteria: 'Response should contain context.location as {} and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + ...commonInputWithNoLocation, + userId: 'sajal12', + traits: traits, + integrations: { + All: true, + }, + originalTimestamp: '2021-01-25T15:32:56.409Z', + }, + metadata, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + metadata: { + destinationId: 'dummyDestId', + }, + output: transformResultBuilder({ + method: 'POST', + endpoint: + 'https://experience.ninetailed.co/v2/organizations/dummyOrganisationId/environments/main/events', + JSON: { + events: [ + { + context: { + app: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + campaign: { + name: 'campign_123', + source: 'social marketing', + medium: 'facebook', + term: '1 year', + }, + library: { + name: 'RudderstackSDK', + version: 'Ruddderstack SDK version', + }, + locale: 'en-US', + page: { + path: '/signup', + referrer: 'https://rudderstack.medium.com/', + search: '?type=freetrial', + url: 'https://app.rudderstack.com/signup?type=freetrial', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + location: {}, + }, + type: 'identify', + channel: 'web', + userId: 'sajal12', + messageId: 'dummy_msg_id', + traits: traits, + anonymousId: 'anon_123', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }, + ], + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, ]; From 6a7c534a7df812bb7e39c1905eadcc29d7cd1329 Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Wed, 20 Mar 2024 15:24:37 +0530 Subject: [PATCH 051/240] fix: heap: make userId as required for track and identify call (#3198) fix: heap: make userId as required for track and idenitfy call --- .../v2/destinations/heap/procWorkflow.yaml | 4 +- .../destinations/heap/processor/data.ts | 101 ++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/cdk/v2/destinations/heap/procWorkflow.yaml b/src/cdk/v2/destinations/heap/procWorkflow.yaml index 8326a61a79..ac12e7e02a 100644 --- a/src/cdk/v2/destinations/heap/procWorkflow.yaml +++ b/src/cdk/v2/destinations/heap/procWorkflow.yaml @@ -40,7 +40,9 @@ steps: }); .message.properties.idempotencyKey ? ($.context.payload.idempotency_key = .message.properties.idempotencyKey); - + - name: validateuserId + template: | + $.assert($.context.payload.identity, "userId is required"); - name: finalPayload description: In batchMode we return payload directly condition: $.batchMode diff --git a/test/integrations/destinations/heap/processor/data.ts b/test/integrations/destinations/heap/processor/data.ts index 6efa45435c..f503134148 100644 --- a/test/integrations/destinations/heap/processor/data.ts +++ b/test/integrations/destinations/heap/processor/data.ts @@ -961,4 +961,105 @@ export const data = [ }, }, }, + { + name: 'heap', + description: 'Test 8 -> Identify: No userId is present', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + appId: '', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + DisplayName: 'Heap.io', + ID: '1WTbl0l5GjOQKOvfmcGwk0T49kV', + Name: 'HEAP', + }, + Enabled: true, + ID: '1WTcDSEOE437e4ePH10BJNELXmE', + Name: 'heap test', + Transformations: [], + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + ip: '0.0.0.0', + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + locale: 'en-US', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', + }, + integrations: { + All: true, + }, + traits: { + email: 'sampath@gmail.com', + }, + messageId: 'fca2e71a-5d30-48e1-ba45-761c16e3820f', + originalTimestamp: '2020-01-16T13:21:59.076Z', + receivedAt: '2020-01-16T18:52:03.871+05:30', + request_ip: '[::1]:62312', + sentAt: '2020-01-16T13:22:03.85Z', + timestamp: '2020-01-16T18:51:59.097+05:30', + type: 'identify', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'userId is required: Workflow: procWorkflow, Step: validateuserId, ChildStep: undefined, OriginalError: userId is required', + statTags: { + destinationId: 'destId', + workspaceId: 'wspId', + destType: 'HEAP', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + }, + statusCode: 400, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, ]; From 38e36838bdee3a46bd1355632633e20ce2c8932f Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 20 Mar 2024 10:52:45 +0000 Subject: [PATCH 052/240] chore(release): 1.60.0 --- CHANGELOG.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3f2b57d19..ccc3c560a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,59 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.60.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.60.0) (2024-03-20) + + +### Features + +* add Koala destination ([#3122](https://github.com/rudderlabs/rudder-transformer/issues/3122)) ([1ca039d](https://github.com/rudderlabs/rudder-transformer/commit/1ca039d64ebb1a18a0fc6b78ed5ee08528ad6b48)) +* add support for interaction events in sfmc ([#3109](https://github.com/rudderlabs/rudder-transformer/issues/3109)) ([0486049](https://github.com/rudderlabs/rudder-transformer/commit/0486049ba2ad96b50d8f29e96b46b96a8a5c9f76)) +* add support of ([c0ad214](https://github.com/rudderlabs/rudder-transformer/commit/c0ad21463981ef66154c8157083924f76825762d)) +* add support of custom page/screen event name in mixpanel ([#3098](https://github.com/rudderlabs/rudder-transformer/issues/3098)) ([0eb2393](https://github.com/rudderlabs/rudder-transformer/commit/0eb2393939fba2452ef7f07a1d149d87f18290c3)) +* add support of skip_user_properties_sync on Amplitude ([#3181](https://github.com/rudderlabs/rudder-transformer/issues/3181)) ([5e4ddbd](https://github.com/rudderlabs/rudder-transformer/commit/5e4ddbd8a591341a581a5721505d6dcb010f2eec)) +* adding zod validations ([#3066](https://github.com/rudderlabs/rudder-transformer/issues/3066)) ([325433b](https://github.com/rudderlabs/rudder-transformer/commit/325433b9188c8d1dbe740c7e193cdc2e58fdd751)) +* consent mode support for google adwords remarketing list ([#3143](https://github.com/rudderlabs/rudder-transformer/issues/3143)) ([7532c90](https://github.com/rudderlabs/rudder-transformer/commit/7532c90d7e1feac00f12961c56da18757010f44a)) +* **facebook:** update content_type mapping logic for fb pixel and fb conversions ([#3113](https://github.com/rudderlabs/rudder-transformer/issues/3113)) ([aea417c](https://github.com/rudderlabs/rudder-transformer/commit/aea417cd2691547399010c034cadbc5db6b0c6ee)) +* klaviyo profile mapping ([#3105](https://github.com/rudderlabs/rudder-transformer/issues/3105)) ([2761786](https://github.com/rudderlabs/rudder-transformer/commit/2761786ff3fc99ed6d4d3b7a6c2400226b1cfb12)) +* ninetailed: add default value for context.location as {} ([#3197](https://github.com/rudderlabs/rudder-transformer/issues/3197)) ([91fc0fb](https://github.com/rudderlabs/rudder-transformer/commit/91fc0fb3e9eeb127298a0ce305ef6d1d7b72a39f)) +* onboard destination movable ink ([#3167](https://github.com/rudderlabs/rudder-transformer/issues/3167)) ([7018b1e](https://github.com/rudderlabs/rudder-transformer/commit/7018b1e5e7f37ae177191c5ecf3a71cfe2f3d147)) +* onboard new destination ninetailed ([#3106](https://github.com/rudderlabs/rudder-transformer/issues/3106)) ([0e2588e](https://github.com/rudderlabs/rudder-transformer/commit/0e2588ecd87f3b2c6877a099aa1cbf2d5325966c)) +* update proxy data type for response handler input ([7d6ea12](https://github.com/rudderlabs/rudder-transformer/commit/7d6ea123e08b793a87f35290e740cbef547c3862)) +* update proxy tests for cm360 ([9dd8625](https://github.com/rudderlabs/rudder-transformer/commit/9dd862540cc8e4e56b9bc638cc1da62e5f19c45f)) +* update proxy tests for cm360 ([#3039](https://github.com/rudderlabs/rudder-transformer/issues/3039)) ([0504ffa](https://github.com/rudderlabs/rudder-transformer/commit/0504ffa898956f5b61771fb32ecfd0e0bf15248f)) +* update proxy v1 test cases ([b1327eb](https://github.com/rudderlabs/rudder-transformer/commit/b1327ebdb049163b3c5f046cb4605518e99481f3)) +* use dontBatch directive in algolia ([#3169](https://github.com/rudderlabs/rudder-transformer/issues/3169)) ([916aaec](https://github.com/rudderlabs/rudder-transformer/commit/916aaecb1939160620d5fd3c4c0c0e33f2a371b2)) + + +### Bug Fixes + +* add error handling for tiktok ads ([#3144](https://github.com/rudderlabs/rudder-transformer/issues/3144)) ([e93e47f](https://github.com/rudderlabs/rudder-transformer/commit/e93e47f33e098104fb532916932fe38bbfeaa4a1)) +* **algolia:** added check for objectIds or filters to be non empty ([#3126](https://github.com/rudderlabs/rudder-transformer/issues/3126)) ([d619c97](https://github.com/rudderlabs/rudder-transformer/commit/d619c9769cd270cb2d16dad0865683ff4beb2d19)) +* am formatting issues ([4653b74](https://github.com/rudderlabs/rudder-transformer/commit/4653b74522cc917230c211ce1df1b57e8a607ad7)) +* api contract for v1 proxy ([76e0284](https://github.com/rudderlabs/rudder-transformer/commit/76e02848c58a6630c36f724dc4ccbac3d29a8007)) +* api contract for v1 proxy ([#3049](https://github.com/rudderlabs/rudder-transformer/issues/3049)) ([93947db](https://github.com/rudderlabs/rudder-transformer/commit/93947db35cdaf1ca7ed87ec5f73567754af312ab)) +* clevertap remove stringification of array object properties ([#3048](https://github.com/rudderlabs/rudder-transformer/issues/3048)) ([69e43b6](https://github.com/rudderlabs/rudder-transformer/commit/69e43b6ffadeaec87b7440da34a341890ceba252)) +* convert to string from null in hs ([#3136](https://github.com/rudderlabs/rudder-transformer/issues/3136)) ([75e9f46](https://github.com/rudderlabs/rudder-transformer/commit/75e9f462b0ff9b9a8abab3c78dc7d147926e9e5e)) +* email mapping for clevertap ([c1b3736](https://github.com/rudderlabs/rudder-transformer/commit/c1b3736ab60c9582bdf1c4b07a761976de0da16f)) +* email mapping for clevertap ([#3173](https://github.com/rudderlabs/rudder-transformer/issues/3173)) ([04eab92](https://github.com/rudderlabs/rudder-transformer/commit/04eab92e1c383f9e8cdd5c845530a42a0af2932a)) +* event fix and added utility ([#3142](https://github.com/rudderlabs/rudder-transformer/issues/3142)) ([9b705b7](https://github.com/rudderlabs/rudder-transformer/commit/9b705b71a9d3a595ea0fbf532602c3941b0a18db)) +* fb pixel test case refactor ([#3075](https://github.com/rudderlabs/rudder-transformer/issues/3075)) ([cff7d1c](https://github.com/rudderlabs/rudder-transformer/commit/cff7d1c4578087a37614c0ef4529058481873479)) +* fixed 500 status for algolia dontBatch ([#3178](https://github.com/rudderlabs/rudder-transformer/issues/3178)) ([6330888](https://github.com/rudderlabs/rudder-transformer/commit/6330888ad5c67e3a800037b56501fc08da09e4d1)) +* heap: make userId as required for track and identify call ([#3198](https://github.com/rudderlabs/rudder-transformer/issues/3198)) ([6a7c534](https://github.com/rudderlabs/rudder-transformer/commit/6a7c534a7df812bb7e39c1905eadcc29d7cd1329)) +* label not present in prometheus metrics ([#3176](https://github.com/rudderlabs/rudder-transformer/issues/3176)) ([01d460c](https://github.com/rudderlabs/rudder-transformer/commit/01d460c3edaf39b35c4686516c9e9140be46aa5e)) +* metadata structure correction ([#3119](https://github.com/rudderlabs/rudder-transformer/issues/3119)) ([8351b5c](https://github.com/rudderlabs/rudder-transformer/commit/8351b5cbbf81bbc14b2f884feaae4ad3ca59a39a)) +* one_signal: Encode external_id in endpoint ([#3140](https://github.com/rudderlabs/rudder-transformer/issues/3140)) ([8a20886](https://github.com/rudderlabs/rudder-transformer/commit/8a2088608d6da4b35bbb506db2fc3df1e4d41f3b)) +* prepare-for-staging-deploy.yml ([afb2f45](https://github.com/rudderlabs/rudder-transformer/commit/afb2f450ddee0522e802327dce68ac33a04c9639)) +* prepare-for-staging-deploy.yml ([05ffe82](https://github.com/rudderlabs/rudder-transformer/commit/05ffe820e5c5a3b346f39c268dd49fca47568461)) +* rakuten: sync property mapping sourcekeys to rudderstack standard spec ([#3129](https://github.com/rudderlabs/rudder-transformer/issues/3129)) ([2ebff95](https://github.com/rudderlabs/rudder-transformer/commit/2ebff956ff2aa74b008a8de832a31d8774d2d47e)) +* reddit revenue mapping for floating point values ([#3118](https://github.com/rudderlabs/rudder-transformer/issues/3118)) ([41f4078](https://github.com/rudderlabs/rudder-transformer/commit/41f4078011ef54334bb9ecc11a7b2ccc8831a4aa)) +* release action git ([#3166](https://github.com/rudderlabs/rudder-transformer/issues/3166)) ([dff7eb9](https://github.com/rudderlabs/rudder-transformer/commit/dff7eb9b8072016a16e7083c60507a9d03302f17)) +* release fix feat, bug order ([#3165](https://github.com/rudderlabs/rudder-transformer/issues/3165)) ([17da0a9](https://github.com/rudderlabs/rudder-transformer/commit/17da0a9cd2efb7b3ae061db081c737cb38d30df2)) +* send proper status to server in cm360 ([#3127](https://github.com/rudderlabs/rudder-transformer/issues/3127)) ([229ce47](https://github.com/rudderlabs/rudder-transformer/commit/229ce473af1ddd62d946bea1b018c882b142a5ef)) +* typo ([650911e](https://github.com/rudderlabs/rudder-transformer/commit/650911e44c5c99f346f4bcfd8145fcd6993d7759)) +* upload js coverage to codecov ([#3179](https://github.com/rudderlabs/rudder-transformer/issues/3179)) ([d2eba21](https://github.com/rudderlabs/rudder-transformer/commit/d2eba21191dc4f7b610414158af68e5533016014)) +* version deprecation failure false positive ([#3104](https://github.com/rudderlabs/rudder-transformer/issues/3104)) ([657b780](https://github.com/rudderlabs/rudder-transformer/commit/657b7805eb01da25a007d978198d5debf03917fd)) + ## [1.59.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.59.0) (2024-03-18) diff --git a/package-lock.json b/package-lock.json index 63f19b12c3..5701e64a62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.59.0", + "version": "1.60.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.59.0", + "version": "1.60.0", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index 51b8b43a54..5c1d9c1848 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.59.0", + "version": "1.60.0", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From f51a01adafa96a50dd60dc4f47ed6b272dfc2e94 Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Thu, 21 Mar 2024 12:52:54 +0530 Subject: [PATCH 053/240] Update CHANGELOG.md --- CHANGELOG.md | 41 ----------------------------------------- 1 file changed, 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccc3c560a1..da2fd7c565 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,53 +7,12 @@ All notable changes to this project will be documented in this file. See [standa ### Features -* add Koala destination ([#3122](https://github.com/rudderlabs/rudder-transformer/issues/3122)) ([1ca039d](https://github.com/rudderlabs/rudder-transformer/commit/1ca039d64ebb1a18a0fc6b78ed5ee08528ad6b48)) -* add support for interaction events in sfmc ([#3109](https://github.com/rudderlabs/rudder-transformer/issues/3109)) ([0486049](https://github.com/rudderlabs/rudder-transformer/commit/0486049ba2ad96b50d8f29e96b46b96a8a5c9f76)) -* add support of ([c0ad214](https://github.com/rudderlabs/rudder-transformer/commit/c0ad21463981ef66154c8157083924f76825762d)) -* add support of custom page/screen event name in mixpanel ([#3098](https://github.com/rudderlabs/rudder-transformer/issues/3098)) ([0eb2393](https://github.com/rudderlabs/rudder-transformer/commit/0eb2393939fba2452ef7f07a1d149d87f18290c3)) -* add support of skip_user_properties_sync on Amplitude ([#3181](https://github.com/rudderlabs/rudder-transformer/issues/3181)) ([5e4ddbd](https://github.com/rudderlabs/rudder-transformer/commit/5e4ddbd8a591341a581a5721505d6dcb010f2eec)) -* adding zod validations ([#3066](https://github.com/rudderlabs/rudder-transformer/issues/3066)) ([325433b](https://github.com/rudderlabs/rudder-transformer/commit/325433b9188c8d1dbe740c7e193cdc2e58fdd751)) -* consent mode support for google adwords remarketing list ([#3143](https://github.com/rudderlabs/rudder-transformer/issues/3143)) ([7532c90](https://github.com/rudderlabs/rudder-transformer/commit/7532c90d7e1feac00f12961c56da18757010f44a)) -* **facebook:** update content_type mapping logic for fb pixel and fb conversions ([#3113](https://github.com/rudderlabs/rudder-transformer/issues/3113)) ([aea417c](https://github.com/rudderlabs/rudder-transformer/commit/aea417cd2691547399010c034cadbc5db6b0c6ee)) -* klaviyo profile mapping ([#3105](https://github.com/rudderlabs/rudder-transformer/issues/3105)) ([2761786](https://github.com/rudderlabs/rudder-transformer/commit/2761786ff3fc99ed6d4d3b7a6c2400226b1cfb12)) * ninetailed: add default value for context.location as {} ([#3197](https://github.com/rudderlabs/rudder-transformer/issues/3197)) ([91fc0fb](https://github.com/rudderlabs/rudder-transformer/commit/91fc0fb3e9eeb127298a0ce305ef6d1d7b72a39f)) -* onboard destination movable ink ([#3167](https://github.com/rudderlabs/rudder-transformer/issues/3167)) ([7018b1e](https://github.com/rudderlabs/rudder-transformer/commit/7018b1e5e7f37ae177191c5ecf3a71cfe2f3d147)) -* onboard new destination ninetailed ([#3106](https://github.com/rudderlabs/rudder-transformer/issues/3106)) ([0e2588e](https://github.com/rudderlabs/rudder-transformer/commit/0e2588ecd87f3b2c6877a099aa1cbf2d5325966c)) -* update proxy data type for response handler input ([7d6ea12](https://github.com/rudderlabs/rudder-transformer/commit/7d6ea123e08b793a87f35290e740cbef547c3862)) -* update proxy tests for cm360 ([9dd8625](https://github.com/rudderlabs/rudder-transformer/commit/9dd862540cc8e4e56b9bc638cc1da62e5f19c45f)) -* update proxy tests for cm360 ([#3039](https://github.com/rudderlabs/rudder-transformer/issues/3039)) ([0504ffa](https://github.com/rudderlabs/rudder-transformer/commit/0504ffa898956f5b61771fb32ecfd0e0bf15248f)) -* update proxy v1 test cases ([b1327eb](https://github.com/rudderlabs/rudder-transformer/commit/b1327ebdb049163b3c5f046cb4605518e99481f3)) -* use dontBatch directive in algolia ([#3169](https://github.com/rudderlabs/rudder-transformer/issues/3169)) ([916aaec](https://github.com/rudderlabs/rudder-transformer/commit/916aaecb1939160620d5fd3c4c0c0e33f2a371b2)) ### Bug Fixes -* add error handling for tiktok ads ([#3144](https://github.com/rudderlabs/rudder-transformer/issues/3144)) ([e93e47f](https://github.com/rudderlabs/rudder-transformer/commit/e93e47f33e098104fb532916932fe38bbfeaa4a1)) -* **algolia:** added check for objectIds or filters to be non empty ([#3126](https://github.com/rudderlabs/rudder-transformer/issues/3126)) ([d619c97](https://github.com/rudderlabs/rudder-transformer/commit/d619c9769cd270cb2d16dad0865683ff4beb2d19)) -* am formatting issues ([4653b74](https://github.com/rudderlabs/rudder-transformer/commit/4653b74522cc917230c211ce1df1b57e8a607ad7)) -* api contract for v1 proxy ([76e0284](https://github.com/rudderlabs/rudder-transformer/commit/76e02848c58a6630c36f724dc4ccbac3d29a8007)) -* api contract for v1 proxy ([#3049](https://github.com/rudderlabs/rudder-transformer/issues/3049)) ([93947db](https://github.com/rudderlabs/rudder-transformer/commit/93947db35cdaf1ca7ed87ec5f73567754af312ab)) -* clevertap remove stringification of array object properties ([#3048](https://github.com/rudderlabs/rudder-transformer/issues/3048)) ([69e43b6](https://github.com/rudderlabs/rudder-transformer/commit/69e43b6ffadeaec87b7440da34a341890ceba252)) -* convert to string from null in hs ([#3136](https://github.com/rudderlabs/rudder-transformer/issues/3136)) ([75e9f46](https://github.com/rudderlabs/rudder-transformer/commit/75e9f462b0ff9b9a8abab3c78dc7d147926e9e5e)) -* email mapping for clevertap ([c1b3736](https://github.com/rudderlabs/rudder-transformer/commit/c1b3736ab60c9582bdf1c4b07a761976de0da16f)) -* email mapping for clevertap ([#3173](https://github.com/rudderlabs/rudder-transformer/issues/3173)) ([04eab92](https://github.com/rudderlabs/rudder-transformer/commit/04eab92e1c383f9e8cdd5c845530a42a0af2932a)) -* event fix and added utility ([#3142](https://github.com/rudderlabs/rudder-transformer/issues/3142)) ([9b705b7](https://github.com/rudderlabs/rudder-transformer/commit/9b705b71a9d3a595ea0fbf532602c3941b0a18db)) -* fb pixel test case refactor ([#3075](https://github.com/rudderlabs/rudder-transformer/issues/3075)) ([cff7d1c](https://github.com/rudderlabs/rudder-transformer/commit/cff7d1c4578087a37614c0ef4529058481873479)) -* fixed 500 status for algolia dontBatch ([#3178](https://github.com/rudderlabs/rudder-transformer/issues/3178)) ([6330888](https://github.com/rudderlabs/rudder-transformer/commit/6330888ad5c67e3a800037b56501fc08da09e4d1)) * heap: make userId as required for track and identify call ([#3198](https://github.com/rudderlabs/rudder-transformer/issues/3198)) ([6a7c534](https://github.com/rudderlabs/rudder-transformer/commit/6a7c534a7df812bb7e39c1905eadcc29d7cd1329)) -* label not present in prometheus metrics ([#3176](https://github.com/rudderlabs/rudder-transformer/issues/3176)) ([01d460c](https://github.com/rudderlabs/rudder-transformer/commit/01d460c3edaf39b35c4686516c9e9140be46aa5e)) -* metadata structure correction ([#3119](https://github.com/rudderlabs/rudder-transformer/issues/3119)) ([8351b5c](https://github.com/rudderlabs/rudder-transformer/commit/8351b5cbbf81bbc14b2f884feaae4ad3ca59a39a)) -* one_signal: Encode external_id in endpoint ([#3140](https://github.com/rudderlabs/rudder-transformer/issues/3140)) ([8a20886](https://github.com/rudderlabs/rudder-transformer/commit/8a2088608d6da4b35bbb506db2fc3df1e4d41f3b)) -* prepare-for-staging-deploy.yml ([afb2f45](https://github.com/rudderlabs/rudder-transformer/commit/afb2f450ddee0522e802327dce68ac33a04c9639)) -* prepare-for-staging-deploy.yml ([05ffe82](https://github.com/rudderlabs/rudder-transformer/commit/05ffe820e5c5a3b346f39c268dd49fca47568461)) -* rakuten: sync property mapping sourcekeys to rudderstack standard spec ([#3129](https://github.com/rudderlabs/rudder-transformer/issues/3129)) ([2ebff95](https://github.com/rudderlabs/rudder-transformer/commit/2ebff956ff2aa74b008a8de832a31d8774d2d47e)) -* reddit revenue mapping for floating point values ([#3118](https://github.com/rudderlabs/rudder-transformer/issues/3118)) ([41f4078](https://github.com/rudderlabs/rudder-transformer/commit/41f4078011ef54334bb9ecc11a7b2ccc8831a4aa)) -* release action git ([#3166](https://github.com/rudderlabs/rudder-transformer/issues/3166)) ([dff7eb9](https://github.com/rudderlabs/rudder-transformer/commit/dff7eb9b8072016a16e7083c60507a9d03302f17)) -* release fix feat, bug order ([#3165](https://github.com/rudderlabs/rudder-transformer/issues/3165)) ([17da0a9](https://github.com/rudderlabs/rudder-transformer/commit/17da0a9cd2efb7b3ae061db081c737cb38d30df2)) -* send proper status to server in cm360 ([#3127](https://github.com/rudderlabs/rudder-transformer/issues/3127)) ([229ce47](https://github.com/rudderlabs/rudder-transformer/commit/229ce473af1ddd62d946bea1b018c882b142a5ef)) -* typo ([650911e](https://github.com/rudderlabs/rudder-transformer/commit/650911e44c5c99f346f4bcfd8145fcd6993d7759)) -* upload js coverage to codecov ([#3179](https://github.com/rudderlabs/rudder-transformer/issues/3179)) ([d2eba21](https://github.com/rudderlabs/rudder-transformer/commit/d2eba21191dc4f7b610414158af68e5533016014)) -* version deprecation failure false positive ([#3104](https://github.com/rudderlabs/rudder-transformer/issues/3104)) ([657b780](https://github.com/rudderlabs/rudder-transformer/commit/657b7805eb01da25a007d978198d5debf03917fd)) ## [1.59.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.59.0) (2024-03-18) From 2aac2a62547b7a7c617735fc3d6e88e0a1bed76e Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Fri, 22 Mar 2024 16:03:02 +0530 Subject: [PATCH 054/240] feat: consent field support for gaoc for API v15 and upgrade the api version from v14 to v16 (#3121) * feat: consent field support for GAOC * fix: clean up of commented code * fix: clean up the old implementation of GARL to avoid code duplication * fix: small edit * chore: store sales updated, review comments addressed * fix: relocating the util and test cases * fix: refactoring the util * fix: updating API version to v16 * fix: review comments address * Update src/v0/destinations/google_adwords_offline_conversions/transform.js Co-authored-by: Sai Kumar Battinoju <88789928+saikumarrs@users.noreply.github.com> * Update src/v0/destinations/google_adwords_offline_conversions/transform.js Co-authored-by: Sai Kumar Battinoju <88789928+saikumarrs@users.noreply.github.com> * fix: review comments address * fix: review comments addressed * fix: review comments addressed * fix: utils added * fix: util function made more pluggable * chore: add event validation for movable ink destination (#3190) chore: add validation for movable ink destination --------- Co-authored-by: Sai Kumar Battinoju <88789928+saikumarrs@users.noreply.github.com> Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Co-authored-by: Krishna Chaitanya --- src/constants/destinationCanonicalNames.js | 12 + .../config.js | 8 +- .../transform.js | 15 +- .../utils.js | 38 +- .../utils.test.js | 139 +++++- .../config.js | 6 + .../transform.js | 5 +- src/v0/util/googleUtils/index.js | 97 +++- src/v0/util/googleUtils/index.test.js | 209 ++++++++- .../dataDelivery/business.ts | 20 +- .../dataDelivery/oauth.ts | 8 +- .../network.ts | 34 +- .../processor/data.ts | 430 +++++++++++++++++- .../router/data.ts | 28 +- 14 files changed, 946 insertions(+), 103 deletions(-) diff --git a/src/constants/destinationCanonicalNames.js b/src/constants/destinationCanonicalNames.js index 17848e6b94..b84aff1089 100644 --- a/src/constants/destinationCanonicalNames.js +++ b/src/constants/destinationCanonicalNames.js @@ -152,6 +152,18 @@ const DestCanonicalNames = { 'the trade desk', ], INTERCOM: ['INTERCOM', 'intercom', 'Intercom'], + GOOGLE_ADWORDS_REMARKETING_LISTS: [ + 'GOOGLE_ADWORDS_REMARKETING_LISTS', + 'google_adwords_remarketing_lists', + 'Google Adwords Remarketing Lists', + 'google adwords remarketing lists', + ], + GOOGLE_ADWORDS_OFFLINE_CONVERSIONS: [ + 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', + 'google_adwords_offline_conversions', + 'Google Adwords Offline Conversions', + 'google adwords offline conversions', + ], koala: ['Koala', 'koala', 'KOALA'], }; diff --git a/src/v0/destinations/google_adwords_offline_conversions/config.js b/src/v0/destinations/google_adwords_offline_conversions/config.js index a02732894f..f065be946c 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/config.js +++ b/src/v0/destinations/google_adwords_offline_conversions/config.js @@ -1,6 +1,6 @@ const { getMappingConfig } = require('../../util'); -const API_VERSION = 'v14'; +const API_VERSION = 'v16'; const BASE_ENDPOINT = `https://googleads.googleapis.com/${API_VERSION}/customers/:customerId`; @@ -42,6 +42,11 @@ const CONVERSION_CUSTOM_VARIABLE_CACHE_TTL = process.env.CONVERSION_CUSTOM_VARIA const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); +const consentConfigMap = { + personalizationConsent: 'adPersonalization', + userDataConsent: 'adUserData', +}; + module.exports = { trackClickConversionsMapping: MAPPING_CONFIG[CONFIG_CATEGORIES.TRACK_CLICK_CONVERSIONS_CONFIG.name], @@ -58,4 +63,5 @@ module.exports = { MAPPING_CONFIG[CONFIG_CATEGORIES.TRACK_STORE_CONVERSION_CONFIG_ADD_CONVERSION.name], trackAddStoreAddressConversionsMapping: MAPPING_CONFIG[CONFIG_CATEGORIES.TRACK_STORE_ADDRESS_IDENTIFIER.name], + consentConfigMap, }; diff --git a/src/v0/destinations/google_adwords_offline_conversions/transform.js b/src/v0/destinations/google_adwords_offline_conversions/transform.js index 68d4d01fa7..c3be0f7cab 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/transform.js +++ b/src/v0/destinations/google_adwords_offline_conversions/transform.js @@ -3,7 +3,6 @@ const { InstrumentationError, ConfigurationError } = require('@rudderstack/integ const { EventType } = require('../../../constants'); const { getHashFromArrayWithDuplicate, - constructPayload, removeHyphens, getHashFromArray, handleRtTfSingleEventError, @@ -11,16 +10,14 @@ const { getSuccessRespEvents, combineBatchRequestsWithSameJobIds, } = require('../../util'); -const { - CALL_CONVERSION, - trackCallConversionsMapping, - STORE_CONVERSION_CONFIG, -} = require('./config'); +const { CALL_CONVERSION, STORE_CONVERSION_CONFIG } = require('./config'); const { validateDestinationConfig, getStoreConversionPayload, requestBuilder, getClickConversionPayloadAndEndpoint, + getConsentsDataFromIntegrationObj, + getCallConversionPayload, } = require('./utils'); const helper = require('./helper'); @@ -41,12 +38,15 @@ const getConversions = (message, metadata, { Config }, event, conversionType) => const { properties, timestamp, originalTimestamp } = message; const filteredCustomerId = removeHyphens(customerId); + const eventLevelConsentsData = getConsentsDataFromIntegrationObj(message); + if (conversionType === 'click') { // click conversion const convertedPayload = getClickConversionPayloadAndEndpoint( message, Config, filteredCustomerId, + eventLevelConsentsData, ); payload = convertedPayload.payload; endpoint = convertedPayload.endpoint; @@ -55,7 +55,7 @@ const getConversions = (message, metadata, { Config }, event, conversionType) => endpoint = STORE_CONVERSION_CONFIG.replace(':customerId', filteredCustomerId); } else { // call conversions - payload = constructPayload(message, trackCallConversionsMapping); + payload = getCallConversionPayload(message, Config, eventLevelConsentsData); endpoint = CALL_CONVERSION.replace(':customerId', filteredCustomerId); } @@ -119,7 +119,6 @@ const trackResponseBuilder = (message, metadata, destination) => { const process = async (event) => { const { message, metadata, destination } = event; - if (!message.type) { throw new InstrumentationError('Message type is not present. Aborting message.'); } diff --git a/src/v0/destinations/google_adwords_offline_conversions/utils.js b/src/v0/destinations/google_adwords_offline_conversions/utils.js index 67c0ef31c8..70b42e2157 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/utils.js +++ b/src/v0/destinations/google_adwords_offline_conversions/utils.js @@ -18,6 +18,7 @@ const { isDefinedAndNotNull, getAuthErrCategoryFromStCode, getAccessToken, + getIntegrationsObj, } = require('../../util'); const { SEARCH_STREAM, @@ -27,10 +28,13 @@ const { trackAddStoreAddressConversionsMapping, trackClickConversionsMapping, CLICK_CONVERSION, + trackCallConversionsMapping, + consentConfigMap, } = require('./config'); const { processAxiosResponse } = require('../../../adapters/utils/networkUtils'); const Cache = require('../../util/cache'); const helper = require('./helper'); +const { finaliseConsent } = require('../../util/googleUtils'); const conversionActionIdCache = new Cache(CONVERSION_ACTION_ID_CACHE_TTL); @@ -221,6 +225,17 @@ function getExisitingUserIdentifier(userIdentifierInfo, defaultUserIdentifier) { return result; } +const getCallConversionPayload = (message, Config, eventLevelConsentsData) => { + const payload = constructPayload(message, trackCallConversionsMapping); + // here conversions[0] should be present because there are some mandatory properties mapped in the mapping json. + payload.conversions[0].consent = finaliseConsent( + consentConfigMap, + eventLevelConsentsData, + Config, + ); + return payload; +}; + /** * This Function create the add conversion payload * and returns the payload @@ -277,6 +292,10 @@ const getAddConversionPayload = (message, Config) => { set(payload, 'operations.create.userIdentifiers[0]', {}); } } + // add consent support for store conversions. Note: No event level consent supported. + const consentObject = finaliseConsent(consentConfigMap, {}, Config); + // create property should be present because there are some mandatory properties mapped in the mapping json. + set(payload, 'operations.create.consent', consentObject); return payload; }; @@ -292,7 +311,12 @@ const getStoreConversionPayload = (message, Config, event) => { return payload; }; -const getClickConversionPayloadAndEndpoint = (message, Config, filteredCustomerId) => { +const getClickConversionPayloadAndEndpoint = ( + message, + Config, + filteredCustomerId, + eventLevelConsent, +) => { const email = getFieldValueFromMessage(message, 'emailOnly'); const phone = getFieldValueFromMessage(message, 'phone'); const { hashUserIdentifier, defaultUserIdentifier, UserIdentifierSource, conversionEnvironment } = @@ -364,9 +388,19 @@ const getClickConversionPayloadAndEndpoint = (message, Config, filteredCustomerI if (!properties.conversionEnvironment && conversionEnvironment !== 'none') { set(payload, 'conversions[0].conversionEnvironment', conversionEnvironment); } + + // add consent support for click conversions + const consentObject = finaliseConsent(consentConfigMap, eventLevelConsent, Config); + // here conversions[0] is expected to be present there are some mandatory properties mapped in the mapping json. + set(payload, 'conversions[0].consent', consentObject); return { payload, endpoint }; }; +const getConsentsDataFromIntegrationObj = (message) => { + const integrationObj = getIntegrationsObj(message, 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS') || {}; + return integrationObj?.consents || {}; +}; + module.exports = { validateDestinationConfig, generateItemListFromProducts, @@ -377,4 +411,6 @@ module.exports = { buildAndGetAddress, getClickConversionPayloadAndEndpoint, getExisitingUserIdentifier, + getConsentsDataFromIntegrationObj, + getCallConversionPayload, }; diff --git a/src/v0/destinations/google_adwords_offline_conversions/utils.test.js b/src/v0/destinations/google_adwords_offline_conversions/utils.test.js index 8deaa3ab0a..2d1863413c 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/utils.test.js +++ b/src/v0/destinations/google_adwords_offline_conversions/utils.test.js @@ -2,6 +2,8 @@ const { getClickConversionPayloadAndEndpoint, buildAndGetAddress, getExisitingUserIdentifier, + getConsentsDataFromIntegrationObj, + getCallConversionPayload, } = require('./utils'); const getTestMessage = () => { @@ -161,12 +163,16 @@ describe('getExisitingUserIdentifier util tests', () => { describe('getClickConversionPayloadAndEndpoint util tests', () => { it('getClickConversionPayloadAndEndpoint flow check when default field identifier is present', () => { let expectedOutput = { - endpoint: 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + endpoint: 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', payload: { conversions: [ { conversionDateTime: '2022-01-01 12:32:45-08:00', conversionEnvironment: 'WEB', + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, userIdentifiers: [ { hashedEmail: 'fa922cb41ff930664d4c9ced3c472ce7ecf29a0f8248b7018456e990177fff75', @@ -187,11 +193,15 @@ describe('getClickConversionPayloadAndEndpoint util tests', () => { delete fittingPayload.traits.email; delete fittingPayload.properties.email; let expectedOutput = { - endpoint: 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + endpoint: 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', payload: { conversions: [ { conversionDateTime: '2022-01-01 12:32:45-08:00', + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, conversionEnvironment: 'WEB', userIdentifiers: [ { @@ -215,7 +225,7 @@ describe('getClickConversionPayloadAndEndpoint util tests', () => { delete fittingPayload.traits.phone; delete fittingPayload.properties.email; let expectedOutput = { - endpoint: 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + endpoint: 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', payload: { conversions: [ { @@ -237,7 +247,7 @@ describe('getClickConversionPayloadAndEndpoint util tests', () => { ).toThrow('Either of email or phone is required for user identifier'); }); - it('getClickConversionPayloadAndEndpoint flow check when default field identifier is present and product list present', () => { + it('finaliseConsent', () => { let fittingPayload = { ...getTestMessage() }; fittingPayload.properties.products = [ { @@ -251,13 +261,17 @@ describe('getClickConversionPayloadAndEndpoint util tests', () => { }, ]; let expectedOutput = { - endpoint: 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + endpoint: 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', payload: { conversions: [ { cartData: { items: [{ productId: 1234, quantity: 2, unitPrice: 10 }] }, conversionDateTime: '2022-01-01 12:32:45-08:00', conversionEnvironment: 'WEB', + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, userIdentifiers: [ { hashedEmail: 'fa922cb41ff930664d4c9ced3c472ce7ecf29a0f8248b7018456e990177fff75', @@ -273,3 +287,118 @@ describe('getClickConversionPayloadAndEndpoint util tests', () => { ); }); }); + +describe('getConsentsDataFromIntegrationObj', () => { + it('should return an empty object when conversionType is "store"', () => { + const message = {}; + const result = getConsentsDataFromIntegrationObj(message); + expect(result).toEqual({}); + }); + it('should return the consent object when conversion type is call', () => { + const message = { + integrations: { + GOOGLE_ADWORDS_OFFLINE_CONVERSIONS: { + consents: { + adUserData: 'GRANTED', + adPersonalization: 'DENIED', + }, + }, + }, + }; + const conversionType = 'call'; + const result = getConsentsDataFromIntegrationObj(message, conversionType); + expect(result).toEqual({ + adPersonalization: 'DENIED', + adUserData: 'GRANTED', + }); + }); +}); + +describe('getCallConversionPayload', () => { + it('should call conversion payload with consent object', () => { + const message = { + properties: { + callerId: '1234', + callStartDateTime: '2022-01-01 12:32:45-08:00', + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + }; + const result = getCallConversionPayload( + message, + { + userDataConsent: 'GRANTED', + personalizationConsent: 'DENIED', + }, + { + adUserData: 'GRANTED', + adPersonalization: 'GRANTED', + }, + ); + expect(result).toEqual({ + conversions: [ + { + callStartDateTime: '2022-01-01 12:32:45-08:00', + callerId: '1234', + consent: { + adPersonalization: 'GRANTED', + adUserData: 'GRANTED', + }, + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + ], + }); + }); + it('should call conversion payload with consent object', () => { + const message = { + properties: { + callerId: '1234', + callStartDateTime: '2022-01-01 12:32:45-08:00', + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + }; + const result = getCallConversionPayload( + message, + { + userDataConsent: 'GRANTED', + personalizationConsent: 'DENIED', + }, + {}, + ); + expect(result).toEqual({ + conversions: [ + { + callStartDateTime: '2022-01-01 12:32:45-08:00', + callerId: '1234', + consent: { + adPersonalization: 'DENIED', + adUserData: 'GRANTED', + }, + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + ], + }); + }); + it('should call conversion payload with consent object even if no consent input from UI as well as event level', () => { + const message = { + properties: { + callerId: '1234', + callStartDateTime: '2022-01-01 12:32:45-08:00', + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + }; + const result = getCallConversionPayload(message, {}, {}); + expect(result).toEqual({ + conversions: [ + { + callStartDateTime: '2022-01-01 12:32:45-08:00', + callerId: '1234', + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + ], + }); + }); +}); diff --git a/src/v0/destinations/google_adwords_remarketing_lists/config.js b/src/v0/destinations/google_adwords_remarketing_lists/config.js index 5bf0d8a299..0f08b3866d 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/config.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/config.js @@ -16,6 +16,11 @@ const TYPEOFLIST = Object.freeze({ mobileDeviceID: 'mobileId', }); +const consentConfigMap = { + personalizationConsent: 'adPersonalization', + userDataConsent: 'adUserData', +}; + module.exports = { BASE_ENDPOINT, TYPEOFLIST, @@ -23,4 +28,5 @@ module.exports = { hashAttributes, offlineDataJobsMapping: MAPPING_CONFIG[CONFIG_CATEGORIES.AUDIENCE_LIST.name], addressInfoMapping: MAPPING_CONFIG[CONFIG_CATEGORIES.ADDRESSINFO.name], + consentConfigMap, }; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/transform.js b/src/v0/destinations/google_adwords_remarketing_lists/transform.js index 9ab415346a..b0dfaa0c35 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/transform.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/transform.js @@ -15,7 +15,7 @@ const { getAccessToken, } = require('../../util'); -const { populateConsentForGoogleDestinations } = require('../../util/googleUtils'); +const { populateConsentFromConfig } = require('../../util/googleUtils'); const { offlineDataJobsMapping, @@ -24,6 +24,7 @@ const { attributeMapping, hashAttributes, TYPEOFLIST, + consentConfigMap, } = require('./config'); const { JSON_MIME_TYPE } = require('../../util/constant'); const { MappedToDestinationKey } = require('../../../constants'); @@ -218,7 +219,7 @@ const processEvent = async (metadata, message, destination) => { } Object.values(createdPayload).forEach((data) => { - const consentObj = populateConsentForGoogleDestinations(destination.Config); + const consentObj = populateConsentFromConfig(destination.Config, consentConfigMap); response.push(responseBuilder(metadata, data, destination, message, consentObj)); }); return response; diff --git a/src/v0/util/googleUtils/index.js b/src/v0/util/googleUtils/index.js index de73b0fb05..c153731e73 100644 --- a/src/v0/util/googleUtils/index.js +++ b/src/v0/util/googleUtils/index.js @@ -1,36 +1,91 @@ const GOOGLE_ALLOWED_CONSENT_STATUS = ['UNSPECIFIED', 'UNKNOWN', 'GRANTED', 'DENIED']; +const UNSPECIFIED_CONSENT = 'UNSPECIFIED'; +const UNKNOWN_CONSENT = 'UNKNOWN'; + /** - * Populates the consent object based on the provided properties. + * Populates the consent object based on the provided configuration and consent mapping. * - * @param {object} properties - message.properties containing properties related to consent. - * @returns {object} - An object containing consent information. - * ref : https://developers.google.com/google-ads/api/rest/reference/rest/v15/Consent + * @param {Object} config - The configuration object containing consent values. + * @param {Object} consentConfigMap - The mapping of consent keys to consent types. + * @returns {Object} - The consent object populated with consent values based on the configuration. + * * ref : https://developers.google.com/google-ads/api/rest/reference/rest/v16/Consent */ - -const populateConsentForGoogleDestinations = (config) => { +const populateConsentFromConfig = (config, consentConfigMap) => { const consent = {}; - if (config?.userDataConsent) { - if (GOOGLE_ALLOWED_CONSENT_STATUS.includes(config.userDataConsent)) { - consent.adUserData = config.userDataConsent; + Object.keys(consentConfigMap).forEach((key) => { + const consentType = consentConfigMap[key]; + if (config?.[key]) { + if (GOOGLE_ALLOWED_CONSENT_STATUS.includes(config[key])) { + consent[consentType] = config[key]; + } else { + consent[consentType] = UNKNOWN_CONSENT; + } } else { - consent.adUserData = 'UNKNOWN'; + consent[consentType] = UNSPECIFIED_CONSENT; } - } else { - consent.adUserData = 'UNSPECIFIED'; + }); + + return consent; +}; + +/** + * Generates the final consent object based on the provided consent configuration map, event-level consent, and destination configuration. + * + * @param {Object} consentConfigMap - The map of consent configuration keys and their corresponding consent types. + * @param {Object} [eventLevelConsent={}] - The event-level consent object. + * @param {Object} [destConfig={}] - The destination configuration object. + * @returns {Object} The final consent object. + * ref : + * 1) For click conversion : + * a) https://developers.google.com/google-ads/api/rest/reference/rest/v16/customers/uploadClickConversions#ClickConversion + * b) https://developers.google.com/google-ads/api/reference/rpc/v16/ClickConversion#consent + * 2) For Call conversion : + * a) https://developers.google.com/google-ads/api/rest/reference/rest/v16/customers/uploadCallConversions#CallConversion + * b) https://developers.google.com/google-ads/api/reference/rpc/v16/CallConversion#consent + * 3) For Store sales conversion : + * a) https://developers.google.com/google-ads/api/reference/rpc/v16/UserData + * b) https://developers.google.com/google-ads/api/reference/rpc/v16/UserData#consent + */ +const finaliseConsent = (consentConfigMap, eventLevelConsent = {}, destConfig = {}) => { + // Initialize defaultConsentBlock with unspecified consent for all keys defined in consentConfigMap + const defaultConsentBlock = Object.keys(consentConfigMap).reduce((acc, key) => { + const consentType = consentConfigMap[key]; + acc[consentType] = UNSPECIFIED_CONSENT; + return acc; + }, {}); + + // If destConfig is provided, update defaultConsentBlock based on it using populateConsentFromConfig + if (Object.keys(destConfig).length > 0) { + const populatedConsent = populateConsentFromConfig(destConfig, consentConfigMap); + Object.assign(defaultConsentBlock, populatedConsent); } - if (config?.personalizationConsent) { - if (GOOGLE_ALLOWED_CONSENT_STATUS.includes(config.personalizationConsent)) { - consent.adPersonalization = config.personalizationConsent; + const consentObj = {}; + + // Iterate through each key in consentConfigMap to determine the final consent + Object.keys(consentConfigMap).forEach((configKey) => { + const consentKey = consentConfigMap[configKey]; // e.g., 'adUserData' + + // Prioritize event-level consent if available + if (eventLevelConsent && eventLevelConsent.hasOwnProperty(consentKey)) { + consentObj[consentKey] = GOOGLE_ALLOWED_CONSENT_STATUS.includes(eventLevelConsent[consentKey]) + ? eventLevelConsent[consentKey] + : UNKNOWN_CONSENT; } else { - consent.adPersonalization = 'UNKNOWN'; + // Fallback to default consent block + consentObj[consentKey] = defaultConsentBlock[consentKey]; } - } else { - consent.adPersonalization = 'UNSPECIFIED'; - } - return consent; + }); + + return consentObj; }; -module.exports = { populateConsentForGoogleDestinations }; +module.exports = { + populateConsentFromConfig, + UNSPECIFIED_CONSENT, + UNKNOWN_CONSENT, + GOOGLE_ALLOWED_CONSENT_STATUS, + finaliseConsent, +}; diff --git a/src/v0/util/googleUtils/index.test.js b/src/v0/util/googleUtils/index.test.js index 9d1aa5e51a..28e0fa9ac8 100644 --- a/src/v0/util/googleUtils/index.test.js +++ b/src/v0/util/googleUtils/index.test.js @@ -1,8 +1,12 @@ -const { populateConsentForGoogleDestinations } = require('./index'); +const { populateConsentFromConfig, finaliseConsent } = require('./index'); -describe('unit test for populateConsentForGoogleDestinations', () => { +describe('unit test for populateConsentFromConfig', () => { + const consentConfigMap = { + personalizationConsent: 'adPersonalization', + userDataConsent: 'adUserData', + }; it('should return an UNSPECIFIED object when no properties are provided', () => { - const result = populateConsentForGoogleDestinations({}); + const result = populateConsentFromConfig({}, consentConfigMap); expect(result).toEqual({ adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED', @@ -11,18 +15,18 @@ describe('unit test for populateConsentForGoogleDestinations', () => { it('should set adUserData property of consent object when userDataConsent property is provided and its value is one of the allowed consent statuses', () => { const properties = { userDataConsent: 'GRANTED' }; - const result = populateConsentForGoogleDestinations(properties); + const result = populateConsentFromConfig(properties, consentConfigMap); expect(result).toEqual({ adUserData: 'GRANTED', adPersonalization: 'UNSPECIFIED' }); }); it('should set adPersonalization property of consent object when personalizationConsent property is provided and its value is one of the allowed consent statuses', () => { const properties = { personalizationConsent: 'DENIED' }; - const result = populateConsentForGoogleDestinations(properties); + const result = populateConsentFromConfig(properties, consentConfigMap); expect(result).toEqual({ adPersonalization: 'DENIED', adUserData: 'UNSPECIFIED' }); }); it('should return an UNSPECIFIED object when properties parameter is not provided', () => { - const result = populateConsentForGoogleDestinations(); + const result = populateConsentFromConfig(undefined, consentConfigMap); expect(result).toEqual({ adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED', @@ -30,7 +34,7 @@ describe('unit test for populateConsentForGoogleDestinations', () => { }); it('should return an UNSPECIFIED object when properties parameter is null', () => { - const result = populateConsentForGoogleDestinations(null); + const result = populateConsentFromConfig(null, consentConfigMap); expect(result).toEqual({ adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED', @@ -38,7 +42,7 @@ describe('unit test for populateConsentForGoogleDestinations', () => { }); it('should return an UNSPECIFIED object when properties parameter is an UNSPECIFIED object', () => { - const result = populateConsentForGoogleDestinations({}); + const result = populateConsentFromConfig({}, consentConfigMap); expect(result).toEqual({ adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED', @@ -46,13 +50,196 @@ describe('unit test for populateConsentForGoogleDestinations', () => { }); it('should return UNKNOWN when properties parameter contains adUserData and adPersonalization with non-allowed values', () => { - const result = populateConsentForGoogleDestinations({ - userDataConsent: 'RANDOM', - personalizationConsent: 'RANDOM', + const result = populateConsentFromConfig( + { + userDataConsent: 'RANDOM', + personalizationConsent: 'RANDOM', + }, + consentConfigMap, + ); + expect(result).toEqual({ + adPersonalization: 'UNKNOWN', + adUserData: 'UNKNOWN', + }); + }); +}); + +describe('finaliseConsent', () => { + const consentConfigMap = { + personalizationConsent: 'adPersonalization', + userDataConsent: 'adUserData', + }; + // Returns an object containing consent information. + it('should return an object containing consent information when eventLevelConsent, destConfig, and destinationAllowedConsentKeys are provided', () => { + const eventLevelConsent = { + adUserData: 'GRANTED', + adPersonalization: 'DENIED', + }; + const destConfig = { + userDataConsent: 'UNKNOWN', + personalizationConsent: 'GRANTED', + }; + + const result = finaliseConsent(consentConfigMap, eventLevelConsent, destConfig); + + expect(result).toEqual({ + adUserData: 'GRANTED', + adPersonalization: 'DENIED', + }); + }); + + it('should return an object containing consent information from destConfig when evenLevelConsent is empty object', () => { + const eventLevelConsent = {}; // for store conversion we will use this + const destConfig = { + userDataConsent: 'UNKNOWN', + personalizationConsent: 'GRANTED', + }; + + const result = finaliseConsent(consentConfigMap, eventLevelConsent, destConfig); + + expect(result).toEqual({ + adUserData: 'UNKNOWN', + adPersonalization: 'GRANTED', + }); + }); + + // If destConfig is not provided, it does not return UNSPECIFIED_CONSENT. + it('should not return UNSPECIFIED_CONSENT when destConfig is not provided but event level consent is provided', () => { + const eventLevelConsent = { + adUserData: 'GRANTED', + adPersonalization: 'DENIED', + }; + const result = finaliseConsent(consentConfigMap, eventLevelConsent, undefined); + + // Assert + expect(result).toEqual({ + adUserData: 'GRANTED', + adPersonalization: 'DENIED', + }); + }); + + it('should return UNSPECIFIED_CONSENT when both destConfig and event level consent is not provided', () => { + const result = finaliseConsent(consentConfigMap, undefined, undefined); + + // Assert + expect(result).toEqual({ + adUserData: 'UNSPECIFIED', + adPersonalization: 'UNSPECIFIED', + }); + }); + + it('should return UNKWOWN_CONSENT when destConfig is provided with wrong consent value', () => { + const destConfig = { + userDataConsent: 'UNKNOWN', + personalizationConsent: 'WRONG CONSENT', + }; + + const result = finaliseConsent(consentConfigMap, undefined, destConfig); + + expect(result).toEqual({ + adUserData: 'UNKNOWN', + adPersonalization: 'UNKNOWN', }); + }); + + it('should return UNKWOWN_CONSENT when destConfig is provided with wrong consent value', () => { + const destConfig = { + userDataConsent: 'UNKNOWN', + personalizationConsent: 'WRONG CONSENT', + }; + + const result = finaliseConsent(consentConfigMap, undefined, destConfig); + expect(result).toEqual({ adPersonalization: 'UNKNOWN', adUserData: 'UNKNOWN', }); }); + + it('should return consent block with appropriate fields and values from destConfig', () => { + const consentConfigMap = { + personalizationConsent: 'newKey1', + userDataConsent: 'newKey2', + }; + const destConfig = { + userDataConsent: 'GRANTED', + personalizationConsent: 'GRANTED', + }; + + const result = finaliseConsent(consentConfigMap, undefined, destConfig); + + expect(result).toEqual({ + newKey1: 'GRANTED', + newKey2: 'GRANTED', + }); + }); + + it('should return consent block with appropriate fields from consentConfigMap and values from eventLevel consent', () => { + const consentConfigMap = { + personalizationConsent: 'newKey1', + userDataConsent: 'newKey2', + }; + const destConfig = { + userDataConsent: 'GRANTED', + personalizationConsent: 'GRANTED', + }; + + const eventLevelConsent = { + newKey1: 'UNKNOWN', + newKey2: 'UNSPECIFIED', + }; + + const result = finaliseConsent(consentConfigMap, eventLevelConsent, destConfig); + + expect(result).toEqual({ + newKey1: 'UNKNOWN', + newKey2: 'UNSPECIFIED', + }); + }); + + it('consentConfig and eventLevelConsent should have parity, also the values should be within allowed values otherwise UNKNOWN is returned ', () => { + const consentConfigMap = { + personalizationConsent: 'newKey1', + userDataConsent: 'newKey2', + }; + const destConfig = { + userDataConsent: 'GRANTED', + personalizationConsent: 'GRANTED', + }; + + const eventLevelConsent = { + adUserData: 'UNKNOWN', + adPersonalization: 'UNSPECIFIED', + }; + + const result = finaliseConsent(consentConfigMap, eventLevelConsent, destConfig); + + expect(result).toEqual({ + newKey1: 'GRANTED', + newKey2: 'GRANTED', + }); + }); + + it('consentConfig and eventLevelConsent should have parity, otherwise it will take values from destConfig ', () => { + const consentConfigMap = { + personalizationConsent: 'newKey1', + userDataConsent: 'newKey2', + }; + const destConfig = { + userDataConsent: 'GRANTED', + personalizationConsent: 'GRANTED', + }; + + const eventLevelConsent = { + newKey1: 'DENIED', + newKey2: 'RANDOM', + }; + + const result = finaliseConsent(consentConfigMap, eventLevelConsent, destConfig); + + expect(result).toEqual({ + newKey1: 'DENIED', + newKey2: 'UNKNOWN', + }); + }); }); diff --git a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts index fbeaf7f250..87aafea0af 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts @@ -235,7 +235,7 @@ export const testScenariosForV0API = [ params: params.param1, JSON: invalidArgumentRequestPayload, endpoint: - 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/11122233331/offlineUserDataJobs', }), method: 'POST', }, @@ -253,7 +253,7 @@ export const testScenariosForV0API = [ code: 400, details: [ { - '@type': 'type.googleapis.com/google.ads.googleads.v14.errors.GoogleAdsFailure', + '@type': 'type.googleapis.com/google.ads.googleads.v16.errors.GoogleAdsFailure', errors: [ { errorCode: { @@ -309,7 +309,7 @@ export const testScenariosForV0API = [ headers: headers.header1, params: params.param1, JSON: validRequestPayload1, - endpoint: 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + endpoint: 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', }), method: 'POST', }, @@ -350,7 +350,7 @@ export const testScenariosForV0API = [ params: params.param2, JSON: validRequestPayload2, endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/1234567891:uploadClickConversions', }), method: 'POST', }, @@ -400,7 +400,7 @@ export const testScenariosForV0API = [ params: params.param3, JSON: validRequestPayload2, endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/1234567891:uploadClickConversions', }), method: 'POST', }, @@ -453,7 +453,7 @@ export const testScenariosForV1API = [ params: params.param1, JSON: invalidArgumentRequestPayload, endpoint: - 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/11122233331/offlineUserDataJobs', }, metadataArray, ), @@ -500,7 +500,7 @@ export const testScenariosForV1API = [ params: params.param1, JSON: validRequestPayload1, endpoint: - 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', }, metadataArray, ), @@ -545,7 +545,7 @@ export const testScenariosForV1API = [ params: params.param2, JSON: validRequestPayload2, endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/1234567891:uploadClickConversions', }, metadataArray, ), @@ -591,7 +591,7 @@ export const testScenariosForV1API = [ params: params.param3, JSON: validRequestPayload2, endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/1234567891:uploadClickConversions', }, metadataArray, ), @@ -637,7 +637,7 @@ export const testScenariosForV1API = [ params: params.param4, JSON: notAllowedToAccessFeatureRequestPayload, endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567893:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/1234567893:uploadClickConversions', }, metadataArray, ), diff --git a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts index 15a150d0e5..e14e4109f0 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts @@ -95,7 +95,7 @@ export const v0oauthScenarios = [ request: { body: generateProxyV0Payload({ ...commonRequestParameters, - endpoint: 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs', + endpoint: 'https://googleads.googleapis.com/v16/customers/customerid/offlineUserDataJobs', }), method: 'POST', }, @@ -138,7 +138,7 @@ export const v0oauthScenarios = [ request: { body: generateProxyV0Payload({ ...commonRequestParameters, - endpoint: 'https://googleads.googleapis.com/v14/customers/1234/offlineUserDataJobs', + endpoint: 'https://googleads.googleapis.com/v16/customers/1234/offlineUserDataJobs', }), method: 'POST', }, @@ -184,7 +184,7 @@ export const v1oauthScenarios = [ { ...commonRequestParameters, endpoint: - 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/customerid/offlineUserDataJobs', }, metadataArray, ), @@ -230,7 +230,7 @@ export const v1oauthScenarios = [ body: generateProxyV1Payload( { ...commonRequestParameters, - endpoint: 'https://googleads.googleapis.com/v14/customers/1234/offlineUserDataJobs', + endpoint: 'https://googleads.googleapis.com/v16/customers/1234/offlineUserDataJobs', }, metadataArray, ), diff --git a/test/integrations/destinations/google_adwords_offline_conversions/network.ts b/test/integrations/destinations/google_adwords_offline_conversions/network.ts index 7dc7f97933..4dad9e0d1b 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/network.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/network.ts @@ -1,7 +1,7 @@ export const networkCallsData = [ { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs:create', + url: 'https://googleads.googleapis.com/v16/customers/11122233331/offlineUserDataJobs:create', data: { job: { storeSalesMetadata: { @@ -30,7 +30,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1112223333/googleAds:searchStream', + url: 'https://googleads.googleapis.com/v16/customers/1112223333/googleAds:searchStream', data: { query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Sign-up - click'`, }, @@ -63,7 +63,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs/OFFLINE_USER_DATA_JOB_ID_FOR_ADD_FAILURE:addOperations', + url: 'https://googleads.googleapis.com/v16/customers/11122233331/offlineUserDataJobs/OFFLINE_USER_DATA_JOB_ID_FOR_ADD_FAILURE:addOperations', data: { enable_partial_failure: false, enable_warnings: false, @@ -108,7 +108,7 @@ export const networkCallsData = [ status: 'INVALID_ARGUMENT', details: [ { - '@type': 'type.googleapis.com/google.ads.googleads.v14.errors.GoogleAdsFailure', + '@type': 'type.googleapis.com/google.ads.googleads.v16.errors.GoogleAdsFailure', errors: [ { errorCode: { @@ -144,7 +144,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs:create', + url: 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs:create', data: { job: { storeSalesMetadata: { @@ -173,7 +173,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs/OFFLINE_USER_DATA_JOB_ID:addOperations', + url: 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs/OFFLINE_USER_DATA_JOB_ID:addOperations', data: { enable_partial_failure: false, enable_warnings: false, @@ -216,7 +216,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs/OFFLINE_USER_DATA_JOB_ID:run', + url: 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs/OFFLINE_USER_DATA_JOB_ID:run', data: { validate_only: false }, params: { destination: 'google_adwords_offline_conversion' }, headers: { @@ -238,7 +238,7 @@ export const networkCallsData = [ description: 'Mock response from destination depicting a request with invalid authentication credentials', httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs:create', + url: 'https://googleads.googleapis.com/v16/customers/customerid/offlineUserDataJobs:create', data: { job: { storeSalesMetadata: { @@ -274,7 +274,7 @@ export const networkCallsData = [ description: 'Mock response from destination depicting a request with invalid authentication scopes', httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1234/offlineUserDataJobs:create', + url: 'https://googleads.googleapis.com/v16/customers/1234/offlineUserDataJobs:create', data: { job: { storeSalesMetadata: { @@ -307,7 +307,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1234567890/googleAds:searchStream', + url: 'https://googleads.googleapis.com/v16/customers/1234567890/googleAds:searchStream', data: { query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Sign-up - click'`, }, @@ -335,7 +335,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1234567891/googleAds:searchStream', + url: 'https://googleads.googleapis.com/v16/customers/1234567891/googleAds:searchStream', data: { query: "SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Sign-up - click'", @@ -368,7 +368,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1234567891/googleAds:searchStream', + url: 'https://googleads.googleapis.com/v16/customers/1234567891/googleAds:searchStream', data: { query: 'SELECT conversion_custom_variable.name FROM conversion_custom_variable' }, headers: { Authorization: 'Bearer abcd1234', @@ -402,7 +402,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + url: 'https://googleads.googleapis.com/v16/customers/1234567891:uploadClickConversions', data: { conversions: [ { @@ -469,7 +469,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + url: 'https://googleads.googleapis.com/v16/customers/1234567891:uploadClickConversions', data: { conversions: [ { @@ -530,7 +530,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1234567893/googleAds:searchStream', + url: 'https://googleads.googleapis.com/v16/customers/1234567893/googleAds:searchStream', data: { query: "SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Sign-up - click'", @@ -563,7 +563,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1234567893:uploadClickConversions', + url: 'https://googleads.googleapis.com/v16/customers/1234567893:uploadClickConversions', data: { conversions: [ { @@ -615,7 +615,7 @@ export const networkCallsData = [ 'Customer is not allowlisted for accessing this feature., at conversions[0].conversion_environment', details: [ { - '@type': 'type.googleapis.com/google.ads.googleads.v14.errors.GoogleAdsFailure', + '@type': 'type.googleapis.com/google.ads.googleads.v16.errors.GoogleAdsFailure', errors: [ { errorCode: { diff --git a/test/integrations/destinations/google_adwords_offline_conversions/processor/data.ts b/test/integrations/destinations/google_adwords_offline_conversions/processor/data.ts index f47deaef67..decb1e58c7 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/processor/data.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/processor/data.ts @@ -176,7 +176,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -263,6 +263,10 @@ export const data = [ conversionValue: 1, currencyCode: 'GBP', orderId: 'PL-123QR', + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, }, ], partialFailure: true, @@ -465,7 +469,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -520,6 +524,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, gbraid: 'gbraid', wbraid: 'wbraid', externalAttributionData: { @@ -754,7 +762,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -809,6 +817,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, gbraid: 'gbraid', wbraid: 'wbraid', externalAttributionData: { @@ -1043,7 +1055,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadCallConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadCallConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -1099,6 +1111,10 @@ export const data = [ conversions: [ { callerId: 'callerId', + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, callStartDateTime: '2022-08-28 15:01:30+05:30', conversionDateTime: '2022-01-01 12:32:45-08:00', conversionValue: 1, @@ -2015,7 +2031,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -2072,6 +2088,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, gbraid: 'gbraid', wbraid: 'wbraid', externalAttributionData: { @@ -2128,7 +2148,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadCallConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadCallConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -2185,6 +2205,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, callerId: 'callerId', callStartDateTime: '2022-08-28 15:01:30+05:30', conversionDateTime: '2022-01-01 12:32:45-08:00', @@ -2350,7 +2374,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -2379,6 +2403,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, gclid: 'gclid', conversionDateTime: '2022-01-01 12:32:45-08:00', userIdentifiers: [ @@ -2546,7 +2574,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadCallConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadCallConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -2575,6 +2603,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, callerId: 'callerId', callStartDateTime: '2022-08-28 15:01:30+05:30', conversionDateTime: '2022-01-01 12:32:45-08:00', @@ -2778,7 +2810,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadCallConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadCallConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -2832,6 +2864,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, callerId: 'callerId', callStartDateTime: '2022-08-28 15:01:30+05:30', conversionDateTime: '2022-09-20 08:50:04+05:30', @@ -2997,7 +3033,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -3025,6 +3061,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, gclid: 'gclid', conversionDateTime: '2022-01-01 12:32:45-08:00', userIdentifiers: [ @@ -3501,7 +3541,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -3532,6 +3572,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, cartData: { items: [ { @@ -3861,7 +3905,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -3892,6 +3936,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, cartData: { items: [ { @@ -4049,7 +4097,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -4077,6 +4125,10 @@ export const data = [ addConversionPayload: { operations: { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code', @@ -4382,7 +4434,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -4409,6 +4461,10 @@ export const data = [ addConversionPayload: { operations: { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code', @@ -4577,7 +4633,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -4604,6 +4660,10 @@ export const data = [ addConversionPayload: { operations: { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code', @@ -4775,7 +4835,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -4802,6 +4862,10 @@ export const data = [ addConversionPayload: { operations: { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code', @@ -4935,7 +4999,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -4962,6 +5026,10 @@ export const data = [ addConversionPayload: { operations: { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code', @@ -5091,7 +5159,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -5118,6 +5186,10 @@ export const data = [ addConversionPayload: { operations: { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code', @@ -5245,7 +5317,323 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', + }, + params: { + event: 'Sign-up - click', + customerId: '1112223333', + }, + body: { + JSON: { + event: '1112223333', + isStoreConversion: true, + createJobPayload: { + job: { + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + storeSalesMetadata: { + loyaltyFraction: '1', + transaction_upload_fraction: '1', + }, + }, + }, + addConversionPayload: { + operations: { + create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, + transaction_attribute: { + store_attribute: { + store_code: 'store code', + }, + transaction_amount_micros: '100000000', + currency_code: 'INR', + transaction_date_time: '2019-10-14 16:45:18+05:30', + }, + userIdentifiers: [{}], + }, + }, + enable_partial_failure: false, + enable_warnings: false, + validate_only: false, + }, + executeJobPayload: { + validate_only: false, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: timestampMock, + }, + { + name: 'google_adwords_offline_conversions', + description: 'Test 26 : store conversion consent mapped from UI config', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + traits: {}, + }, + event: 'Product Clicked', + type: 'track', + messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { + item_id: 'item id', + merchant_id: 'merchant id', + currency: 'INR', + revenue: '100', + store_code: 'store code', + gclid: 'gclid', + conversionDateTime: '2019-10-14T11:15:18.299Z', + product_id: '123445', + quantity: 123, + }, + integrations: { + All: true, + }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + }, + metadata: { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + destination: { + Config: { + isCustomerAllowed: false, + customerId: '111-222-3333', + subAccount: true, + loginCustomerId: 'login-customer-id', + userDataConsent: 'GRANTED', + personalizationConsent: 'DENIED', + eventsToOfflineConversionsTypeMapping: [ + { + from: 'Product Clicked', + to: 'store', + }, + ], + eventsToConversionsNamesMapping: [ + { + from: 'Product Clicked', + to: 'Sign-up - click', + }, + ], + hashUserIdentifier: true, + defaultUserIdentifier: 'phone', + validateOnly: false, + rudderAccountId: '2EOknn1JNH7WK1MfNkgr4t3u4fGYKkRK', + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', + }, + params: { + event: 'Sign-up - click', + customerId: '1112223333', + }, + body: { + JSON: { + event: '1112223333', + isStoreConversion: true, + createJobPayload: { + job: { + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + storeSalesMetadata: { + loyaltyFraction: '1', + transaction_upload_fraction: '1', + }, + }, + }, + addConversionPayload: { + operations: { + create: { + consent: { + adPersonalization: 'DENIED', + adUserData: 'GRANTED', + }, + transaction_attribute: { + store_attribute: { + store_code: 'store code', + }, + transaction_amount_micros: '100000000', + currency_code: 'INR', + transaction_date_time: '2019-10-14 16:45:18+05:30', + }, + userIdentifiers: [{}], + }, + }, + enable_partial_failure: false, + enable_warnings: false, + validate_only: false, + }, + executeJobPayload: { + validate_only: false, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: timestampMock, + }, + { + name: 'google_adwords_offline_conversions', + description: + 'Test 27 : store conversion consent mapped from UI config even though integration object is present', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + traits: {}, + }, + event: 'Product Clicked', + type: 'track', + messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { + item_id: 'item id', + merchant_id: 'merchant id', + currency: 'INR', + revenue: '100', + store_code: 'store code', + gclid: 'gclid', + conversionDateTime: '2019-10-14T11:15:18.299Z', + product_id: '123445', + quantity: 123, + }, + integrations: { + google_adwords_offline_conversion: { + consent: { + adUserdata: 'UNSPECIFIED', + adPersonalization: 'GRANTED', + }, + }, + }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + }, + metadata: { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + destination: { + Config: { + isCustomerAllowed: false, + customerId: '111-222-3333', + subAccount: true, + loginCustomerId: 'login-customer-id', + userDataConsent: 'GRANTED', + personalizationConsent: 'DENIED', + eventsToOfflineConversionsTypeMapping: [ + { + from: 'Product Clicked', + to: 'store', + }, + ], + eventsToConversionsNamesMapping: [ + { + from: 'Product Clicked', + to: 'Sign-up - click', + }, + ], + hashUserIdentifier: true, + defaultUserIdentifier: 'phone', + validateOnly: false, + rudderAccountId: '2EOknn1JNH7WK1MfNkgr4t3u4fGYKkRK', + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -5272,6 +5660,10 @@ export const data = [ addConversionPayload: { operations: { create: { + consent: { + adPersonalization: 'DENIED', + adUserData: 'GRANTED', + }, transaction_attribute: { store_attribute: { store_code: 'store code', diff --git a/test/integrations/destinations/google_adwords_offline_conversions/router/data.ts b/test/integrations/destinations/google_adwords_offline_conversions/router/data.ts index a38980f0e9..596e7550e5 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/router/data.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/router/data.ts @@ -487,7 +487,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -511,6 +511,10 @@ export const data = [ operations: [ { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code' }, transaction_amount_micros: '100000000', @@ -528,6 +532,10 @@ export const data = [ }, { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code2' }, transaction_amount_micros: '100000000', @@ -561,7 +569,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/7693729833:uploadCallConversions', + 'https://googleads.googleapis.com/v16/customers/7693729833:uploadCallConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -589,6 +597,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, callerId: '1234', callStartDateTime: '2019-10-14T11:15:18.299Z', conversionDateTime: '2019-10-14 16:45:18+05:30', @@ -673,7 +685,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -722,6 +734,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, gbraid: 'gbraid', wbraid: 'wbraid', externalAttributionData: { @@ -806,7 +822,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadCallConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadCallConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -855,6 +871,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, callerId: 'callerId', callStartDateTime: '2022-08-28 15:01:30+05:30', conversionDateTime: '2022-01-01 12:32:45-08:00', From 345c87d7c530c621ae3fd6c504d64e5a14e31f22 Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Tue, 26 Mar 2024 10:18:27 +0530 Subject: [PATCH 055/240] fix: shopify invalid_event metric prometheus label (#3200) --- src/v0/sources/shopify/transform.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v0/sources/shopify/transform.js b/src/v0/sources/shopify/transform.js index 4f09984054..93e3ed0c72 100644 --- a/src/v0/sources/shopify/transform.js +++ b/src/v0/sources/shopify/transform.js @@ -154,7 +154,7 @@ const processEvent = async (inputEvent, metricMetadata) => { default: if (!SUPPORTED_TRACK_EVENTS.includes(shopifyTopic)) { stats.increment('invalid_shopify_event', { - event: shopifyTopic, + writeKey: metricMetadata.writeKey, source: metricMetadata.source, shopifyTopic: metricMetadata.shopifyTopic, }); From f658a36ab986bbf92b361aefeed09bf7011422e9 Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Tue, 26 Mar 2024 10:28:09 +0530 Subject: [PATCH 056/240] chore: adding more package systems to dependabot (#3184) --- .github/dependabot.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b18fd29357..040e07f81d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,11 @@ updates: directory: '/' schedule: interval: 'weekly' + - package-ecosystem: 'docker' + directory: '/' + schedule: + interval: 'daily' + - package-ecosystem: 'npm' + directory: '/' + schedule: + interval: 'daily' From a86c2771034877cef4161cda61bcda5fdda2d89f Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:26:32 +0530 Subject: [PATCH 057/240] fix: tiktok_ads: validate message.event type (#3203) * fix: tiktok_ads: validate message.event type * fix: test cases * fix: validation for event name --- src/v0/destinations/tiktok_ads/transform.js | 8 +++----- src/v0/destinations/tiktok_ads/transformV2.js | 3 ++- .../destinations/tiktok_ads/processor/data.ts | 6 +++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/v0/destinations/tiktok_ads/transform.js b/src/v0/destinations/tiktok_ads/transform.js index ba852b9a97..17a37984c3 100644 --- a/src/v0/destinations/tiktok_ads/transform.js +++ b/src/v0/destinations/tiktok_ads/transform.js @@ -19,6 +19,7 @@ const { getHashFromArrayWithDuplicate, handleRtTfSingleEventError, batchMultiplexedEvents, + validateEventName, } = require('../../util'); const { process: processV2, processRouterDest: processRouterDestV2 } = require('./transformV2'); const { getContents } = require('./util'); @@ -129,12 +130,9 @@ const getTrackResponse = (message, Config, event) => { const trackResponseBuilder = async (message, { Config }) => { const { eventsToStandard, sendCustomEvents } = Config; - if (!message.event || typeof message.event !== 'string') { - throw new InstrumentationError('Either event name is not present or it is not a string'); - } - let event = message.event?.toLowerCase().trim(); + validateEventName(message.event); + let event = message.event.toLowerCase().trim(); const standardEventsMap = getHashFromArrayWithDuplicate(eventsToStandard); - if (!sendCustomEvents && eventNameMapping[event] === undefined && !standardEventsMap[event]) { throw new InstrumentationError( `Event name (${event}) is not valid, must be mapped to one of standard events`, diff --git a/src/v0/destinations/tiktok_ads/transformV2.js b/src/v0/destinations/tiktok_ads/transformV2.js index 48c5b19e64..3bd8699e3a 100644 --- a/src/v0/destinations/tiktok_ads/transformV2.js +++ b/src/v0/destinations/tiktok_ads/transformV2.js @@ -14,6 +14,7 @@ const { getDestinationExternalID, getHashFromArrayWithDuplicate, handleRtTfSingleEventError, + validateEventName, } = require('../../util'); const { getContents, hashUserField } = require('./util'); const config = require('./config'); @@ -59,7 +60,7 @@ const getTrackResponsePayload = (message, destConfig, event) => { const trackResponseBuilder = async (message, { Config }) => { const { eventsToStandard, sendCustomEvents, accessToken, pixelCode } = Config; - + validateEventName(message.event); let event = message.event?.toLowerCase().trim(); if (!event) { throw new InstrumentationError('Event name is required'); diff --git a/test/integrations/destinations/tiktok_ads/processor/data.ts b/test/integrations/destinations/tiktok_ads/processor/data.ts index d0447da43c..429024b8a9 100644 --- a/test/integrations/destinations/tiktok_ads/processor/data.ts +++ b/test/integrations/destinations/tiktok_ads/processor/data.ts @@ -1369,7 +1369,7 @@ export const data = [ body: [ { statusCode: 400, - error: 'Either event name is not present or it is not a string', + error: 'Event is a required field and should be a string', statTags: { errorCategory: 'dataValidation', errorType: 'instrumentation', @@ -6055,7 +6055,7 @@ export const data = [ body: [ { statusCode: 400, - error: 'Event name is required', + error: 'Event is a required field and should be a string', statTags: { errorCategory: 'dataValidation', errorType: 'instrumentation', @@ -7004,7 +7004,7 @@ export const data = [ body: [ { statusCode: 400, - error: 'Either event name is not present or it is not a string', + error: 'Event is a required field and should be a string', statTags: { errorCategory: 'dataValidation', errorType: 'instrumentation', From ec481001dd1b5035f4e5d541c929e761806cb08d Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:31:17 +0530 Subject: [PATCH 058/240] chore: update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da2fd7c565..5d3a37e122 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file. See [standa ### Bug Fixes * heap: make userId as required for track and identify call ([#3198](https://github.com/rudderlabs/rudder-transformer/issues/3198)) ([6a7c534](https://github.com/rudderlabs/rudder-transformer/commit/6a7c534a7df812bb7e39c1905eadcc29d7cd1329)) +* tiktok_ads: validate message.event type ([#3203](https://github.com/rudderlabs/rudder-transformer/issues/3203)) ([a86c277](https://github.com/rudderlabs/rudder-transformer/commit/a86c2771034877cef4161cda61bcda5fdda2d89f)) ## [1.59.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.59.0) (2024-03-18) From 808727de17e400ed102a843ab3b30f81f8900f24 Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Tue, 26 Mar 2024 19:01:27 +0530 Subject: [PATCH 059/240] fix: deployment file paths (#3216) --- .github/workflows/prepare-for-prod-dt-deploy.yml | 7 +++---- .github/workflows/prepare-for-prod-ut-deploy.yml | 7 +++---- .github/workflows/prepare-for-staging-deploy.yml | 8 ++++---- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/.github/workflows/prepare-for-prod-dt-deploy.yml b/.github/workflows/prepare-for-prod-dt-deploy.yml index a5ca48e3f8..fedbac8ba1 100644 --- a/.github/workflows/prepare-for-prod-dt-deploy.yml +++ b/.github/workflows/prepare-for-prod-dt-deploy.yml @@ -118,10 +118,9 @@ jobs: yq eval -i ".user-transformer.image.repository=\"$TF_IMAGE_REPOSITORY\"" multi-tenant/multi-tenant.yaml git add multi-tenant/multi-tenant.yaml - cd ../../../../config-be-rudder-transformer - yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" values.prod.yaml - yq eval -i ".config-be-rudder-transformer.image.repository=\"$TF_IMAGE_REPOSITORY\"" values.prod.yaml - git add values.prod.yaml + cd ../../../../config-be-rudder-transformer/environment/prod + yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" base.yaml + git add base.yaml git commit -m "chore: upgrade shared transformers to $TAG_NAME" git push -u origin shared-transformer-$TAG_NAME diff --git a/.github/workflows/prepare-for-prod-ut-deploy.yml b/.github/workflows/prepare-for-prod-ut-deploy.yml index c2900d61da..a6f7271d9c 100644 --- a/.github/workflows/prepare-for-prod-ut-deploy.yml +++ b/.github/workflows/prepare-for-prod-ut-deploy.yml @@ -102,11 +102,10 @@ jobs: cd rudder-devops git checkout -b shared-user-transformer-$UT_TAG_NAME - cd helm-charts/config-be-rudder-transformer + cd helm-charts/config-be-rudder-transformer/environment/prod - yq eval -i ".config-be-user-transformer.image.tag=\"$UT_TAG_NAME\"" values.prod.yaml - yq eval -i ".config-be-user-transformer.image.repository=\"$TF_IMAGE_REPOSITORY\"" values.prod.yaml - git add values.prod.yaml + yq eval -i ".config-be-user-transformer.image.tag=\"$UT_TAG_NAME\"" base.yaml + git add base.yaml git commit -m "chore: upgrade shared user-transformers to $UT_TAG_NAME" git push -u origin shared-user-transformer-$UT_TAG_NAME diff --git a/.github/workflows/prepare-for-staging-deploy.yml b/.github/workflows/prepare-for-staging-deploy.yml index 1bd7e276f4..46cd731d19 100644 --- a/.github/workflows/prepare-for-staging-deploy.yml +++ b/.github/workflows/prepare-for-staging-deploy.yml @@ -112,10 +112,10 @@ jobs: yq eval -i ".user-transformer.image.tag=\"$TAG_NAME\"" staging.yaml git add staging.yaml - cd ../../../../config-be-rudder-transformer - yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" values.staging.yaml - yq eval -i ".config-be-user-transformer.image.tag=\"$TAG_NAME\"" values.staging.yaml - git add values.staging.yaml + cd ../../../../config-be-rudder-transformer/environment/staging + yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" base.yaml + yq eval -i ".config-be-user-transformer.image.tag=\"$TAG_NAME\"" base.yaml + git add base.yaml git commit -m "chore: upgrade staging env transformers to \"$TAG_NAME\"" git push -u origin $BRANCH_NAME From 8a091449c4097d5dbae76eb0a16c03515d12ad38 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Tue, 26 Mar 2024 22:34:14 +0530 Subject: [PATCH 060/240] chore: component tests webhook (#3196) --- src/types/zodTypes.ts | 2 +- .../destinations/webhook/processor/data.ts | 217 ++++++++++ .../destinations/webhook/router/data.ts | 387 ++++++++++++++++++ 3 files changed, 605 insertions(+), 1 deletion(-) diff --git a/src/types/zodTypes.ts b/src/types/zodTypes.ts index 0a65a2bae2..75f12c5e9b 100644 --- a/src/types/zodTypes.ts +++ b/src/types/zodTypes.ts @@ -1,5 +1,5 @@ -import { z } from 'zod'; import { isDefinedAndNotNullAndNotEmpty } from '@rudderstack/integrations-lib'; +import { z } from 'zod'; import { isHttpStatusSuccess } from '../v0/util'; const ProcessorTransformationOutputSchema = z.object({ diff --git a/test/integrations/destinations/webhook/processor/data.ts b/test/integrations/destinations/webhook/processor/data.ts index dbe83a79a5..e9629041fe 100644 --- a/test/integrations/destinations/webhook/processor/data.ts +++ b/test/integrations/destinations/webhook/processor/data.ts @@ -2758,4 +2758,221 @@ export const data = [ }, }, }, + { + name: 'webhook', + description: 'Test POST method with track message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event: 'spin_result', + integrations: { + All: true, + }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { + additional_bet_index: 0, + level: 1, + }, + timestamp: '2019-09-01T15:46:51.693229+05:30', + type: 'track', + }, + destination: { + Config: { + webhookUrl: 'http://6b0e6a60.ngrok.io', + headers: [ + { + from: 'Content-Type', + to: 'application/xml', + }, + { + from: 'test2', + to: 'value2', + }, + ], + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + JSON: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event: 'spin_result', + integrations: { + All: true, + }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { + additional_bet_index: 0, + level: 1, + }, + timestamp: '2019-09-01T15:46:51.693229+05:30', + type: 'track', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + userId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + type: 'REST', + method: 'POST', + endpoint: 'http://6b0e6a60.ngrok.io', + headers: { + 'content-type': 'application/xml', + test2: 'value2', + }, + params: {}, + files: {}, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'webhook', + description: 'Test method PATCH', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + context: { + traits: { + address: { + city: 'Dhaka', + country: 'Bangladesh', + }, + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + }, + }, + event: 'spin_result', + integrations: { + All: true, + }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { + prop1: 1, + }, + timestamp: '2019-09-01T15:46:51.693229+05:30', + type: 'track', + user_properties: { + coin_balance: 9466052, + }, + }, + destination: { + Config: { + webhookUrl: 'http://6b0e6a60.ngrok.io', + webhookMethod: 'PATCH', + headers: [ + { + from: 'test2', + to: 'value2', + }, + ], + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + JSON: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + context: { + traits: { + address: { + city: 'Dhaka', + country: 'Bangladesh', + }, + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + }, + }, + event: 'spin_result', + integrations: { + All: true, + }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { + prop1: 1, + }, + timestamp: '2019-09-01T15:46:51.693229+05:30', + type: 'track', + user_properties: { + coin_balance: 9466052, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + userId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + type: 'REST', + method: 'PATCH', + endpoint: 'http://6b0e6a60.ngrok.io', + headers: { + 'content-type': 'application/json', + test2: 'value2', + }, + params: {}, + files: {}, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + statusCode: 200, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/webhook/router/data.ts b/test/integrations/destinations/webhook/router/data.ts index a082eb12f1..6c738ee8a7 100644 --- a/test/integrations/destinations/webhook/router/data.ts +++ b/test/integrations/destinations/webhook/router/data.ts @@ -419,4 +419,391 @@ export const data = [ }, }, }, + { + name: 'webhook', + description: 'Identify payload with 3 events in 1 batch', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + anonymousId: '234234234234234', + channel: 'mobile', + context: { + app: { + build: '1', + name: 'AMTestProject', + namespace: 'com.rudderstack.android.rudderstack.sampleAndroidApp', + version: '1.0', + }, + device: { + id: '8d872292709c6fbe', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'android', + }, + library: { + name: 'com.rudderstack.android.sdk.core', + version: '1.0.2', + }, + locale: 'en-US', + network: { + carrier: 'Android', + bluetooth: false, + cellular: true, + wifi: true, + }, + os: { + name: 'Android', + version: '9', + }, + screen: { + density: 420, + height: 1794, + width: 1080, + }, + timezone: 'Asia/Kolkata', + traits: { + address: { + city: 'Kolkata', + country: 'India', + postalcode: '700096', + state: 'West bengal', + street: 'Park Street', + }, + age: '30', + anonymousId: '8d872292709c6fbe', + birthday: '2020-05-26', + createdat: '18th March 2020', + description: 'Premium User for 3 years', + email: 'identify@test.com', + firstname: 'John', + userId: 'sample_user_id', + lastname: 'Sparrow', + name: 'John Sparrow', + id: 'sample_user_id', + phone: '9876543210', + username: 'john_sparrow', + quantity: '5', + price: '56.0', + }, + userAgent: + 'Dalvik/2.1.0 (Linux; U; Android 9; AOSP on IA Emulator Build/PSR1.180720.117)', + }, + event: 'identify', + integrations: { + All: true, + }, + messageId: '1590431830865-3be680d6-7dcd-4b05-8460-f3acc30046d9', + originalTimestamp: '2020-05-25T18:37:10.865Z', + sentAt: '2020-05-25T18:37:10.917Z', + type: 'identify', + userId: 'sample_user_id', + }, + metadata: { jobId: 2, userId: 'u1' }, + destination: { + Config: { + webhookUrl: 'http://6b0e6a60.ngrok.io', + headers: [ + { from: '', to: '' }, + { from: 'test2', to: 'value2' }, + ], + }, + DestinationDefinition: { Config: { cdkV2Enabled: true } }, + }, + }, + { + message: { + anonymousId: '1231241234123', + channel: 'mobile', + context: { + timezone: 'Asia/Kolkata', + traits: { + address: { + city: 'Kolkata', + country: 'India', + postalcode: '700096', + state: 'West bengal', + street: 'Park Street', + }, + age: '30', + anonymousId: '8d872292709c6fbe', + birthday: '2020-05-26', + createdat: '18th March 2020', + description: 'Premium User for 3 years', + email: 'identify2@test.com', + firstname: 'John', + userId: 'sample_user_id', + lastname: 'Sparrow', + name: 'John Sparrow', + id: 'sample_user_id', + phone: '9876543210', + username: 'john_sparrow', + quantity: '5', + price: '56.0', + }, + userAgent: + 'Dalvik/2.1.0 (Linux; U; Android 9; AOSP on IA Emulator Build/PSR1.180720.117)', + }, + event: 'identify', + integrations: { + All: true, + }, + messageId: '23432324-3be680d6-7dcd-4b05-8460-f3acc30046d9', + type: 'identify', + userId: 'sample_user_id', + }, + metadata: { jobId: 3, userId: 'u1' }, + destination: { + Config: { webhookUrl: 'https://6b0e6a60.ngrok.io/n' }, + DestinationDefinition: { Config: { cdkV2Enabled: true } }, + }, + }, + { + message: { + anonymousId: '345345435', + channel: 'mobile', + event: 'identify', + integrations: { + All: true, + }, + messageId: '23432324-3be680d6-7dcd-4b05-8460-f3acc30046d9', + type: 'identify', + userId: 'sample_user_id', + }, + metadata: { jobId: 4, userId: 'u1' }, + destination: { + Config: { webhookUrl: 'https://6b0e6a60.ngrok.io/n' }, + DestinationDefinition: { Config: { cdkV2Enabled: true } }, + }, + }, + ], + destType: 'webhook', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + body: { + XML: {}, + JSON_ARRAY: {}, + JSON: { + anonymousId: '234234234234234', + channel: 'mobile', + context: { + app: { + build: '1', + name: 'AMTestProject', + namespace: 'com.rudderstack.android.rudderstack.sampleAndroidApp', + version: '1.0', + }, + device: { + id: '8d872292709c6fbe', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'android', + }, + library: { + name: 'com.rudderstack.android.sdk.core', + version: '1.0.2', + }, + locale: 'en-US', + network: { + carrier: 'Android', + bluetooth: false, + cellular: true, + wifi: true, + }, + os: { + name: 'Android', + version: '9', + }, + screen: { + density: 420, + height: 1794, + width: 1080, + }, + timezone: 'Asia/Kolkata', + traits: { + address: { + city: 'Kolkata', + country: 'India', + postalcode: '700096', + state: 'West bengal', + street: 'Park Street', + }, + age: '30', + anonymousId: '8d872292709c6fbe', + birthday: '2020-05-26', + createdat: '18th March 2020', + description: 'Premium User for 3 years', + email: 'identify@test.com', + firstname: 'John', + userId: 'sample_user_id', + lastname: 'Sparrow', + name: 'John Sparrow', + id: 'sample_user_id', + phone: '9876543210', + username: 'john_sparrow', + quantity: '5', + price: '56.0', + }, + userAgent: + 'Dalvik/2.1.0 (Linux; U; Android 9; AOSP on IA Emulator Build/PSR1.180720.117)', + }, + event: 'identify', + integrations: { + All: true, + }, + messageId: '1590431830865-3be680d6-7dcd-4b05-8460-f3acc30046d9', + originalTimestamp: '2020-05-25T18:37:10.865Z', + sentAt: '2020-05-25T18:37:10.917Z', + type: 'identify', + userId: 'sample_user_id', + }, + FORM: {}, + }, + files: {}, + endpoint: 'http://6b0e6a60.ngrok.io', + userId: '234234234234234', + headers: { 'content-type': 'application/json', test2: 'value2' }, + version: '1', + params: {}, + type: 'REST', + method: 'POST', + }, + metadata: [{ jobId: 2, userId: 'u1' }], + batched: false, + statusCode: 200, + destination: { + Config: { + webhookUrl: 'http://6b0e6a60.ngrok.io', + headers: [ + { from: '', to: '' }, + { from: 'test2', to: 'value2' }, + ], + }, + DestinationDefinition: { Config: { cdkV2Enabled: true } }, + }, + }, + { + batchedRequest: { + body: { + XML: {}, + JSON_ARRAY: {}, + JSON: { + anonymousId: '1231241234123', + channel: 'mobile', + context: { + timezone: 'Asia/Kolkata', + traits: { + address: { + city: 'Kolkata', + country: 'India', + postalcode: '700096', + state: 'West bengal', + street: 'Park Street', + }, + age: '30', + anonymousId: '8d872292709c6fbe', + birthday: '2020-05-26', + createdat: '18th March 2020', + description: 'Premium User for 3 years', + email: 'identify2@test.com', + firstname: 'John', + userId: 'sample_user_id', + lastname: 'Sparrow', + name: 'John Sparrow', + id: 'sample_user_id', + phone: '9876543210', + username: 'john_sparrow', + quantity: '5', + price: '56.0', + }, + userAgent: + 'Dalvik/2.1.0 (Linux; U; Android 9; AOSP on IA Emulator Build/PSR1.180720.117)', + }, + event: 'identify', + integrations: { + All: true, + }, + messageId: '23432324-3be680d6-7dcd-4b05-8460-f3acc30046d9', + type: 'identify', + userId: 'sample_user_id', + }, + FORM: {}, + }, + files: {}, + endpoint: 'https://6b0e6a60.ngrok.io/n', + userId: '1231241234123', + headers: { 'content-type': 'application/json' }, + version: '1', + params: {}, + type: 'REST', + method: 'POST', + }, + metadata: [{ jobId: 3, userId: 'u1' }], + batched: false, + statusCode: 200, + destination: { + Config: { + webhookUrl: 'https://6b0e6a60.ngrok.io/n', + }, + DestinationDefinition: { Config: { cdkV2Enabled: true } }, + }, + }, + { + batchedRequest: { + body: { + XML: {}, + JSON_ARRAY: {}, + JSON: { + anonymousId: '345345435', + channel: 'mobile', + event: 'identify', + integrations: { + All: true, + }, + messageId: '23432324-3be680d6-7dcd-4b05-8460-f3acc30046d9', + type: 'identify', + userId: 'sample_user_id', + }, + FORM: {}, + }, + files: {}, + endpoint: 'https://6b0e6a60.ngrok.io/n', + userId: '345345435', + headers: { 'content-type': 'application/json' }, + version: '1', + params: {}, + type: 'REST', + method: 'POST', + }, + metadata: [{ jobId: 4, userId: 'u1' }], + batched: false, + statusCode: 200, + destination: { + Config: { + webhookUrl: 'https://6b0e6a60.ngrok.io/n', + }, + DestinationDefinition: { Config: { cdkV2Enabled: true } }, + }, + }, + ], + }, + }, + }, + }, ]; From b1b479faae4f2dd359fbbc6078feb2e6d8f71849 Mon Sep 17 00:00:00 2001 From: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Date: Wed, 27 Mar 2024 10:23:37 +0530 Subject: [PATCH 061/240] chore: add mixpanel batch size metrics (#3201) * chore: add mixpanel batch size metrics * refactor: reduce duplicate code * fix: calculate batch metrics after chuncking --- src/util/prometheus.js | 24 ++++++++++++++++++++++++ src/v0/destinations/mp/transform.js | 16 ++++++++++++++++ src/v0/destinations/mp/util.js | 28 ++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 5de7ac899d..09d0359b5d 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -575,6 +575,30 @@ class Prometheus { type: 'gauge', labelNames: ['destination_id'], }, + { + name: 'mixpanel_batch_engage_pack_size', + help: 'mixpanel_batch_engage_pack_size', + type: 'gauge', + labelNames: ['destination_id'], + }, + { + name: 'mixpanel_batch_group_pack_size', + help: 'mixpanel_batch_group_pack_size', + type: 'gauge', + labelNames: ['destination_id'], + }, + { + name: 'mixpanel_batch_track_pack_size', + help: 'mixpanel_batch_track_pack_size', + type: 'gauge', + labelNames: ['destination_id'], + }, + { + name: 'mixpanel_batch_import_pack_size', + help: 'mixpanel_batch_import_pack_size', + type: 'gauge', + labelNames: ['destination_id'], + }, // Histograms { diff --git a/src/v0/destinations/mp/transform.js b/src/v0/destinations/mp/transform.js index 10271bebef..a2c40a5672 100644 --- a/src/v0/destinations/mp/transform.js +++ b/src/v0/destinations/mp/transform.js @@ -37,6 +37,7 @@ const { batchEvents, trimTraits, generatePageOrScreenCustomEventName, + recordBatchSizeMetrics, } = require('./util'); const { CommonUtils } = require('../../../util/common'); @@ -479,6 +480,13 @@ const process = (event) => processSingleMessage(event.message, event.destination // Ref: https://help.mixpanel.com/hc/en-us/articles/115004613766-Default-Properties-Collected-by-Mixpanel // Ref: https://help.mixpanel.com/hc/en-us/articles/115004561786-Track-UTM-Tags const processRouterDest = async (inputs, reqMetadata) => { + const batchSize = { + engage: 0, + groups: 0, + track: 0, + import: 0, + }; + const groupedEvents = groupEventsByType(inputs); const response = await Promise.all( groupedEvents.map(async (listOfEvents) => { @@ -521,12 +529,20 @@ const processRouterDest = async (inputs, reqMetadata) => { ...importRespList, ]; + batchSize.engage += engageRespList.length; + batchSize.groups += groupsRespList.length; + batchSize.track += trackRespList.length; + batchSize.import += importRespList.length; + return [...batchSuccessRespList, ...batchErrorRespList]; }), ); // Flatten the response array containing batched events from multiple groups const allBatchedEvents = lodash.flatMap(response); + + const { destination } = allBatchedEvents[0]; + recordBatchSizeMetrics(batchSize, destination.ID); return combineBatchRequestsWithSameJobIds(allBatchedEvents); }; diff --git a/src/v0/destinations/mp/util.js b/src/v0/destinations/mp/util.js index f56242d88b..d564e805ad 100644 --- a/src/v0/destinations/mp/util.js +++ b/src/v0/destinations/mp/util.js @@ -25,6 +25,7 @@ const { mappingConfig, } = require('./config'); const { CommonUtils } = require('../../../util/common'); +const stats = require('../../../util/stats'); const mPIdentifyConfigJson = mappingConfig[ConfigCategory.IDENTIFY.name]; const mPProfileAndroidConfigJson = mappingConfig[ConfigCategory.PROFILE_ANDROID.name]; @@ -342,6 +343,32 @@ const generatePageOrScreenCustomEventName = (message, userDefinedEventTemplate) return eventName; }; +/** + * Records the batch size metrics for different endpoints. + * + * @param {Object} batchSize - The object containing the batch size for different endpoints. + * @param {number} batchSize.engage - The batch size for engage endpoint. + * @param {number} batchSize.groups - The batch size for group endpoint. + * @param {number} batchSize.track - The batch size for track endpoint. + * @param {number} batchSize.import - The batch size for import endpoint. + * @param {string} destinationId - The ID of the destination. + * @returns {void} + */ +const recordBatchSizeMetrics = (batchSize, destinationId) => { + stats.gauge('mixpanel_batch_engage_pack_size', batchSize.engage, { + destination_id: destinationId, + }); + stats.gauge('mixpanel_batch_group_pack_size', batchSize.groups, { + destination_id: destinationId, + }); + stats.gauge('mixpanel_batch_track_pack_size', batchSize.track, { + destination_id: destinationId, + }); + stats.gauge('mixpanel_batch_import_pack_size', batchSize.import, { + destination_id: destinationId, + }); +}; + module.exports = { createIdentifyResponse, isImportAuthCredentialsAvailable, @@ -351,4 +378,5 @@ module.exports = { batchEvents, trimTraits, generatePageOrScreenCustomEventName, + recordBatchSizeMetrics, }; From 70a468bf16ecd5ee0b6fecee4b837895d19c525f Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Thu, 28 Mar 2024 11:05:42 +0530 Subject: [PATCH 062/240] fix: fixed userId mapping, now mapping to uid instead of id (#3192) --- src/cdk/v2/destinations/fullstory/procWorkflow.yaml | 2 +- test/integrations/destinations/fullstory/processor/data.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdk/v2/destinations/fullstory/procWorkflow.yaml b/src/cdk/v2/destinations/fullstory/procWorkflow.yaml index 1a54e8688c..26da955623 100644 --- a/src/cdk/v2/destinations/fullstory/procWorkflow.yaml +++ b/src/cdk/v2/destinations/fullstory/procWorkflow.yaml @@ -76,7 +76,7 @@ steps: "use_most_recent": .message.properties.useMostRecent, }; $.context.payload.user = { - "id": .message.properties.userId ?? .message.userId, + "uid": .message.properties.userId ?? .message.userId, } - name: cleanPayload diff --git a/test/integrations/destinations/fullstory/processor/data.ts b/test/integrations/destinations/fullstory/processor/data.ts index d206b4a84f..9c8d29c7e8 100644 --- a/test/integrations/destinations/fullstory/processor/data.ts +++ b/test/integrations/destinations/fullstory/processor/data.ts @@ -149,7 +149,7 @@ export const data = [ id: 's001', }, user: { - id: 'u001', + uid: 'u001', }, }, JSON_ARRAY: {}, From 81290e8ae43f52e01f04bd426238273d93759070 Mon Sep 17 00:00:00 2001 From: Rohith BCS Date: Thu, 28 Mar 2024 14:43:44 +0530 Subject: [PATCH 063/240] chore: skip users and tracks tables with destConfig (#3214) --- .../destinations/azure_datalake/transform.js | 15 +- .../destinations/azure_synapse/transform.js | 11 +- src/v0/destinations/bq/transform.js | 11 +- src/v0/destinations/clickhouse/transform.js | 59 ++--- src/v0/destinations/deltalake/transform.js | 19 +- src/v0/destinations/gcs_datalake/transform.js | 19 +- src/v0/destinations/mssql/transform.js | 12 +- src/v0/destinations/postgres/transform.js | 11 +- src/v0/destinations/rs/transform.js | 11 +- src/v0/destinations/s3_datalake/transform.js | 12 +- src/v0/destinations/snowflake/transform.js | 11 +- src/warehouse/index.js | 8 +- .../data/warehouse/dest_config_scenarios.js | 223 ++++++++++++++++++ test/__tests__/warehouse.test.js | 23 ++ 14 files changed, 328 insertions(+), 117 deletions(-) create mode 100644 test/__tests__/data/warehouse/dest_config_scenarios.js diff --git a/src/v0/destinations/azure_datalake/transform.js b/src/v0/destinations/azure_datalake/transform.js index 8d29c70e06..6c97e8671f 100644 --- a/src/v0/destinations/azure_datalake/transform.js +++ b/src/v0/destinations/azure_datalake/transform.js @@ -1,25 +1,24 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const azureDatalake = 'azure_datalake'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} +const provider = 'azure_datalake'; function getDataTypeOverride() {} function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; - const provider = azureDatalake; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, getDataTypeOverride, provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, + destConfig: event.destination?.Config, }); } -exports.process = process; +module.exports = { + provider, + process, +}; diff --git a/src/v0/destinations/azure_synapse/transform.js b/src/v0/destinations/azure_synapse/transform.js index d98f269475..a80ad7cbdc 100644 --- a/src/v0/destinations/azure_synapse/transform.js +++ b/src/v0/destinations/azure_synapse/transform.js @@ -1,28 +1,25 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const azureSynapse = 'azure_synapse'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} +const provider = 'azure_synapse'; function getDataTypeOverride() {} function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; - const provider = azureSynapse; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, getDataTypeOverride, provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/v0/destinations/bq/transform.js b/src/v0/destinations/bq/transform.js index e9e496f6a4..8c8be140a9 100644 --- a/src/v0/destinations/bq/transform.js +++ b/src/v0/destinations/bq/transform.js @@ -1,10 +1,6 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const bigquery = 'bq'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} +const provider = 'bq'; function getDataTypeOverride() {} @@ -13,8 +9,7 @@ function process(event) { const whIDResolve = event.request.query.whIDResolve === 'true' || false; const whStoreEvent = event.destination.Config.storeFullEvent === true; const destJsonPaths = event.destination?.Config?.jsonPaths || ''; - const provider = bigquery; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, @@ -23,10 +18,12 @@ function process(event) { provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, destJsonPaths, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/v0/destinations/clickhouse/transform.js b/src/v0/destinations/clickhouse/transform.js index 24158cc41f..491475419c 100644 --- a/src/v0/destinations/clickhouse/transform.js +++ b/src/v0/destinations/clickhouse/transform.js @@ -1,53 +1,44 @@ const { processWarehouseMessage } = require('../../../warehouse'); const { getDataType } = require('../../../warehouse/index'); -const clickhouse = 'clickhouse'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} +const provider = 'clickhouse'; function getDataTypeOverride(key, val, options) { if (options.chEnableArraySupport === 'false') { return 'string'; } - if (Array.isArray(val)) { - // for now returning it as string. confirm this case - if (val.length === 0) { - return 'string'; - } - // check for different data types in the array. if there are different then return array(string) - const firstValueDataType = getDataType(key, val[0], {}); - let finalDataType = firstValueDataType; - for (let i = 1; i < val.length; i += 1) { - const dataType = getDataType(key, val[i], {}); - if (finalDataType !== dataType) { - if (finalDataType === 'string') { - break; - } - if (dataType === 'float' && finalDataType === 'int') { - finalDataType = 'float'; - // eslint-disable-next-line no-continue - continue; - } - if (dataType === 'int' && finalDataType === 'float') { - // eslint-disable-next-line no-continue - continue; - } - finalDataType = 'string'; + if (!Array.isArray(val) || val.length === 0) { + return 'string'; + } + + // check for different data types in the array. if there are different -> return array(string) + let finalDataType = getDataType(key, val[0], {}); + for (let i = 1; i < val.length; i += 1) { + const dataType = getDataType(key, val[i], {}); + if (finalDataType !== dataType) { + if (finalDataType === 'string') { + break; + } + if (dataType === 'float' && finalDataType === 'int') { + finalDataType = 'float'; + // eslint-disable-next-line no-continue + continue; + } + if (dataType === 'int' && finalDataType === 'float') { + // eslint-disable-next-line no-continue + continue; } + finalDataType = 'string'; } - return `array(${finalDataType})`; } - return 'string'; + return `array(${finalDataType})`; } function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; - const provider = clickhouse; const chEnableArraySupport = event.request.query.chEnableArraySupport || 'false'; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, @@ -55,10 +46,12 @@ function process(event) { provider, chEnableArraySupport, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/v0/destinations/deltalake/transform.js b/src/v0/destinations/deltalake/transform.js index 49d40131d4..637b64cf36 100644 --- a/src/v0/destinations/deltalake/transform.js +++ b/src/v0/destinations/deltalake/transform.js @@ -1,25 +1,22 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const deltalake = 'deltalake'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} - -function getDataTypeOverride() {} +const provider = 'deltalake'; function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; - const provider = deltalake; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, - getDataTypeOverride, + getDataTypeOverride: () => {}, provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, + destConfig: event.destination?.Config, }); } -exports.process = process; +module.exports = { + provider, + process, +}; diff --git a/src/v0/destinations/gcs_datalake/transform.js b/src/v0/destinations/gcs_datalake/transform.js index 3e5f1dcfa3..366dcf3483 100644 --- a/src/v0/destinations/gcs_datalake/transform.js +++ b/src/v0/destinations/gcs_datalake/transform.js @@ -1,25 +1,22 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const gcsDatalake = 'gcs_datalake'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} - -function getDataTypeOverride() {} +const provider = 'gcs_datalake'; function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; - const provider = gcsDatalake; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, - getDataTypeOverride, + getDataTypeOverride: () => {}, provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, + destConfig: event.destination?.Config, }); } -exports.process = process; +module.exports = { + provider, + process, +}; diff --git a/src/v0/destinations/mssql/transform.js b/src/v0/destinations/mssql/transform.js index 2baadebdee..12dd7b40c6 100644 --- a/src/v0/destinations/mssql/transform.js +++ b/src/v0/destinations/mssql/transform.js @@ -1,28 +1,24 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const mssql = 'mssql'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} - +const provider = 'mssql'; function getDataTypeOverride() {} function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; - const provider = mssql; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, getDataTypeOverride, provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/v0/destinations/postgres/transform.js b/src/v0/destinations/postgres/transform.js index 32c6b0a069..b57bf4369a 100644 --- a/src/v0/destinations/postgres/transform.js +++ b/src/v0/destinations/postgres/transform.js @@ -1,10 +1,6 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const postgres = 'postgres'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} +const provider = 'postgres'; function getDataTypeOverride(key, val, options, jsonKey = false) { if (key === 'violationErrors' || jsonKey) { @@ -17,8 +13,7 @@ function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; const destJsonPaths = event.destination?.Config?.jsonPaths || ''; - const provider = postgres; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, @@ -26,10 +21,12 @@ function process(event) { provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, destJsonPaths, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/v0/destinations/rs/transform.js b/src/v0/destinations/rs/transform.js index f051ff49d5..781600a8e2 100644 --- a/src/v0/destinations/rs/transform.js +++ b/src/v0/destinations/rs/transform.js @@ -2,11 +2,7 @@ const { processWarehouseMessage } = require('../../../warehouse'); // redshift destination string limit, if the string length crosses 512 we will change data type to text which is varchar(max) in redshift const RSStringLimit = 512; -const redshift = 'rs'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} +const provider = 'rs'; function getDataTypeOverride(key, val, options, jsonKey = false) { if (jsonKey) { @@ -26,8 +22,7 @@ function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; const destJsonPaths = event.destination?.Config?.jsonPaths || ''; - const provider = redshift; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, @@ -35,10 +30,12 @@ function process(event) { provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, destJsonPaths, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/v0/destinations/s3_datalake/transform.js b/src/v0/destinations/s3_datalake/transform.js index 7013224faa..8bbfa1556d 100644 --- a/src/v0/destinations/s3_datalake/transform.js +++ b/src/v0/destinations/s3_datalake/transform.js @@ -1,29 +1,25 @@ const { processWarehouseMessage } = require('../../../warehouse'); // use postgres providers for s3-datalake -const s3datalakeProvider = 's3_datalake'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} - +const provider = 's3_datalake'; function getDataTypeOverride() {} function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; - const provider = s3datalakeProvider; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, getDataTypeOverride, provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/v0/destinations/snowflake/transform.js b/src/v0/destinations/snowflake/transform.js index 7682d13db3..bf53c57978 100644 --- a/src/v0/destinations/snowflake/transform.js +++ b/src/v0/destinations/snowflake/transform.js @@ -1,10 +1,6 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const snowflake = 'snowflake'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} +const provider = 'snowflake'; function getDataTypeOverride(key, val, options, jsonKey = false) { if (key === 'violationErrors' || jsonKey) { @@ -18,8 +14,7 @@ function process(event) { const whIDResolve = event.request.query.whIDResolve === 'true' || false; const whStoreEvent = event.destination.Config.storeFullEvent === true; const destJsonPaths = event.destination?.Config?.jsonPaths || ''; - const provider = snowflake; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, @@ -28,10 +23,12 @@ function process(event) { provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, destJsonPaths, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/warehouse/index.js b/src/warehouse/index.js index b3d1c5e4bc..f62f02af79 100644 --- a/src/warehouse/index.js +++ b/src/warehouse/index.js @@ -154,7 +154,7 @@ function setDataFromColumnMappingAndComputeColumnTypes( const columnName = utils.safeColumnName(options, key); // do not set column if val is null/empty/object if (typeof val === 'object' || isBlank(val)) { - // delete in output and columnTypes, so as to remove if we user + // delete in output and columnTypes, to remove if the user // has set property with same name // eslint-disable-next-line no-param-reassign delete output[columnName]; @@ -565,8 +565,10 @@ function processWarehouseMessage(message, options) { : {}; const responses = []; const eventType = message.type?.toLowerCase(); - const skipTracksTable = options.integrationOptions.skipTracksTable || false; - const skipUsersTable = options.integrationOptions.skipUsersTable || false; + const skipTracksTable = + options.destConfig?.skipTracksTable || options.integrationOptions.skipTracksTable || false; + const skipUsersTable = + options.destConfig?.skipUsersTable || options.integrationOptions.skipUsersTable || false; const skipReservedKeywordsEscaping = options.integrationOptions.skipReservedKeywordsEscaping || false; diff --git a/test/__tests__/data/warehouse/dest_config_scenarios.js b/test/__tests__/data/warehouse/dest_config_scenarios.js new file mode 100644 index 0000000000..0e290b7153 --- /dev/null +++ b/test/__tests__/data/warehouse/dest_config_scenarios.js @@ -0,0 +1,223 @@ +const _ = require("lodash"); + +const trackMessage = { + destination: { Config: {} }, + message: { + type: "track", + messageId: "my-track-message-id-1", + userId: "9bb5d4c2-a7aa-4a36-9efb-dd2b1aec5d33", + anonymousId: "e6ab2c5e-2cda-44a9-a962-e2f67df78bca", + channel: "web", + context: { + app: { + build: "1.0.0", + name: "RudderLabs JavaScript SDK", + namespace: "com.rudderlabs.javascript", + version: "1.0.5" + }, + ip: "0.0.0.0", + library: { + name: "RudderLabs JavaScript SDK", + version: "1.0.5" + }, + locale: "en-GB", + os: { + name: "", + version: "" + }, + screen: { density: 2 }, + traits: { + city: "Disney", + country: "USA", + email: "mickey@disney.com", + firstname: "Mickey" + }, + userAgent: "Mozilla/5.0 Chrome/79.0.3945.117 Safari/537.36" + }, + event: "groups", + integrations: { All: true }, + originalTimestamp: "2020-01-24T06:29:02.364Z", + properties: { currency: "USD" }, + receivedAt: "2020-01-24T11:59:02.403+05:30", + request_ip: "[::1]:53708", + sentAt: "2020-01-24T06:29:02.364Z", + timestamp: "2020-01-24T11:59:02.403+05:30" + }, + request: { query: { whSchemaVersion: "v1" } } +}; + +const identifyMessage = { + destination: { Config: {} }, + message: { + type: "identify", + messageId: "my-identify-message-id-1", + sentAt: "2021-01-03T17:02:53.195Z", + userId: "user123", + channel: "web", + integrations: { All: true }, + context: { + os: { + "name": "android", + "version": "1.12.3" + }, + app: { + name: "RudderLabs JavaScript SDK", + build: "1.0.0", + version: "1.1.11", + namespace: "com.rudderlabs.javascript" + }, + traits: { + email: "user123@email.com", + phone: "+917836362334", + userId: "user123" + }, + locale: "en-US", + device: { + token: "token", + id: "id", + type: "ios" + }, + library: { + name: "RudderLabs JavaScript SDK", + version: "1.1.11" + }, + userAgent: "Gecko/20100101 Firefox/84.0" + }, + rudderId: "8f8fa6b5-8e24-489c-8e22-61f23f2e364f", + anonymousId: "97c46c81-3140-456d-b2a9-690d70aaca35", + originalTimestamp: "2020-01-24T06:29:02.364Z", + receivedAt: "2020-01-24T11:59:02.403+05:30", + request_ip: "[::1]:53708", + timestamp: "2020-01-24T11:59:02.403+05:30" + }, + request: { query: { whSchemaVersion: "v1" } } +}; + +const scenarios = [ + { + name: "track event is not skipped when options are not provided", + skipUsersTable: null, + skipTracksTable: null, + event: _.cloneDeep(trackMessage), + expected: [ + { + id: "my-track-message-id-1", + table: "tracks" + }, + { + id: "my-track-message-id-1", + table: "_groups" + } + ] + }, + { + name: "track event is not skipped when skipTracksTable is false", + skipUsersTable: null, + skipTracksTable: false, + event: _.cloneDeep(trackMessage), + expected: [ + { + id: "my-track-message-id-1", + table: "tracks" + }, + { + id: "my-track-message-id-1", + table: "_groups" + } + ] + }, + { + name: "track event is skipped when skipTracksTable is true", + skipUsersTable: null, + skipTracksTable: true, + event: _.cloneDeep(trackMessage), + expected: [ + { + id: "my-track-message-id-1", + table: "_groups" + } + ] + }, + { + name: "track event is not affected by skipUsersTable", + skipUsersTable: true, + skipTracksTable: null, + event: _.cloneDeep(trackMessage), + expected: [ + { + id: "my-track-message-id-1", + table: "tracks" + }, + { + id: "my-track-message-id-1", + table: "_groups" + } + ] + }, + { + name: "user event is not skipped when options are not provided", + skipUsersTable: null, + skipTracksTable: null, + event: _.cloneDeep(identifyMessage), + expected: [ + { + id: "my-identify-message-id-1", + table: "identifies" + }, + { + id: "user123", + table: "users" + } + ] + }, + { + name: "user event is not skipped when skipUsersTable is false", + skipUsersTable: false, + skipTracksTable: null, + event: _.cloneDeep(identifyMessage), + expected: [ + { + id: "my-identify-message-id-1", + table: "identifies" + }, + { + id: "user123", + table: "users" + } + ] + }, + { + name: "user event is skipped when skipUsersTable is true", + skipUsersTable: true, + skipTracksTable: null, + event: _.cloneDeep(identifyMessage), + expected: [ + { + id: "my-identify-message-id-1", + table: "identifies" + } + ] + }, + { + name: "user event is not affected by skipTracksTable", + skipUsersTable: null, + skipTracksTable: true, + event: _.cloneDeep(identifyMessage), + expected: [ + { + id: "my-identify-message-id-1", + table: "identifies" + }, + { + id: "user123", + table: "users" + } + ] + }, +]; + +module.exports = { + scenarios: function() { + return _.cloneDeep(scenarios); + } +}; \ No newline at end of file diff --git a/test/__tests__/warehouse.test.js b/test/__tests__/warehouse.test.js index 772e59e65a..2c89120686 100644 --- a/test/__tests__/warehouse.test.js +++ b/test/__tests__/warehouse.test.js @@ -8,6 +8,7 @@ const { opOutput } = require(`./data/warehouse/integration_options_events.js`); const { names } = require(`./data/warehouse/names.js`); +const destConfig = require(`./data/warehouse/dest_config_scenarios.js`); const { largeNoOfColumnsevent } = require(`./data/warehouse/event_columns_length`); @@ -1009,6 +1010,28 @@ describe("Add receivedAt for events missing it", () => { }); describe("Integration options", () => { + describe("Destination config options", () => { + destConfig.scenarios().forEach(scenario => { + it(scenario.name, () => { + if (scenario.skipUsersTable !== null) { + scenario.event.destination.Config.skipUsersTable = scenario.skipUsersTable + } + if (scenario.skipTracksTable !== null) { + scenario.event.destination.Config.skipTracksTable = scenario.skipTracksTable + } + + transformers.forEach((transformer, index) => { + const received = transformer.process(scenario.event); + expect(received).toHaveLength(scenario.expected.length); + for (const i in received) { + const evt = received[i]; + expect(evt.data.id ? evt.data.id : evt.data.ID).toEqual(scenario.expected[i].id); + expect(evt.metadata.table.toLowerCase()).toEqual(scenario.expected[i].table); + } + }); + }); + }); + }); describe("track", () => { it("should generate two events for every track call", () => { const i = opInput("track"); From 2f30c56af62e983d09b5d4f2da9a0ba22f5c1612 Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Mon, 1 Apr 2024 13:45:37 +0530 Subject: [PATCH 064/240] fix: ninetailed: remove page support (#3218) --- src/cdk/v2/destinations/ninetailed/config.js | 4 - .../ninetailed/data/pageMapping.json | 7 -- .../destinations/ninetailed/procWorkflow.yaml | 2 +- src/cdk/v2/destinations/ninetailed/utils.js | 6 - .../destinations/ninetailed/processor/data.ts | 3 +- .../destinations/ninetailed/processor/page.ts | 108 ------------------ .../destinations/ninetailed/router/data.ts | 48 ++------ 7 files changed, 14 insertions(+), 164 deletions(-) delete mode 100644 src/cdk/v2/destinations/ninetailed/data/pageMapping.json delete mode 100644 test/integrations/destinations/ninetailed/processor/page.ts diff --git a/src/cdk/v2/destinations/ninetailed/config.js b/src/cdk/v2/destinations/ninetailed/config.js index c38496a415..a59b2a1671 100644 --- a/src/cdk/v2/destinations/ninetailed/config.js +++ b/src/cdk/v2/destinations/ninetailed/config.js @@ -17,10 +17,6 @@ const ConfigCategories = { type: 'identify', name: 'identifyMapping', }, - PAGE: { - type: 'page', - name: 'pageMapping', - }, }; // MAX_BATCH_SIZE : // Maximum number of events to send in a single batch diff --git a/src/cdk/v2/destinations/ninetailed/data/pageMapping.json b/src/cdk/v2/destinations/ninetailed/data/pageMapping.json deleted file mode 100644 index 80ec2f58f1..0000000000 --- a/src/cdk/v2/destinations/ninetailed/data/pageMapping.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - { - "sourceKeys": "properties", - "required": true, - "destKey": "properties" - } -] diff --git a/src/cdk/v2/destinations/ninetailed/procWorkflow.yaml b/src/cdk/v2/destinations/ninetailed/procWorkflow.yaml index 6f5056ce10..383b850a4d 100644 --- a/src/cdk/v2/destinations/ninetailed/procWorkflow.yaml +++ b/src/cdk/v2/destinations/ninetailed/procWorkflow.yaml @@ -16,7 +16,7 @@ steps: template: | let messageType = $.outputs.messageType; $.assert(messageType, "message Type is not present. Aborting"); - $.assert(messageType in {{$.EventType.([.TRACK,.IDENTIFY,.PAGE])}}, "message type " + messageType + " is not supported"); + $.assert(messageType in {{$.EventType.([.TRACK,.IDENTIFY])}}, "message type " + messageType + " is not supported"); $.assertConfig(.destination.Config.organisationId, "Organisation ID is not present. Aborting"); $.assertConfig(.destination.Config.environment, "Environment is not present. Aborting"); - name: preparePayload diff --git a/src/cdk/v2/destinations/ninetailed/utils.js b/src/cdk/v2/destinations/ninetailed/utils.js index b716422a0e..47b27b3b9d 100644 --- a/src/cdk/v2/destinations/ninetailed/utils.js +++ b/src/cdk/v2/destinations/ninetailed/utils.js @@ -31,12 +31,6 @@ const constructFullPayload = (message) => { config.mappingConfig[config.ConfigCategories.IDENTIFY.name], ); break; - case 'page': - typeSpecifcPayload = constructPayload( - message, - config.mappingConfig[config.ConfigCategories.PAGE.name], - ); - break; default: break; } diff --git a/test/integrations/destinations/ninetailed/processor/data.ts b/test/integrations/destinations/ninetailed/processor/data.ts index 4e5fa72365..9d3cd217cd 100644 --- a/test/integrations/destinations/ninetailed/processor/data.ts +++ b/test/integrations/destinations/ninetailed/processor/data.ts @@ -1,5 +1,4 @@ import { validationFailures } from './validation'; import { track } from './track'; -import { page } from './page'; import { identify } from './identify'; -export const data = [...identify, ...page, ...track, ...validationFailures]; +export const data = [...identify, ...track, ...validationFailures]; diff --git a/test/integrations/destinations/ninetailed/processor/page.ts b/test/integrations/destinations/ninetailed/processor/page.ts deleted file mode 100644 index 93a086ceea..0000000000 --- a/test/integrations/destinations/ninetailed/processor/page.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { destination, context, commonProperties, metadata } from '../commonConfig'; -import { transformResultBuilder } from '../../../testUtils'; -export const page = [ - { - id: 'ninetailed-test-page-success-1', - name: 'ninetailed', - description: 'page call with all mappings available', - scenario: 'Framework+Buisness', - successCriteria: 'Response should contain all the mappings and status code should be 200', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination, - message: { - context, - type: 'page', - event: 'product purchased', - userId: 'sajal12', - channel: 'mobile', - messageId: 'dummy_msg_id', - properties: commonProperties, - anonymousId: 'anon_123', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-25T15:32:56.409Z', - }, - metadata, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - destinationId: 'dummyDestId', - }, - output: transformResultBuilder({ - method: 'POST', - endpoint: - 'https://experience.ninetailed.co/v2/organizations/dummyOrganisationId/environments/main/events', - JSON: { - events: [ - { - context: { - app: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - campaign: { - name: 'campign_123', - source: 'social marketing', - medium: 'facebook', - term: '1 year', - }, - library: { - name: 'RudderstackSDK', - version: 'Ruddderstack SDK version', - }, - locale: 'en-US', - page: { - path: '/signup', - referrer: 'https://rudderstack.medium.com/', - search: '?type=freetrial', - url: 'https://app.rudderstack.com/signup?type=freetrial', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - location: { - coordinates: { - latitude: 40.7128, - longitude: -74.006, - }, - city: 'San Francisco', - postalCode: '94107', - region: 'CA', - regionCode: 'CA', - country: ' United States', - countryCode: 'United States of America', - continent: 'North America', - timezone: 'America/Los_Angeles', - }, - }, - type: 'page', - channel: 'mobile', - messageId: 'dummy_msg_id', - properties: commonProperties, - anonymousId: 'anon_123', - originalTimestamp: '2021-01-25T15:32:56.409Z', - }, - ], - }, - userId: '', - }), - statusCode: 200, - }, - ], - }, - }, - }, -]; diff --git a/test/integrations/destinations/ninetailed/router/data.ts b/test/integrations/destinations/ninetailed/router/data.ts index 05105f4aed..1bf664d1c4 100644 --- a/test/integrations/destinations/ninetailed/router/data.ts +++ b/test/integrations/destinations/ninetailed/router/data.ts @@ -31,15 +31,6 @@ export const data = [ metadata: { jobId: 1, userId: 'u1' }, destination, }, - { - message: { - ...commonInput, - type: 'page', - properties: pageProperties, - }, - metadata: { jobId: 2, userId: 'u1' }, - destination, - }, { message: { type: 'identify', @@ -80,11 +71,6 @@ export const data = [ event: 'product list viewed', properties: trackProperties, }, - { - ...commonOutput, - type: 'page', - properties: pageProperties, - }, { type: 'identify', ...commonOutput, @@ -103,7 +89,6 @@ export const data = [ }, metadata: [ { jobId: 1, userId: 'u1' }, - { jobId: 2, userId: 'u1' }, { jobId: 3, userId: 'u1' }, ], batched: true, @@ -142,21 +127,9 @@ export const data = [ { message: { ...commonInput, - type: 'page', - properties: { - title: 'Sample Page', - url: 'https://example.com/?utm_campaign=example_campaign&utm_content=example_content', - path: '/', - hash: '', - search: '?utm_campaign=example_campaign&utm_content=example_content', - width: '1920', - height: '1080', - query: { - utm_campaign: 'example_campaign', - utm_content: 'example_content', - }, - referrer: '', - }, + type: 'track', + event: 'product added', + properties: trackProperties, }, metadata: { jobId: 2, userId: 'u1' }, destination, @@ -210,8 +183,9 @@ export const data = [ }, { ...commonOutput, - type: 'page', - properties: pageProperties, + type: 'track', + event: 'product added', + properties: trackProperties, }, ], }, @@ -264,8 +238,9 @@ export const data = [ { message: { ...commonInput, - type: 'page', - properties: pageProperties, + type: 'track', + event: 'product added', + properties: trackProperties, }, metadata: { jobId: 2, userId: 'u1' }, destination, @@ -330,8 +305,9 @@ export const data = [ }, { ...commonOutput, - type: 'page', - properties: pageProperties, + type: 'track', + event: 'product added', + properties: trackProperties, }, ], }, From 667095fa8316cd95a066f15b848ad503c6b4af80 Mon Sep 17 00:00:00 2001 From: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Date: Mon, 1 Apr 2024 14:48:50 +0530 Subject: [PATCH 065/240] feat: update movable ink batch size (#3223) * feat: update movable ink batch size * test: add testcase for batching on max request size --- src/cdk/v2/destinations/movable_ink/config.js | 3 +- .../movable_ink/procWorkflow.yaml | 1 + .../destinations/movable_ink/rtWorkflow.yaml | 2 +- .../destinations/movable_ink/common.ts | 180 ++++++++++++++++ .../destinations/movable_ink/mocks.ts | 6 + .../movable_ink/processor/identify.ts | 2 +- .../movable_ink/processor/track.ts | 6 + .../movable_ink/processor/validation.ts | 44 ++++ .../destinations/movable_ink/router/data.ts | 199 +++++++++++++++++- 9 files changed, 437 insertions(+), 6 deletions(-) create mode 100644 test/integrations/destinations/movable_ink/mocks.ts diff --git a/src/cdk/v2/destinations/movable_ink/config.js b/src/cdk/v2/destinations/movable_ink/config.js index 673e94620e..9a0200ab44 100644 --- a/src/cdk/v2/destinations/movable_ink/config.js +++ b/src/cdk/v2/destinations/movable_ink/config.js @@ -1,3 +1,4 @@ module.exports = { - MAX_REQUEST_SIZE_IN_BYTES: 13500, + MAX_REQUEST_SIZE_IN_BYTES: 1000000, + MAX_BATCH_SIZE: 1000, }; diff --git a/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml b/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml index 43dbb3cbce..394190049b 100644 --- a/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml +++ b/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml @@ -24,6 +24,7 @@ steps: $.assertConfig(.destination.Config.accessKey, "Access key is not present . Aborting"); $.assertConfig(.destination.Config.accessSecret, "Access Secret is not present. Aborting"); $.assert(.message.timestamp ?? .message.originalTimestamp, "Timestamp is not present. Aborting"); + $.assert(!(messageType === {{$.EventType.TRACK}} && !(.message.event)), "Event name is not present. Aborting"); const userId = .message.().( {{{{$.getGenericPaths("userIdOnly")}}}}; diff --git a/src/cdk/v2/destinations/movable_ink/rtWorkflow.yaml b/src/cdk/v2/destinations/movable_ink/rtWorkflow.yaml index 46afb34d53..3ffa49f15b 100644 --- a/src/cdk/v2/destinations/movable_ink/rtWorkflow.yaml +++ b/src/cdk/v2/destinations/movable_ink/rtWorkflow.yaml @@ -42,7 +42,7 @@ steps: description: Batches the successfulEvents template: | let batches = $.BatchUtils.chunkArrayBySizeAndLength( - $.outputs.successfulEvents, {maxSizeInBytes: $.MAX_REQUEST_SIZE_IN_BYTES}).items; + $.outputs.successfulEvents, {maxSizeInBytes: $.MAX_REQUEST_SIZE_IN_BYTES, maxItems: $.MAX_BATCH_SIZE}).items; batches@batch.({ "batchedRequest": { diff --git a/test/integrations/destinations/movable_ink/common.ts b/test/integrations/destinations/movable_ink/common.ts index f7eaa7af39..29fe76852c 100644 --- a/test/integrations/destinations/movable_ink/common.ts +++ b/test/integrations/destinations/movable_ink/common.ts @@ -110,6 +110,186 @@ const trackTestProperties = { position: 2, category: 'Games', }, + { + product_id: '122c6f5d5cf86a4c77358033', + sku: '7472-998-0112', + name: 'Ticket to Ride', + price: 20, + position: 3, + category: 'Games', + }, + { + product_id: '222c6f5d5cf86a4c77358033', + sku: '9472-998-0112', + name: 'Catan', + price: 30, + position: 4, + category: 'Games', + }, + { + product_id: '322c6f5d5cf86a4c77358033', + sku: '7472-998-0112', + name: 'Pandemic', + price: 25, + position: 5, + category: 'Games', + }, + { + product_id: '422c6f5d5cf86a4c77358033', + sku: '8472-998-0113', + name: 'Exploding Kittens', + price: 15, + position: 6, + category: 'Games', + }, + { + product_id: '522c6f5d5cf86a4c77358033', + sku: '8472-998-0114', + name: 'Codenames', + price: 18, + position: 7, + category: 'Games', + }, + { + product_id: '622c6f5d5cf86a4c77358034', + sku: '8472-998-0115', + name: 'Scythe', + price: 35, + position: 8, + category: 'Games', + }, + { + product_id: '622c6f5d5cf86a4c77358033', + sku: '8472-998-0112', + name: 'Cones of Dunshire', + price: 40, + position: 1, + category: 'Games', + url: 'https://www.website.com/product/path', + image_url: 'https://www.website.com/product/path.jpg', + }, + { + product_id: '577c6f5d5cf86a4c7735ba03', + sku: '3309-483-2201', + name: 'Five Crowns', + price: 5, + position: 2, + category: 'Games', + }, + { + product_id: '122c6f5d5cf86a4c77358033', + sku: '7472-998-0112', + name: 'Ticket to Ride', + price: 20, + position: 3, + category: 'Games', + }, + { + product_id: '222c6f5d5cf86a4c77358033', + sku: '9472-998-0112', + name: 'Catan', + price: 30, + position: 4, + category: 'Games', + }, + { + product_id: '322c6f5d5cf86a4c77358033', + sku: '7472-998-0112', + name: 'Pandemic', + price: 25, + position: 5, + category: 'Games', + }, + { + product_id: '422c6f5d5cf86a4c77358033', + sku: '8472-998-0113', + name: 'Exploding Kittens', + price: 15, + position: 6, + category: 'Games', + }, + { + product_id: '522c6f5d5cf86a4c77358033', + sku: '8472-998-0114', + name: 'Codenames', + price: 18, + position: 7, + category: 'Games', + }, + { + product_id: '622c6f5d5cf86a4c77358034', + sku: '8472-998-0115', + name: 'Scythe', + price: 35, + position: 8, + category: 'Games', + }, + { + product_id: '622c6f5d5cf86a4c77358033', + sku: '8472-998-0112', + name: 'Cones of Dunshire', + price: 40, + position: 1, + category: 'Games', + url: 'https://www.website.com/product/path', + image_url: 'https://www.website.com/product/path.jpg', + }, + { + product_id: '577c6f5d5cf86a4c7735ba03', + sku: '3309-483-2201', + name: 'Five Crowns', + price: 5, + position: 2, + category: 'Games', + }, + { + product_id: '122c6f5d5cf86a4c77358033', + sku: '7472-998-0112', + name: 'Ticket to Ride', + price: 20, + position: 3, + category: 'Games', + }, + { + product_id: '222c6f5d5cf86a4c77358033', + sku: '9472-998-0112', + name: 'Catan', + price: 30, + position: 4, + category: 'Games', + }, + { + product_id: '322c6f5d5cf86a4c77358033', + sku: '7472-998-0112', + name: 'Pandemic', + price: 25, + position: 5, + category: 'Games', + }, + { + product_id: '422c6f5d5cf86a4c77358033', + sku: '8472-998-0113', + name: 'Exploding Kittens', + price: 15, + position: 6, + category: 'Games', + }, + { + product_id: '522c6f5d5cf86a4c77358033', + sku: '8472-998-0114', + name: 'Codenames', + price: 18, + position: 7, + category: 'Games', + }, + { + product_id: '622c6f5d5cf86a4c77358034', + sku: '8472-998-0115', + name: 'Scythe', + price: 35, + position: 8, + category: 'Games', + }, ], }, 'Products Searched': { query: 'HDMI cable', url: 'https://www.website.com/product/path' }, diff --git a/test/integrations/destinations/movable_ink/mocks.ts b/test/integrations/destinations/movable_ink/mocks.ts new file mode 100644 index 0000000000..2468f51315 --- /dev/null +++ b/test/integrations/destinations/movable_ink/mocks.ts @@ -0,0 +1,6 @@ +import config from '../../../../src/cdk/v2/destinations/movable_ink/config'; + +export const defaultMockFns = () => { + jest.replaceProperty(config, 'MAX_REQUEST_SIZE_IN_BYTES', 5000); + jest.replaceProperty(config, 'MAX_BATCH_SIZE', 2); +}; diff --git a/test/integrations/destinations/movable_ink/processor/identify.ts b/test/integrations/destinations/movable_ink/processor/identify.ts index 27186da05c..e5bbf5a9a7 100644 --- a/test/integrations/destinations/movable_ink/processor/identify.ts +++ b/test/integrations/destinations/movable_ink/processor/identify.ts @@ -1,6 +1,6 @@ import { ProcessorTestData } from '../../../testTypes'; import { generateMetadata, transformResultBuilder } from '../../../testUtils'; -import { destType, channel, destination, traits, headers } from '../common'; +import { destType, destination, traits, headers } from '../common'; export const identify: ProcessorTestData[] = [ { diff --git a/test/integrations/destinations/movable_ink/processor/track.ts b/test/integrations/destinations/movable_ink/processor/track.ts index 5f30a3de83..890de11a0c 100644 --- a/test/integrations/destinations/movable_ink/processor/track.ts +++ b/test/integrations/destinations/movable_ink/processor/track.ts @@ -23,6 +23,7 @@ export const track: ProcessorTestData[] = [ channel, anonymousId: 'anonId123', userId: 'userId123', + event: 'Product Added', properties: trackTestProperties['Product Added'], integrations: { All: true, @@ -49,6 +50,7 @@ export const track: ProcessorTestData[] = [ channel, userId: 'userId123', anonymousId: 'anonId123', + event: 'Product Added', properties: trackTestProperties['Product Added'], integrations: { All: true, @@ -84,6 +86,7 @@ export const track: ProcessorTestData[] = [ channel, anonymousId: 'anonId123', userId: 'userId123', + event: 'Order Completed', properties: trackTestProperties['Order Completed'], integrations: { All: true, @@ -110,6 +113,7 @@ export const track: ProcessorTestData[] = [ channel, userId: 'userId123', anonymousId: 'anonId123', + event: 'Order Completed', properties: trackTestProperties['Order Completed'], integrations: { All: true, @@ -145,6 +149,7 @@ export const track: ProcessorTestData[] = [ channel, anonymousId: 'anonId123', userId: 'userId123', + event: 'Custom Event', properties: trackTestProperties['Custom Event'], integrations: { All: true, @@ -171,6 +176,7 @@ export const track: ProcessorTestData[] = [ channel, userId: 'userId123', anonymousId: 'anonId123', + event: 'Custom Event', properties: trackTestProperties['Custom Event'], integrations: { All: true, diff --git a/test/integrations/destinations/movable_ink/processor/validation.ts b/test/integrations/destinations/movable_ink/processor/validation.ts index ab6b123eb7..6aafb5e2c0 100644 --- a/test/integrations/destinations/movable_ink/processor/validation.ts +++ b/test/integrations/destinations/movable_ink/processor/validation.ts @@ -214,4 +214,48 @@ export const validation: ProcessorTestData[] = [ }, }, }, + { + id: 'MovableInk-validation-test-6', + name: destType, + description: 'Missing event name', + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + anonymousId: 'anonId123', + userId: 'userId123', + properties: {}, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Event name is not present. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Event name is not present. Aborting', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/movable_ink/router/data.ts b/test/integrations/destinations/movable_ink/router/data.ts index 72df3d7074..afadfec56e 100644 --- a/test/integrations/destinations/movable_ink/router/data.ts +++ b/test/integrations/destinations/movable_ink/router/data.ts @@ -1,6 +1,7 @@ import { RouterTestData } from '../../../testTypes'; import { RouterTransformationRequest } from '../../../../../src/types'; import { generateMetadata } from '../../../testUtils'; +import { defaultMockFns } from '../mocks'; import { destType, channel, @@ -43,6 +44,7 @@ const routerRequest: RouterTransformationRequest = { channel, anonymousId: 'anonId123', userId: 'userId123', + event: 'Product Added', properties: trackTestProperties['Product Added'], integrations: { All: true, @@ -58,19 +60,73 @@ const routerRequest: RouterTransformationRequest = { channel, anonymousId: 'anonId123', userId: 'userId123', - properties: trackTestProperties['Custom Event'], + event: 'Custom Event', integrations: { All: true, }, + originalTimestamp: '2024-03-04T15:32:56.409Z', }, metadata: generateMetadata(4), destination, }, + { + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Custom Event', + properties: trackTestProperties['Custom Event'], + integrations: { + All: true, + }, + }, + metadata: generateMetadata(5), + destination, + }, + ], + destType, +}; + +// >5KB payload +const routerRequest2: RouterTransformationRequest = { + input: [ + { + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Order Completed', + properties: trackTestProperties['Order Completed'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + destination, + }, + { + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Custom Event', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(2), + destination, + }, ], destType, }; -export const data: RouterTestData[] = [ +export const data = [ { id: 'MovableInk-router-test-1', name: destType, @@ -118,6 +174,7 @@ export const data: RouterTestData[] = [ channel, userId: 'userId123', anonymousId: 'anonId123', + event: 'Product Added', properties: trackTestProperties['Product Added'], integrations: { All: true, @@ -138,6 +195,42 @@ export const data: RouterTestData[] = [ statusCode: 200, destination, }, + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: destination.Config.endpoint, + headers, + params: {}, + body: { + JSON: { + events: [ + { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + event: 'Custom Event', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [generateMetadata(4)], + batched: true, + statusCode: 200, + destination, + }, { metadata: [generateMetadata(2)], batched: false, @@ -147,7 +240,7 @@ export const data: RouterTestData[] = [ destination, }, { - metadata: [generateMetadata(4)], + metadata: [generateMetadata(5)], batched: false, statusCode: 400, error: 'Timestamp is not present. Aborting', @@ -158,5 +251,105 @@ export const data: RouterTestData[] = [ }, }, }, + mockFns: defaultMockFns, + }, + { + id: 'MovableInk-router-test-2', + name: destType, + description: 'Basic Router Test to test Max Request Size', + scenario: 'Framework', + successCriteria: + 'Some events should be transformed successfully and some should fail for missing fields and status code should be 200', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: routerRequest2, + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: destination.Config.endpoint, + headers, + params: {}, + body: { + JSON: { + events: [ + { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + event: 'Order Completed', + properties: trackTestProperties['Order Completed'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [generateMetadata(1)], + batched: true, + statusCode: 200, + destination, + }, + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: destination.Config.endpoint, + headers, + params: {}, + body: { + JSON: { + events: [ + { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + event: 'Custom Event', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [generateMetadata(2)], + batched: true, + statusCode: 200, + destination, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, }, ]; From d9b7e1f70565d59979aee3e62f60e39edb9a23c7 Mon Sep 17 00:00:00 2001 From: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Date: Mon, 1 Apr 2024 15:10:10 +0530 Subject: [PATCH 066/240] feat: onboard new destination bloomreach (#3185) * feat: onboard new destination bloomreach * refactor: move code to typescript * feat: add proxy and partial handling support * feat: onboard on router * chore: add proxy testcases * docs: add ref --- src/cdk/v2/destinations/bloomreach/config.ts | 30 +++ .../BloomreachCustomerPropertiesConfig.json | 36 +++ .../destinations/bloomreach/procWorkflow.yaml | 119 ++++++++++ .../destinations/bloomreach/rtWorkflow.yaml | 76 ++++++ src/cdk/v2/destinations/bloomreach/utils.ts | 31 +++ src/constants/destinationCanonicalNames.js | 1 + src/features.json | 5 +- .../destinations/bloomreach/networkHandler.js | 83 +++++++ .../destinations/bloomreach/common.ts | 99 ++++++++ .../bloomreach/dataDelivery/business.ts | 195 ++++++++++++++++ .../bloomreach/dataDelivery/data.ts | 3 + .../bloomreach/dataDelivery/other.ts | 212 +++++++++++++++++ .../destinations/bloomreach/mocks.ts | 5 + .../destinations/bloomreach/network.ts | 124 ++++++++++ .../destinations/bloomreach/processor/data.ts | 5 + .../bloomreach/processor/identify.ts | 156 +++++++++++++ .../destinations/bloomreach/processor/page.ts | 72 ++++++ .../bloomreach/processor/track.ts | 173 ++++++++++++++ .../bloomreach/processor/validation.ts | 131 +++++++++++ .../destinations/bloomreach/router/data.ts | 220 ++++++++++++++++++ 20 files changed, 1774 insertions(+), 2 deletions(-) create mode 100644 src/cdk/v2/destinations/bloomreach/config.ts create mode 100644 src/cdk/v2/destinations/bloomreach/data/BloomreachCustomerPropertiesConfig.json create mode 100644 src/cdk/v2/destinations/bloomreach/procWorkflow.yaml create mode 100644 src/cdk/v2/destinations/bloomreach/rtWorkflow.yaml create mode 100644 src/cdk/v2/destinations/bloomreach/utils.ts create mode 100644 src/v1/destinations/bloomreach/networkHandler.js create mode 100644 test/integrations/destinations/bloomreach/common.ts create mode 100644 test/integrations/destinations/bloomreach/dataDelivery/business.ts create mode 100644 test/integrations/destinations/bloomreach/dataDelivery/data.ts create mode 100644 test/integrations/destinations/bloomreach/dataDelivery/other.ts create mode 100644 test/integrations/destinations/bloomreach/mocks.ts create mode 100644 test/integrations/destinations/bloomreach/network.ts create mode 100644 test/integrations/destinations/bloomreach/processor/data.ts create mode 100644 test/integrations/destinations/bloomreach/processor/identify.ts create mode 100644 test/integrations/destinations/bloomreach/processor/page.ts create mode 100644 test/integrations/destinations/bloomreach/processor/track.ts create mode 100644 test/integrations/destinations/bloomreach/processor/validation.ts create mode 100644 test/integrations/destinations/bloomreach/router/data.ts diff --git a/src/cdk/v2/destinations/bloomreach/config.ts b/src/cdk/v2/destinations/bloomreach/config.ts new file mode 100644 index 0000000000..90fbcc63c6 --- /dev/null +++ b/src/cdk/v2/destinations/bloomreach/config.ts @@ -0,0 +1,30 @@ +import { getMappingConfig } from '../../../../v0/util'; + +export const CUSTOMER_COMMAND = 'customers'; +export const CUSTOMER_EVENT_COMMAND = 'customers/events'; +export const MAX_BATCH_SIZE = 50; + +// ref:- https://documentation.bloomreach.com/engagement/reference/batch-commands-2 +export const getBatchEndpoint = (apiBaseUrl: string, projectToken: string): string => + `${apiBaseUrl}/track/v2/projects/${projectToken}/batch`; + +const CONFIG_CATEGORIES = { + CUSTOMER_PROPERTIES_CONFIG: { name: 'BloomreachCustomerPropertiesConfig' }, +}; +const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); +export const EXCLUSION_FIELDS: string[] = [ + 'email', + 'firstName', + 'firstname', + 'first_name', + 'lastName', + 'lastname', + 'last_name', + 'name', + 'phone', + 'city', + 'birthday', + 'country', +]; +export const CUSTOMER_PROPERTIES_CONFIG = + MAPPING_CONFIG[CONFIG_CATEGORIES.CUSTOMER_PROPERTIES_CONFIG.name]; diff --git a/src/cdk/v2/destinations/bloomreach/data/BloomreachCustomerPropertiesConfig.json b/src/cdk/v2/destinations/bloomreach/data/BloomreachCustomerPropertiesConfig.json new file mode 100644 index 0000000000..cb4c2f7201 --- /dev/null +++ b/src/cdk/v2/destinations/bloomreach/data/BloomreachCustomerPropertiesConfig.json @@ -0,0 +1,36 @@ +[ + { + "destKey": "first_name", + "sourceKeys": "firstName", + "sourceFromGenericMap": true + }, + { + "destKey": "last_name", + "sourceKeys": "lastName", + "sourceFromGenericMap": true + }, + { + "destKey": "email", + "sourceKeys": "emailOnly", + "sourceFromGenericMap": true + }, + { + "destKey": "phone", + "sourceKeys": "phone", + "sourceFromGenericMap": true + }, + { + "destKey": "city", + "sourceKeys": "city", + "sourceFromGenericMap": true + }, + { + "destKey": "country", + "sourceKeys": ["traits.address.country", "context.traits.address.country"] + }, + { + "destKey": "birthday", + "sourceKeys": "birthday", + "sourceFromGenericMap": true + } +] diff --git a/src/cdk/v2/destinations/bloomreach/procWorkflow.yaml b/src/cdk/v2/destinations/bloomreach/procWorkflow.yaml new file mode 100644 index 0000000000..f092d90382 --- /dev/null +++ b/src/cdk/v2/destinations/bloomreach/procWorkflow.yaml @@ -0,0 +1,119 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + - name: defaultRequestConfig + path: ../../../../v0/util + - name: toUnixTimestamp + path: ../../../../v0/util + - name: base64Convertor + path: ../../../../v0/util + - name: removeUndefinedAndNullValues + path: ../../../../v0/util + - name: generateExclusionList + path: ../../../../v0/util + - name: extractCustomFields + path: ../../../../v0/util + - name: constructPayload + path: ../../../../v0/util + - path: ./utils + - path: ./config + +steps: + - name: messageType + template: | + $.context.messageType = .message.type.toLowerCase(); + + - name: validateInput + template: | + let messageType = $.context.messageType; + $.assert(messageType, "message Type is not present. Aborting"); + $.assert(messageType in {{$.EventType.([.IDENTIFY,.TRACK,.PAGE,.SCREEN])}}, "message type " + messageType + " is not supported"); + $.assertConfig(.destination.Config.apiBaseUrl, "API Base URL is not present. Aborting"); + $.assertConfig(.destination.Config.apiKey, "API Key is not present . Aborting"); + $.assertConfig(.destination.Config.apiSecret, "API Secret is not present. Aborting"); + $.assertConfig(.destination.Config.projectToken, "Project Token is not present. Aborting"); + $.assertConfig(.destination.Config.hardID, "Hard ID is not present. Aborting"); + $.assertConfig(.destination.Config.softID, "Soft ID is not present. Aborting"); + $.assert(.message.timestamp ?? .message.originalTimestamp, "Timestamp is not present. Aborting"); + const userId = .message.().( + {{{{$.getGenericPaths("userIdOnly")}}}}; + ); + $.assert(userId ?? .message.anonymousId, "Either one of userId or anonymousId is required. Aborting"); + + - name: prepareIdentifyPayload + condition: $.context.messageType === {{$.EventType.IDENTIFY}} + template: | + const customerIDs = $.prepareCustomerIDs(.message, .destination); + const customerProperties = $.constructPayload(.message, $.CUSTOMER_PROPERTIES_CONFIG); + const extraCustomerProperties = $.extractCustomFields(.message, {}, ['traits', 'context.traits'], $.EXCLUSION_FIELDS); + const properties = { + ...customerProperties, + ...extraCustomerProperties + } + const data = .message.().({ + "customer_ids": customerIDs, + "update_timestamp": $.toUnixTimestamp({{{{$.getGenericPaths("timestamp")}}}}), + properties + }); + + $.context.payload = $.removeUndefinedAndNullValues({name: $.CUSTOMER_COMMAND, data}) + + - name: prepareEventName + steps: + - name: pageEventName + condition: $.context.messageType === {{$.EventType.PAGE}} + template: | + const category = .message.category ?? .message.properties.category; + const name = .message.name || .message.properties.name; + const eventNameArray = ["Viewed"]; + category ? eventNameArray.push(category); + name ? eventNameArray.push(name); + eventNameArray.push("Page"); + $.context.event = eventNameArray.join(" "); + - name: screenEventName + condition: $.context.messageType === {{$.EventType.SCREEN}} + template: | + const category = .message.category ?? .message.properties.category; + const name = .message.name || .message.properties.name; + const eventNameArray = ["Viewed"]; + category ? eventNameArray.push(category); + name ? eventNameArray.push(name); + eventNameArray.push("Screen"); + $.context.event = eventNameArray.join(" "); + - name: trackEventName + condition: $.context.messageType === {{$.EventType.TRACK}} + template: | + $.assert(.message.event, "Event name is required. Aborting"); + $.context.event = .message.event + + - name: prepareTrackPageScreenPayload + condition: $.context.messageType !== {{$.EventType.IDENTIFY}} + template: | + const customerIDs = $.prepareCustomerIDs(.message, .destination); + const data = .message.().({ + "customer_ids": customerIDs, + "timestamp": $.toUnixTimestamp({{{{$.getGenericPaths("timestamp")}}}}), + "properties": .properties, + "event_type": $.context.event, + }); + + $.context.payload = $.removeUndefinedAndNullValues({name: $.CUSTOMER_EVENT_COMMAND, data}) + + - name: buildResponse + description: In batchMode we return payload directly + condition: $.batchMode + template: | + $.context.payload + else: + name: buildResponseForProcessTransformation + template: | + const response = $.defaultRequestConfig(); + response.body.JSON = $.context.payload; + response.endpoint = $.getBatchEndpoint(.destination.Config.apiBaseUrl, .destination.Config.projectToken); + response.method = "POST"; + response.headers = { + "Content-Type": "application/json", + "Authorization": "Basic " + $.base64Convertor(.destination.Config.apiKey + ":" + .destination.Config.apiSecret) + } + response; diff --git a/src/cdk/v2/destinations/bloomreach/rtWorkflow.yaml b/src/cdk/v2/destinations/bloomreach/rtWorkflow.yaml new file mode 100644 index 0000000000..b8b27ca02e --- /dev/null +++ b/src/cdk/v2/destinations/bloomreach/rtWorkflow.yaml @@ -0,0 +1,76 @@ +bindings: + - name: handleRtTfSingleEventError + path: ../../../../v0/util/index + - path: ./utils + exportAll: true + - name: base64Convertor + path: ../../../../v0/util + - name: toUnixTimestamp + path: ../../../../v0/util + - name: BatchUtils + path: '@rudderstack/workflow-engine' + - path: ./config + +steps: + - name: validateInput + template: | + $.assert(Array.isArray(^) && ^.length > 0, "Invalid event array") + + - name: transform + externalWorkflow: + path: ./procWorkflow.yaml + bindings: + - name: batchMode + value: true + loopOverInput: true + + - name: successfulEvents + template: | + $.outputs.transform#idx.output.({ + "batchedRequest": ., + "batched": false, + "destination": ^[idx].destination, + "metadata": ^[idx].metadata, + "statusCode": 200 + })[] + + - name: failedEvents + template: | + $.outputs.transform#idx.error.( + $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) + )[] + + - name: batchSuccessfulEvents + description: Batches the successfulEvents + template: | + let batches = $.BatchUtils.chunkArrayBySizeAndLength( + $.outputs.successfulEvents, {maxItems: $.MAX_BATCH_SIZE}).items; + + batches@batch.({ + "batchedRequest": { + "body": { + "JSON": {"commands": ~r batch.batchedRequest[]}, + "JSON_ARRAY": {}, + "XML": {}, + "FORM": {} + }, + "version": "1", + "type": "REST", + "method": "POST", + "endpoint": batch[0].destination.Config.().($.getBatchEndpoint(.apiBaseUrl, .projectToken)), + "headers": batch[0].destination.Config.().({ + "Content-Type": "application/json", + "Authorization": "Basic " + $.base64Convertor(.apiKey + ":" + .apiSecret) + }), + "params": {}, + "files": {} + }, + "metadata": ~r batch.metadata[], + "batched": true, + "statusCode": 200, + "destination": batch[0].destination + })[]; + + - name: finalPayload + template: | + [...$.outputs.batchSuccessfulEvents, ...$.outputs.failedEvents] diff --git a/src/cdk/v2/destinations/bloomreach/utils.ts b/src/cdk/v2/destinations/bloomreach/utils.ts new file mode 100644 index 0000000000..f834fa74e7 --- /dev/null +++ b/src/cdk/v2/destinations/bloomreach/utils.ts @@ -0,0 +1,31 @@ +import { isObject, isEmptyObject, getIntegrationsObj } from '../../../../v0/util'; +import { RudderMessage, Destination } from '../../../../types'; + +const getCustomerIDsFromIntegrationObject = (message: RudderMessage): any => { + const integrationObj = getIntegrationsObj(message, 'bloomreach' as any) || {}; + const { hardID, softID } = integrationObj; + const customerIDs = {}; + + if (isObject(hardID) && !isEmptyObject(hardID)) { + Object.keys(hardID).forEach((id) => { + customerIDs[id] = hardID[id]; + }); + } + + if (isObject(softID) && !isEmptyObject(softID)) { + Object.keys(softID).forEach((id) => { + customerIDs[id] = softID[id]; + }); + } + + return customerIDs; +}; + +export const prepareCustomerIDs = (message: RudderMessage, destination: Destination): any => { + const customerIDs = { + [destination.Config.hardID]: message.userId, + [destination.Config.softID]: message.anonymousId, + ...getCustomerIDsFromIntegrationObject(message), + }; + return customerIDs; +}; diff --git a/src/constants/destinationCanonicalNames.js b/src/constants/destinationCanonicalNames.js index b84aff1089..ee4f4f0b33 100644 --- a/src/constants/destinationCanonicalNames.js +++ b/src/constants/destinationCanonicalNames.js @@ -165,6 +165,7 @@ const DestCanonicalNames = { 'google adwords offline conversions', ], koala: ['Koala', 'koala', 'KOALA'], + bloomreach: ['Bloomreach', 'bloomreach', 'BLOOMREACH'], }; module.exports = { DestHandlerMap, DestCanonicalNames }; diff --git a/src/features.json b/src/features.json index 267923fdb4..fd06b3b241 100644 --- a/src/features.json +++ b/src/features.json @@ -67,8 +67,9 @@ "THE_TRADE_DESK": true, "INTERCOM": true, "NINETAILED": true, - "MOVABLE_INK": true, - "KOALA": true + "KOALA": true, + "BLOOMREACH": true, + "MOVABLE_INK": true }, "regulations": [ "BRAZE", diff --git a/src/v1/destinations/bloomreach/networkHandler.js b/src/v1/destinations/bloomreach/networkHandler.js new file mode 100644 index 0000000000..a3c17a167b --- /dev/null +++ b/src/v1/destinations/bloomreach/networkHandler.js @@ -0,0 +1,83 @@ +const { TransformerProxyError } = require('../../../v0/util/errorTypes'); +const { proxyRequest, prepareProxyRequest } = require('../../../adapters/network'); +const { + processAxiosResponse, + getDynamicErrorType, +} = require('../../../adapters/utils/networkUtils'); +const { isHttpStatusSuccess } = require('../../../v0/util/index'); +const tags = require('../../../v0/util/tags'); + +// { +// "results": [ +// { +// "success": true +// }, +// { +// "success": false, +// "errors": [ +// "At least one id should be specified." +// ] +// } +// ], +// "start_time": 1710750816.8504393, +// "end_time": 1710750816.8518236, +// "success": true +// } +const checkIfEventIsAbortableAndExtractErrorMessage = (element) => { + if (element.success) { + return { isAbortable: false, errorMsg: '' }; + } + + const errorMsg = element.errors.join(', '); + return { isAbortable: true, errorMsg }; +}; + +const responseHandler = (responseParams) => { + const { destinationResponse, rudderJobMetadata } = responseParams; + + const message = '[BLOOMREACH Response V1 Handler] - Request Processed Successfully'; + const responseWithIndividualEvents = []; + const { response, status } = destinationResponse; + + if (isHttpStatusSuccess(status)) { + // check for Partial Event failures and Successes + const { results } = response; + results.forEach((event, idx) => { + const proxyOutput = { + statusCode: 200, + metadata: rudderJobMetadata[idx], + error: 'success', + }; + // update status of partial event if abortable + const { isAbortable, errorMsg } = checkIfEventIsAbortableAndExtractErrorMessage(event); + if (isAbortable) { + proxyOutput.statusCode = 400; + proxyOutput.error = errorMsg; + } + responseWithIndividualEvents.push(proxyOutput); + }); + return { + status, + message, + destinationResponse, + response: responseWithIndividualEvents, + }; + } + throw new TransformerProxyError( + `BLOOMREACH: Error encountered in transformer proxy V1`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); +}; +function networkHandler() { + this.proxy = proxyRequest; + this.processAxiosResponse = processAxiosResponse; + this.prepareProxy = prepareProxyRequest; + this.responseHandler = responseHandler; +} +module.exports = { networkHandler }; diff --git a/test/integrations/destinations/bloomreach/common.ts b/test/integrations/destinations/bloomreach/common.ts new file mode 100644 index 0000000000..798e744cbc --- /dev/null +++ b/test/integrations/destinations/bloomreach/common.ts @@ -0,0 +1,99 @@ +import { Destination } from '../../../../src/types'; + +const destType = 'bloomreach'; +const destTypeInUpperCase = 'BLOOMREACH'; +const displayName = 'bloomreach'; +const channel = 'web'; +const destination: Destination = { + Config: { + apiBaseUrl: 'https://demoapp-api.bloomreach.com', + apiKey: 'test-api-key', + apiSecret: 'test-api-secret', + projectToken: 'test-project-token', + hardID: 'registered', + softID: 'cookie', + }, + DestinationDefinition: { + DisplayName: displayName, + ID: '123', + Name: destTypeInUpperCase, + Config: { cdkV2Enabled: true }, + }, + Enabled: true, + ID: '123', + Name: destTypeInUpperCase, + Transformations: [], + WorkspaceID: 'test-workspace-id', +}; + +const traits = { + email: 'test@example.com', + firstName: 'John', + lastName: 'Doe', + phone: '1234567890', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, +}; + +const properties = { + product_id: '622c6f5d5cf86a4c77358033', + sku: '8472-998-0112', + category: 'Games', + name: 'Cones of Dunshire', + brand: 'Wyatt Games', + variant: 'expansion pack', + price: 49.99, + quantity: 5, + coupon: 'PREORDER15', + currency: 'USD', + position: 1, + url: 'https://www.website.com/product/path', + image_url: 'https://www.website.com/product/path.webp', + key1: 'value1', +}; +const endpoint = 'https://demoapp-api.bloomreach.com/track/v2/projects/test-project-token/batch'; + +const processorInstrumentationErrorStatTags = { + destType: destTypeInUpperCase, + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +const RouterInstrumentationErrorStatTags = { + ...processorInstrumentationErrorStatTags, + feature: 'router', +}; + +const proxyV1RetryableErrorStatTags = { + ...RouterInstrumentationErrorStatTags, + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', +}; + +const headers = { + 'Content-Type': 'application/json', + Authorization: 'Basic dGVzdC1hcGkta2V5OnRlc3QtYXBpLXNlY3JldA==', +}; + +export { + destType, + channel, + destination, + processorInstrumentationErrorStatTags, + RouterInstrumentationErrorStatTags, + traits, + headers, + properties, + endpoint, + proxyV1RetryableErrorStatTags, +}; diff --git a/test/integrations/destinations/bloomreach/dataDelivery/business.ts b/test/integrations/destinations/bloomreach/dataDelivery/business.ts new file mode 100644 index 0000000000..9e71b7a2fd --- /dev/null +++ b/test/integrations/destinations/bloomreach/dataDelivery/business.ts @@ -0,0 +1,195 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload, generateMetadata } from '../../../testUtils'; +import { destType, headers, properties, endpoint } from '../common'; + +const customerProperties = { + email: 'test@example.com', + first_name: 'John', + last_name: 'Doe', + phone: '1234567890', + city: 'New York', + country: 'USA', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, +}; + +const metadataArray = [generateMetadata(1), generateMetadata(2)]; + +// https://documentation.bloomreach.com/engagement/reference/tips-and-best-practices +export const businessProxyV1: ProxyV1TestData[] = [ + { + id: 'bloomreach_v1_business_scenario_1', + name: destType, + description: + '[Proxy v1 API] :: Test for a valid request - where the destination responds with 200 with error for request 2 in a batch', + successCriteria: 'Should return 200 with partial failures within the response payload', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers, + params: {}, + JSON: { + commands: [ + { + name: 'customers', + data: { + customer_ids: { + cookie: '97c46c81-3140-456d-b2a9-690d70aaca35', + }, + update_timestamp: 1709405952, + properties: customerProperties, + }, + }, + { + name: 'customers', + data: { + customer_ids: {}, + }, + }, + ], + }, + endpoint, + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[BLOOMREACH Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + results: [ + { + success: true, + }, + { + success: false, + errors: ['At least one id should be specified.'], + }, + ], + start_time: 1710771351.9885373, + end_time: 1710771351.9891083, + success: true, + }, + status: 200, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + { + statusCode: 400, + metadata: generateMetadata(2), + error: 'At least one id should be specified.', + }, + ], + }, + }, + }, + }, + }, + { + id: 'bloomreach_v1_business_scenario_2', + name: destType, + description: + '[Proxy v1 API] :: Test for a valid request - where the destination responds with 200 without any error', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers, + params: {}, + JSON: { + commands: [ + { + name: 'customers/events', + data: { + customer_ids: { + cookie: '97c46c81-3140-456d-b2a9-690d70aaca35', + }, + timestamp: 1709566376, + properties, + event_type: 'test_event', + }, + }, + { + name: 'customers', + data: { + customer_ids: { + cookie: '97c46c81-3140-456d-b2a9-690d70aaca35', + }, + update_timestamp: 1709405952, + properties: customerProperties, + }, + }, + ], + }, + endpoint, + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[BLOOMREACH Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + results: [ + { + success: true, + }, + { + success: true, + }, + ], + start_time: 1710771351.9885373, + end_time: 1710771351.9891083, + success: true, + }, + status: 200, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + { + statusCode: 200, + metadata: generateMetadata(2), + error: 'success', + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/bloomreach/dataDelivery/data.ts b/test/integrations/destinations/bloomreach/dataDelivery/data.ts new file mode 100644 index 0000000000..5099eafce7 --- /dev/null +++ b/test/integrations/destinations/bloomreach/dataDelivery/data.ts @@ -0,0 +1,3 @@ +import { businessProxyV1 } from './business'; +import { otherProxyV1 } from './other'; +export const data = [...businessProxyV1, ...otherProxyV1]; diff --git a/test/integrations/destinations/bloomreach/dataDelivery/other.ts b/test/integrations/destinations/bloomreach/dataDelivery/other.ts new file mode 100644 index 0000000000..f0dd9cc09a --- /dev/null +++ b/test/integrations/destinations/bloomreach/dataDelivery/other.ts @@ -0,0 +1,212 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload, generateMetadata } from '../../../testUtils'; +import { destType, proxyV1RetryableErrorStatTags } from '../common'; + +const metadataArray = [generateMetadata(1)]; + +// https://documentation.bloomreach.com/engagement/reference/tips-and-best-practices +export const otherProxyV1: ProxyV1TestData[] = [ + { + id: 'bloomreach_v1_other_scenario_1', + name: destType, + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_service_not_available', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 503, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'BLOOMREACH: Error encountered in transformer proxy V1', + status: 503, + }, + }, + }, + }, + }, + { + id: 'bloomreach_v1_other_scenario_2', + name: destType, + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_internal_server_error', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'BLOOMREACH: Error encountered in transformer proxy V1', + status: 500, + }, + }, + }, + }, + }, + { + id: 'bloomreach_v1_other_scenario_3', + name: destType, + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout"', + statusCode: 504, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'BLOOMREACH: Error encountered in transformer proxy V1', + status: 504, + }, + }, + }, + }, + }, + { + id: 'bloomreach_v1_other_scenario_4', + name: destType, + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_null_response', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'BLOOMREACH: Error encountered in transformer proxy V1', + status: 500, + }, + }, + }, + }, + }, + { + id: 'bloomreach_v1_other_scenario_5', + name: destType, + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'BLOOMREACH: Error encountered in transformer proxy V1', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/bloomreach/mocks.ts b/test/integrations/destinations/bloomreach/mocks.ts new file mode 100644 index 0000000000..ba3b22b52a --- /dev/null +++ b/test/integrations/destinations/bloomreach/mocks.ts @@ -0,0 +1,5 @@ +import * as config from '../../../../src/cdk/v2/destinations/bloomreach/config'; + +export const defaultMockFns = () => { + jest.replaceProperty(config, 'MAX_BATCH_SIZE', 3 as typeof config.MAX_BATCH_SIZE); +}; diff --git a/test/integrations/destinations/bloomreach/network.ts b/test/integrations/destinations/bloomreach/network.ts new file mode 100644 index 0000000000..b20ff881b8 --- /dev/null +++ b/test/integrations/destinations/bloomreach/network.ts @@ -0,0 +1,124 @@ +import { destType, headers, properties, endpoint } from './common'; + +export const networkCallsData = [ + { + httpReq: { + url: endpoint, + data: { + commands: [ + { + name: 'customers', + data: { + customer_ids: { + cookie: '97c46c81-3140-456d-b2a9-690d70aaca35', + }, + update_timestamp: 1709405952, + properties: { + email: 'test@example.com', + first_name: 'John', + last_name: 'Doe', + phone: '1234567890', + city: 'New York', + country: 'USA', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, + }, + }, + }, + { + name: 'customers', + data: { + customer_ids: {}, + }, + }, + ], + }, + params: { destination: destType }, + headers, + method: 'POST', + }, + httpRes: { + data: { + results: [ + { + success: true, + }, + { + success: false, + errors: ['At least one id should be specified.'], + }, + ], + start_time: 1710771351.9885373, + end_time: 1710771351.9891083, + success: true, + }, + status: 200, + statusText: 'Ok', + }, + }, + { + httpReq: { + url: endpoint, + data: { + commands: [ + { + name: 'customers/events', + data: { + customer_ids: { + cookie: '97c46c81-3140-456d-b2a9-690d70aaca35', + }, + timestamp: 1709566376, + properties, + event_type: 'test_event', + }, + }, + { + name: 'customers', + data: { + customer_ids: { + cookie: '97c46c81-3140-456d-b2a9-690d70aaca35', + }, + update_timestamp: 1709405952, + properties: { + email: 'test@example.com', + first_name: 'John', + last_name: 'Doe', + phone: '1234567890', + city: 'New York', + country: 'USA', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, + }, + }, + }, + ], + }, + params: { destination: destType }, + headers, + method: 'POST', + }, + httpRes: { + data: { + results: [ + { + success: true, + }, + { + success: true, + }, + ], + start_time: 1710771351.9885373, + end_time: 1710771351.9891083, + success: true, + }, + status: 200, + statusText: 'Ok', + }, + }, +]; diff --git a/test/integrations/destinations/bloomreach/processor/data.ts b/test/integrations/destinations/bloomreach/processor/data.ts new file mode 100644 index 0000000000..a3633ad0dd --- /dev/null +++ b/test/integrations/destinations/bloomreach/processor/data.ts @@ -0,0 +1,5 @@ +import { validation } from './validation'; +import { identify } from './identify'; +import { track } from './track'; +import { page } from './page'; +export const data = [...identify, ...track, ...page, ...validation]; diff --git a/test/integrations/destinations/bloomreach/processor/identify.ts b/test/integrations/destinations/bloomreach/processor/identify.ts new file mode 100644 index 0000000000..2a79cb57e3 --- /dev/null +++ b/test/integrations/destinations/bloomreach/processor/identify.ts @@ -0,0 +1,156 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, destination, traits, headers, endpoint } from '../common'; + +export const identify: ProcessorTestData[] = [ + { + id: 'bloomreach-identify-test-1', + name: destType, + description: 'Identify call to create/update customer properties', + scenario: 'Framework+Business', + successCriteria: 'Response should contain all the mapping and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + userId: 'userId123', + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { registered: 'userId123', cookie: 'anonId123' }, + properties: { + email: 'test@example.com', + first_name: 'John', + last_name: 'Doe', + phone: '1234567890', + city: 'New York', + country: 'USA', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, + }, + update_timestamp: 1709566376, + }, + name: 'customers', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'bloomreach-identify-test-2', + name: destType, + description: 'Identify call with multiple hard and soft identifiers using integration object', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain multiple hard and soft identifiers and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + userId: 'userId123', + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + bloomreach: { + hardID: { + hardID1: 'value1', + }, + softID: { + google_analytics: 'gaId123', + softID2: 'value2', + }, + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { + registered: 'userId123', + cookie: 'anonId123', + hardID1: 'value1', + google_analytics: 'gaId123', + softID2: 'value2', + }, + properties: { + email: 'test@example.com', + first_name: 'John', + last_name: 'Doe', + phone: '1234567890', + city: 'New York', + country: 'USA', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, + }, + update_timestamp: 1709566376, + }, + name: 'customers', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/bloomreach/processor/page.ts b/test/integrations/destinations/bloomreach/processor/page.ts new file mode 100644 index 0000000000..0c2d27989d --- /dev/null +++ b/test/integrations/destinations/bloomreach/processor/page.ts @@ -0,0 +1,72 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, destination, headers, endpoint } from '../common'; + +const properties = { + category: 'Docs', + path: '', + referrer: '', + search: '', + title: '', + url: '', +}; + +export const page: ProcessorTestData[] = [ + { + id: 'bloomreach-page-test-1', + name: destType, + description: 'Page call with category, name', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain event_name = "Viewed {{ category }} {{ name }} Page" and properties and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'page', + anonymousId: 'anonId123', + name: 'Integration', + properties, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { cookie: 'anonId123' }, + properties, + timestamp: 1709566376, + event_type: 'Viewed Docs Integration Page', + }, + name: 'customers/events', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/bloomreach/processor/track.ts b/test/integrations/destinations/bloomreach/processor/track.ts new file mode 100644 index 0000000000..a369f508b2 --- /dev/null +++ b/test/integrations/destinations/bloomreach/processor/track.ts @@ -0,0 +1,173 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, destination, headers, properties, endpoint } from '../common'; + +export const track: ProcessorTestData[] = [ + { + id: 'bloomreach-track-test-1', + name: destType, + description: 'Track call with anonymous user', + scenario: 'Framework+Business', + successCriteria: 'Response should contain all the mapping and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + anonymousId: 'anonId123', + event: 'Product Viewed', + properties, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { cookie: 'anonId123' }, + properties, + timestamp: 1709566376, + event_type: 'Product Viewed', + }, + name: 'customers/events', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'bloomreach-track-test-2', + name: destType, + description: 'Track call with known user', + scenario: 'Framework+Business', + successCriteria: 'Response should contain all the mapping and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + userId: 'userId123', + anonymousId: 'anonId123', + event: 'Product Added', + properties, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { registered: 'userId123', cookie: 'anonId123' }, + properties, + timestamp: 1709566376, + event_type: 'Product Added', + }, + name: 'customers/events', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'bloomreach-track-test-3', + name: destType, + description: 'Track call with no properties', + scenario: 'Framework+Business', + successCriteria: 'Response should contain all the mapping and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + anonymousId: 'anonId123', + event: 'test_event', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { cookie: 'anonId123' }, + timestamp: 1709566376, + event_type: 'test_event', + }, + name: 'customers/events', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/bloomreach/processor/validation.ts b/test/integrations/destinations/bloomreach/processor/validation.ts new file mode 100644 index 0000000000..ff959d74c6 --- /dev/null +++ b/test/integrations/destinations/bloomreach/processor/validation.ts @@ -0,0 +1,131 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata } from '../../../testUtils'; +import { destType, destination, processorInstrumentationErrorStatTags } from '../common'; + +export const validation: ProcessorTestData[] = [ + { + id: 'bloomreach-validation-test-1', + name: destType, + description: 'Missing userId and anonymousId', + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Either one of userId or anonymousId is required. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Either one of userId or anonymousId is required. Aborting', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'bloomreach-validation-test-2', + name: destType, + description: 'Unsupported message type -> group', + scenario: 'Framework', + successCriteria: 'Instrumentation Error for Unsupported message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'group', + userId: 'userId123', + channel: 'mobile', + anonymousId: 'anon_123', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'message type group is not supported: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message type group is not supported', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'bloomreach-validation-test-3', + name: destType, + description: 'Missing required field -> timestamp', + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + integrations: { + All: true, + }, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Timestamp is not present. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Timestamp is not present. Aborting', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/bloomreach/router/data.ts b/test/integrations/destinations/bloomreach/router/data.ts new file mode 100644 index 0000000000..e99d0cc8cd --- /dev/null +++ b/test/integrations/destinations/bloomreach/router/data.ts @@ -0,0 +1,220 @@ +import { generateMetadata } from '../../../testUtils'; +import { defaultMockFns } from '../mocks'; +import { + destType, + destination, + traits, + properties, + headers, + endpoint, + RouterInstrumentationErrorStatTags, +} from '../common'; + +const routerRequest = { + input: [ + { + message: { + type: 'track', + anonymousId: 'anonId1', + event: 'test_event_1A', + properties, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + destination, + }, + { + message: { + type: 'identify', + anonymousId: 'anonId1', + userId: 'userId1', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(2), + destination, + }, + { + message: { + type: 'track', + anonymousId: 'anonId2', + event: 'test_event_2A', + properties, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(3), + destination, + }, + { + message: { + type: 'track', + anonymousId: 'anonId1', + userId: 'userId1', + event: 'test_event_1B', + properties, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(4), + destination, + }, + { + message: { + type: 'identify', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(5), + destination, + }, + ], + destType, +}; +export const data = [ + { + id: 'bloomreach-router-test-1', + name: destType, + description: 'Basic Router Test to test multiple payloads', + scenario: 'Framework', + successCriteria: 'All events should be transformed successfully and status code should be 200', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: routerRequest, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + JSON: { + commands: [ + { + data: { + customer_ids: { cookie: 'anonId1' }, + properties, + timestamp: 1709566376, + event_type: 'test_event_1A', + }, + name: 'customers/events', + }, + { + data: { + customer_ids: { + registered: 'userId1', + cookie: 'anonId1', + }, + properties: { + email: 'test@example.com', + first_name: 'John', + last_name: 'Doe', + phone: '1234567890', + city: 'New York', + country: 'USA', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, + }, + update_timestamp: 1709566376, + }, + name: 'customers', + }, + { + data: { + customer_ids: { cookie: 'anonId2' }, + properties, + timestamp: 1709566376, + event_type: 'test_event_2A', + }, + name: 'customers/events', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [generateMetadata(1), generateMetadata(2), generateMetadata(3)], + batched: true, + statusCode: 200, + destination, + }, + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + JSON: { + commands: [ + { + data: { + customer_ids: { registered: 'userId1', cookie: 'anonId1' }, + properties, + timestamp: 1709566376, + event_type: 'test_event_1B', + }, + name: 'customers/events', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [generateMetadata(4)], + batched: true, + statusCode: 200, + destination, + }, + { + metadata: [generateMetadata(5)], + batched: false, + statusCode: 400, + error: 'Either one of userId or anonymousId is required. Aborting', + statTags: RouterInstrumentationErrorStatTags, + destination, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, +]; From 92515a5fd8a2798c48010078f62b360ec6a49979 Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Mon, 1 Apr 2024 16:57:04 +0530 Subject: [PATCH 067/240] feat: consent field support for ga4 (#3213) * feat: support consent fields in GA4 * chore: code review changes * chore: code review changes * fix(ga4): processor tests --------- Co-authored-by: krishnachaitanya --- src/v0/destinations/ga4/transform.js | 7 + src/v0/destinations/ga4/utils.js | 21 ++ src/v0/destinations/ga4/utils.test.js | 87 ++++- src/v0/util/googleUtils/index.js | 27 ++ src/v0/util/googleUtils/index.test.js | 52 ++- .../destinations/ga4/processor/data.ts | 309 ++++++++++++++++++ 6 files changed, 501 insertions(+), 2 deletions(-) diff --git a/src/v0/destinations/ga4/transform.js b/src/v0/destinations/ga4/transform.js index d8fc531e92..5280a46dab 100644 --- a/src/v0/destinations/ga4/transform.js +++ b/src/v0/destinations/ga4/transform.js @@ -27,6 +27,7 @@ const { const { getItemsArray, validateEventName, + prepareUserConsents, removeInvalidParams, isReservedEventName, getGA4ExclusionList, @@ -238,6 +239,12 @@ const responseBuilder = (message, { Config }) => { rawPayload.user_properties = userProperties; } + // Prepare GA4 consents + const consents = prepareUserConsents(message); + if (!isEmptyObject(consents)) { + rawPayload.consent = consents; + } + payload = removeUndefinedAndNullValues(payload); rawPayload = { ...rawPayload, events: [payload] }; diff --git a/src/v0/destinations/ga4/utils.js b/src/v0/destinations/ga4/utils.js index e4db494727..ce8afda560 100644 --- a/src/v0/destinations/ga4/utils.js +++ b/src/v0/destinations/ga4/utils.js @@ -7,8 +7,10 @@ const { isEmptyObject, extractCustomFields, isDefinedAndNotNull, + getIntegrationsObj, } = require('../../util'); const { mappingConfig, ConfigCategory } = require('./config'); +const { finaliseAnalyticsConsents } = require('../../util/googleUtils'); /** * Reserved event names cannot be used @@ -432,11 +434,30 @@ const prepareUserProperties = (message, piiPropertiesToIgnore = []) => { return validatedUserProperties; }; +/** + * Returns user consents + * Ref : https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=gtag#payload_consent + * @param {*} message + * @returns + */ +const prepareUserConsents = (message) => { + const integrationObj = getIntegrationsObj(message, 'ga4') || {}; + const eventLevelConsentsData = integrationObj?.consents || {}; + const consentConfigMap = { + analyticsPersonalizationConsent: 'ad_user_data', + analyticsUserDataConsent: 'ad_personalization', + }; + + const consents = finaliseAnalyticsConsents(consentConfigMap, eventLevelConsentsData); + return consents; +}; + module.exports = { getItem, getItemList, getItemsArray, validateEventName, + prepareUserConsents, removeInvalidParams, isReservedEventName, getGA4ExclusionList, diff --git a/src/v0/destinations/ga4/utils.test.js b/src/v0/destinations/ga4/utils.test.js index 18b3ab5766..501778910f 100644 --- a/src/v0/destinations/ga4/utils.test.js +++ b/src/v0/destinations/ga4/utils.test.js @@ -1,4 +1,9 @@ -const { validateEventName, prepareUserProperties, removeInvalidParams } = require('./utils'); +const { + validateEventName, + removeInvalidParams, + prepareUserConsents, + prepareUserProperties, +} = require('./utils'); const userPropertyData = [ { @@ -447,4 +452,84 @@ describe('Google Analytics 4 utils test', () => { expect(result).toEqual(expected); }); }); + + describe('prepareUserConsents function tests', () => { + it('Should return an empty object when no consents are given', () => { + const message = {}; + const result = prepareUserConsents(message); + expect(result).toEqual({}); + }); + + it('Should return an empty object when no consents are given', () => { + const message = { + integrations: { + GA4: {}, + }, + }; + const result = prepareUserConsents(message); + expect(result).toEqual({}); + }); + + it('Should return an empty object when no consents are given', () => { + const message = { + integrations: { + GA4: { + consents: {}, + }, + }, + }; + const result = prepareUserConsents(message); + expect(result).toEqual({}); + }); + + it('Should return a consents object when consents are given', () => { + const message = { + integrations: { + GA4: { + consents: { + ad_personalization: 'GRANTED', + ad_user_data: 'GRANTED', + }, + }, + }, + }; + const result = prepareUserConsents(message); + expect(result).toEqual({ + ad_personalization: 'GRANTED', + ad_user_data: 'GRANTED', + }); + }); + + it('Should return an empty object when invalid consents are given', () => { + const message = { + integrations: { + GA4: { + consents: { + ad_personalization: 'NOT_SPECIFIED', + ad_user_data: 'NOT_SPECIFIED', + }, + }, + }, + }; + const result = prepareUserConsents(message); + expect(result).toEqual({}); + }); + + it('Should return a valid consents values from consents object', () => { + const message = { + integrations: { + GA4: { + consents: { + ad_personalization: 'NOT_SPECIFIED', + ad_user_data: 'DENIED', + }, + }, + }, + }; + const result = prepareUserConsents(message); + expect(result).toEqual({ + ad_user_data: 'DENIED', + }); + }); + }); }); diff --git a/src/v0/util/googleUtils/index.js b/src/v0/util/googleUtils/index.js index c153731e73..ef7c244c17 100644 --- a/src/v0/util/googleUtils/index.js +++ b/src/v0/util/googleUtils/index.js @@ -1,4 +1,5 @@ const GOOGLE_ALLOWED_CONSENT_STATUS = ['UNSPECIFIED', 'UNKNOWN', 'GRANTED', 'DENIED']; +const GA4_ALLOWED_CONSENT_STATUS = ['GRANTED', 'DENIED']; const UNSPECIFIED_CONSENT = 'UNSPECIFIED'; const UNKNOWN_CONSENT = 'UNKNOWN'; @@ -82,10 +83,36 @@ const finaliseConsent = (consentConfigMap, eventLevelConsent = {}, destConfig = return consentObj; }; +/** + * Populates the consent object based on the provided configuration and consent mapping. + * @param {*} consentConfigMap + * @param {*} eventLevelConsent + * @returns + */ +const finaliseAnalyticsConsents = (consentConfigMap, eventLevelConsent = {}) => { + const consentObj = {}; + // Iterate through each key in consentConfigMap to set the consent + Object.keys(consentConfigMap).forEach((configKey) => { + const consentKey = consentConfigMap[configKey]; // e.g., 'ad_user_data' + + // Set consent only if valid + if ( + eventLevelConsent && + eventLevelConsent.hasOwnProperty(consentKey) && + GA4_ALLOWED_CONSENT_STATUS.includes(eventLevelConsent[consentKey]) + ) { + consentObj[consentKey] = eventLevelConsent[consentKey]; + } + }); + + return consentObj; +}; + module.exports = { populateConsentFromConfig, UNSPECIFIED_CONSENT, UNKNOWN_CONSENT, GOOGLE_ALLOWED_CONSENT_STATUS, finaliseConsent, + finaliseAnalyticsConsents, }; diff --git a/src/v0/util/googleUtils/index.test.js b/src/v0/util/googleUtils/index.test.js index 28e0fa9ac8..76ec624311 100644 --- a/src/v0/util/googleUtils/index.test.js +++ b/src/v0/util/googleUtils/index.test.js @@ -1,4 +1,8 @@ -const { populateConsentFromConfig, finaliseConsent } = require('./index'); +const { + finaliseConsent, + populateConsentFromConfig, + finaliseAnalyticsConsents, +} = require('./index'); describe('unit test for populateConsentFromConfig', () => { const consentConfigMap = { @@ -243,3 +247,49 @@ describe('finaliseConsent', () => { }); }); }); + +describe('unit test for finaliseAnalyticsConsents', () => { + const consentConfigMap = { + personalizationConsent: 'ad_personalization', + userDataConsent: 'ad_user_data', + }; + it('Should return an empty object when no valid consents are provided', () => { + const result = finaliseAnalyticsConsents(consentConfigMap, {}); + expect(result).toEqual({}); + }); + + it('Should set ad_user_data property of consent object when userDataConsent property is provided and its value is one of the allowed consent statuses', () => { + const properties = { ad_user_data: 'GRANTED' }; + const result = finaliseAnalyticsConsents(consentConfigMap, properties); + expect(result).toEqual({ ad_user_data: 'GRANTED' }); + }); + + it('Should set ad_personalization property of consent object when personalizationConsent property is provided and its value is one of the allowed consent statuses', () => { + const properties = { ad_personalization: 'DENIED' }; + const result = finaliseAnalyticsConsents(consentConfigMap, properties); + expect(result).toEqual({ ad_personalization: 'DENIED' }); + }); + + it('Should return an empty object when properties parameter is not provided', () => { + const result = finaliseAnalyticsConsents(consentConfigMap, undefined); + expect(result).toEqual({}); + }); + + it('Should return an empty object when properties parameter is null', () => { + const result = finaliseAnalyticsConsents(consentConfigMap, null); + expect(result).toEqual({}); + }); + + it('Should return an empty object when properties parameter is an UNSPECIFIED object', () => { + const result = finaliseAnalyticsConsents(consentConfigMap, {}); + expect(result).toEqual({}); + }); + + it('should return empty object when properties parameter contains ad_user_data and ad_personalization with non-allowed values', () => { + const result = finaliseAnalyticsConsents(consentConfigMap, { + userDataConsent: 'RANDOM', + personalizationConsent: 'RANDOM', + }); + expect(result).toEqual({}); + }); +}); diff --git a/test/integrations/destinations/ga4/processor/data.ts b/test/integrations/destinations/ga4/processor/data.ts index f96ca9e74a..4465ec9e2c 100644 --- a/test/integrations/destinations/ga4/processor/data.ts +++ b/test/integrations/destinations/ga4/processor/data.ts @@ -14900,4 +14900,313 @@ export const data = [ }, mockFns: defaultMockFns, }, + { + name: 'ga4', + description: '(gtag) send consents setting to ga4 with login event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', + originalTimestamp: '2022-04-26T05:17:09Z', + anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + device: { + adTrackingEnabled: 'false', + advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', + id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, + }, + ip: '0.0.0.0', + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + locale: 'en-US', + os: { + name: 'iOS', + version: '14.4.1', + }, + screen: { + density: 2, + }, + externalId: [ + { + type: 'ga4AppInstanceId', + id: 'dummyGA4AppInstanceId', + }, + { + type: 'ga4ClientId', + id: 'client_id', + }, + ], + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', + }, + type: 'track', + event: 'login', + properties: { + method: 'Google', + }, + integrations: { + All: true, + GA4: { + consents: { + ad_personalization: 'GRANTED', + ad_user_data: 'GRANTED', + }, + }, + }, + sentAt: '2022-04-20T15:20:57Z', + }, + destination: { + Config: { + apiSecret: 'dummyApiSecret', + measurementId: 'G-123456', + firebaseAppId: '', + blockPageViewEvent: false, + typesOfClient: 'gtag', + extendPageViewParams: false, + sendUserId: false, + eventFilteringOption: 'disable', + blacklistedEvents: [ + { + eventName: '', + }, + ], + whitelistedEvents: [ + { + eventName: '', + }, + ], + enableServerSideIdentify: false, + sendLoginSignup: false, + generateLead: false, + }, + Enabled: true, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.google-analytics.com/mp/collect', + headers: { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', + }, + params: { + api_secret: 'dummyApiSecret', + measurement_id: 'G-123456', + }, + body: { + JSON: { + client_id: 'client_id', + consent: { + ad_personalization: 'GRANTED', + ad_user_data: 'GRANTED', + }, + timestamp_micros: 1650950229000000, + non_personalized_ads: true, + events: [ + { + name: 'login', + params: { + method: 'Google', + engagement_time_msec: 1, + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + name: 'ga4', + description: '(gtag) send consents setting to ga4 with login event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', + originalTimestamp: '2022-04-26T05:17:09Z', + anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + device: { + adTrackingEnabled: 'false', + advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', + id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, + }, + ip: '0.0.0.0', + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + locale: 'en-US', + os: { + name: 'iOS', + version: '14.4.1', + }, + screen: { + density: 2, + }, + externalId: [ + { + type: 'ga4AppInstanceId', + id: 'dummyGA4AppInstanceId', + }, + { + type: 'ga4ClientId', + id: 'client_id', + }, + ], + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', + }, + type: 'track', + event: 'login', + properties: { + method: 'Google', + }, + integrations: { + All: true, + GA4: { + consents: { + ad_personalization: 'NOT_SPECIFIED', + ad_user_data: 'DENIED', + }, + }, + }, + sentAt: '2022-04-20T15:20:57Z', + }, + destination: { + Config: { + apiSecret: 'dummyApiSecret', + measurementId: 'G-123456', + firebaseAppId: '', + blockPageViewEvent: false, + typesOfClient: 'gtag', + extendPageViewParams: false, + sendUserId: false, + eventFilteringOption: 'disable', + blacklistedEvents: [ + { + eventName: '', + }, + ], + whitelistedEvents: [ + { + eventName: '', + }, + ], + enableServerSideIdentify: false, + sendLoginSignup: false, + generateLead: false, + }, + Enabled: true, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.google-analytics.com/mp/collect', + headers: { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', + }, + params: { + api_secret: 'dummyApiSecret', + measurement_id: 'G-123456', + }, + body: { + JSON: { + client_id: 'client_id', + consent: { + ad_user_data: 'DENIED', + }, + timestamp_micros: 1650950229000000, + non_personalized_ads: true, + events: [ + { + name: 'login', + params: { + method: 'Google', + engagement_time_msec: 1, + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, ]; From eb7b197322c617b14c2579de8cb4d4dacf8e1df3 Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Mon, 1 Apr 2024 18:15:12 +0530 Subject: [PATCH 068/240] feat: onboarding linkedin conversion api (#3194) * feat: onboarding linkedin conversion api * fix: fixing network handler proxy v1 * fix: adding test cases round 1 * fix: network handler fix * fix: adding test cases processor router * fix: small edit * fix: adding auth network test cases * fix: adding auth network test cases part 2 * fix: editing logic + test cases * fix: adding util test cases * fix: corrrection in conversion logic and validation * fix: fix lint error * Apply suggestions from code review Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> * feat: review comments addressed * feat: review comments addressed * fix: review comments addressed * fix: aborting the revoked token PRs * fix: editing error category * fix: dest response example --------- Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> --- .../v2/destinations/linkedin_ads/config.js | 26 + .../data/linkedinUserInfoConfig.json | 31 + .../linkedin_ads/procWorkflow.yaml | 80 ++ .../destinations/linkedin_ads/rtWorkflow.yaml | 42 + src/cdk/v2/destinations/linkedin_ads/utils.js | 282 +++++++ .../destinations/linkedin_ads/utils.test.js | 293 +++++++ src/features.json | 1 + src/v0/util/data/GenericFieldMapping.json | 8 +- .../linkedin_ads/networkHandler.js | 112 +++ .../linkedin_ads/dataDelivery/business.ts | 188 +++++ .../linkedin_ads/dataDelivery/data.ts | 4 + .../linkedin_ads/dataDelivery/oauth.ts | 207 +++++ .../destinations/linkedin_ads/network.ts | 186 +++++ .../processor/configLevelFeaturesTestData.ts | 219 ++++++ .../linkedin_ads/processor/data.ts | 11 + .../linkedin_ads/processor/trackTestData.ts | 718 ++++++++++++++++++ .../processor/validationTestData.ts | 323 ++++++++ .../destinations/linkedin_ads/router/data.ts | 462 +++++++++++ test/integrations/testUtils.ts | 2 +- 19 files changed, 3193 insertions(+), 2 deletions(-) create mode 100644 src/cdk/v2/destinations/linkedin_ads/config.js create mode 100644 src/cdk/v2/destinations/linkedin_ads/data/linkedinUserInfoConfig.json create mode 100644 src/cdk/v2/destinations/linkedin_ads/procWorkflow.yaml create mode 100644 src/cdk/v2/destinations/linkedin_ads/rtWorkflow.yaml create mode 100644 src/cdk/v2/destinations/linkedin_ads/utils.js create mode 100644 src/cdk/v2/destinations/linkedin_ads/utils.test.js create mode 100644 src/v1/destinations/linkedin_ads/networkHandler.js create mode 100644 test/integrations/destinations/linkedin_ads/dataDelivery/business.ts create mode 100644 test/integrations/destinations/linkedin_ads/dataDelivery/data.ts create mode 100644 test/integrations/destinations/linkedin_ads/dataDelivery/oauth.ts create mode 100644 test/integrations/destinations/linkedin_ads/network.ts create mode 100644 test/integrations/destinations/linkedin_ads/processor/configLevelFeaturesTestData.ts create mode 100644 test/integrations/destinations/linkedin_ads/processor/data.ts create mode 100644 test/integrations/destinations/linkedin_ads/processor/trackTestData.ts create mode 100644 test/integrations/destinations/linkedin_ads/processor/validationTestData.ts create mode 100644 test/integrations/destinations/linkedin_ads/router/data.ts diff --git a/src/cdk/v2/destinations/linkedin_ads/config.js b/src/cdk/v2/destinations/linkedin_ads/config.js new file mode 100644 index 0000000000..344980e7d0 --- /dev/null +++ b/src/cdk/v2/destinations/linkedin_ads/config.js @@ -0,0 +1,26 @@ +const { getMappingConfig } = require('../../../../v0/util'); + +// ref : https://learn.microsoft.com/en-us/linkedin/marketing/integrations/ads-reporting/conversions-api?view=li-lms-2024-02&tabs=http#adding-multiple-conversion-events-in-a-batch +const BATCH_ENDPOINT = 'https://api.linkedin.com/rest/conversionEvents'; +const API_HEADER_METHOD = 'BATCH_CREATE'; +const API_VERSION = '202402'; // yyyymm format +const API_PROTOCOL_VERSION = '2.0.0'; + +const CONFIG_CATEGORIES = { + USER_INFO: { + name: 'linkedinUserInfoConfig', + type: 'user', + }, +}; + +const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); + +module.exports = { + MAX_BATCH_SIZE: 5000, + BATCH_ENDPOINT, + API_HEADER_METHOD, + API_VERSION, + API_PROTOCOL_VERSION, + CONFIG_CATEGORIES, + MAPPING_CONFIG, +}; diff --git a/src/cdk/v2/destinations/linkedin_ads/data/linkedinUserInfoConfig.json b/src/cdk/v2/destinations/linkedin_ads/data/linkedinUserInfoConfig.json new file mode 100644 index 0000000000..760510b5b3 --- /dev/null +++ b/src/cdk/v2/destinations/linkedin_ads/data/linkedinUserInfoConfig.json @@ -0,0 +1,31 @@ +[ + { + "destKey": "firstName", + "sourceKeys": "firstName", + "required": false, + "sourceFromGenericMap": true + }, + { + "destKey": "lastName", + "sourceKeys": "lastName", + "required": false, + "sourceFromGenericMap": true + }, + { + "destKey": "title", + "sourceKeys": "title", + "required": false, + "sourceFromGenericMap": true + }, + { + "destKey": "companyName", + "sourceKeys": "context.traits.companyName", + "required": false + }, + { + "destKey": "countryCode", + "sourceKeys": "countryCode", + "sourceFromGenericMap": true, + "required": false + } +] diff --git a/src/cdk/v2/destinations/linkedin_ads/procWorkflow.yaml b/src/cdk/v2/destinations/linkedin_ads/procWorkflow.yaml new file mode 100644 index 0000000000..4b17afc368 --- /dev/null +++ b/src/cdk/v2/destinations/linkedin_ads/procWorkflow.yaml @@ -0,0 +1,80 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + exportAll: true + - name: removeUndefinedValues + path: ../../../../v0/util + - name: removeUndefinedNullValuesAndEmptyObjectArray + path: ../../../../v0/util + - name: defaultRequestConfig + path: ../../../../v0/util + - name: OAuthSecretError + path: '@rudderstack/integrations-lib' + - path: ./utils + - path: ./config + - path: lodash + name: cloneDeep + +steps: + - name: checkIfProcessed + condition: .message.statusCode + template: | + $.batchMode ? .message.body.JSON : .message + onComplete: return + - name: messageType + template: | + .message.type.toLowerCase() + - name: validateInput + template: | + let messageType = $.outputs.messageType; + $.assert(messageType, "Message type is not present. Aborting message."); + $.assert(messageType in {{$.EventType.([.TRACK])}}, + "message type " + messageType + " is not supported") + + - name: validateInputForTrack + description: Additional validation for Track events + condition: $.outputs.messageType === {{$.EventType.TRACK}} + template: | + $.assert(.message.event, "event could not be mapped to conversion rule. Aborting.") + - name: commonFields + description: | + Builds common fields in destination payload. + ref: https://learn.microsoft.com/en-us/linkedin/marketing/integrations/ads-reporting/conversions-api?view=li-lms-2024-02&tabs=curl#adding-multiple-conversion-events-in-a-batch + template: | + let commonFields = .message.().({ + "conversionHappenedAt": $.fetchAndVerifyConversionHappenedAt(^.message), + "eventId": $.getOneByPaths(., ^.destination.Config.deduplicationKey) ?? .messageId, + "conversionValue":$.calculateConversionObject(^.message), + "user":{ + "userIds":$.fetchUserIds(^.message,^.destination.Config), + "userInfo":$.curateUserInfoObject(^.message) + } + }); + $.removeUndefinedValues(commonFields) + - name: basePayload + template: | + const payload = $.outputs.commonFields; + payload + + - name: deduceConversionEventRules + template: | + $.context.deduceConversionRulesArray = $.deduceConversionRules(.message.event,.destination.Config) + + - name: preparePayload + template: | + $.context.payloads = $.context.deduceConversionRulesArray@conversionRuleId.( + const newPayload = $.cloneDeep($.outputs.basePayload); + newPayload.conversion = $.createConversionString(conversionRuleId); + $.removeUndefinedNullValuesAndEmptyObjectArray(newPayload) + )[]; + - name: buildResponse + template: | + $.assertThrow((.metadata.secret && .metadata.secret.accessToken), new $.OAuthSecretError("Secret or accessToken is not present in the metadata")) + const accessToken = .metadata.secret.accessToken + const response = $.defaultRequestConfig(); + response.body.JSON = {elements: $.context.payloads}; + response.endpoint = $.BATCH_ENDPOINT; + response.method = "POST"; + response.headers = $.generateHeader(accessToken) + response diff --git a/src/cdk/v2/destinations/linkedin_ads/rtWorkflow.yaml b/src/cdk/v2/destinations/linkedin_ads/rtWorkflow.yaml new file mode 100644 index 0000000000..8b81790de2 --- /dev/null +++ b/src/cdk/v2/destinations/linkedin_ads/rtWorkflow.yaml @@ -0,0 +1,42 @@ +bindings: + - path: ./utils + - path: ./config + +steps: + - name: validateInput + template: | + $.assert(Array.isArray(^) && ^.length > 0, "Invalid event array") + + - name: transform + externalWorkflow: + path: ./procWorkflow.yaml + bindings: + - name: batchMode + value: true + loopOverInput: true + - name: successfulEvents + template: | + $.outputs.transform#idx.output.({ + "message": .[], + "destination": ^ [idx].destination, + "metadata": ^ [idx].metadata + })[] + - name: failedEvents + template: | + $.outputs.transform#idx.error.({ + "metadata": ^[idx].metadata[], + "destination": ^[idx].destination, + "batched": false, + "statusCode": .status, + "error": .message, + "statTags": .originalError.statTags + })[] + + - name: batchSuccessfulEvents + description: Batches the successfulEvents + template: | + $.batchResponseBuilder($.outputs.successfulEvents); + + - name: finalPayload + template: | + [...$.outputs.failedEvents, ...$.outputs.batchSuccessfulEvents] diff --git a/src/cdk/v2/destinations/linkedin_ads/utils.js b/src/cdk/v2/destinations/linkedin_ads/utils.js new file mode 100644 index 0000000000..f3f7783962 --- /dev/null +++ b/src/cdk/v2/destinations/linkedin_ads/utils.js @@ -0,0 +1,282 @@ +const lodash = require('lodash'); +const crypto = require('crypto'); +const moment = require('moment'); + +const { + InstrumentationError, + getHashFromArrayWithDuplicate, + isDefinedAndNotNullAndNotEmpty, + ConfigurationError, +} = require('@rudderstack/integrations-lib'); +const { + getFieldValueFromMessage, + constructPayload, + getDestinationExternalID, +} = require('../../../../v0/util'); +const { + MAPPING_CONFIG, + CONFIG_CATEGORIES, + MAX_BATCH_SIZE, + API_HEADER_METHOD, + API_PROTOCOL_VERSION, + API_VERSION, +} = require('./config'); +const { + AUTH_STATUS_INACTIVE, + REFRESH_TOKEN, +} = require('../../../../adapters/networkhandler/authConstants'); + +const formatEmail = (email, destConfig) => { + if (email) { + if (destConfig.hashData === true) { + return crypto.createHash('sha256').update(email).digest('hex'); + } + return email; + } + return null; +}; + +const fetchUserIds = (message, destConfig) => { + const userIds = []; + const email = formatEmail(getFieldValueFromMessage(message, 'email'), destConfig); + const linkedinFirstPartyAdsTrackingUUID = getDestinationExternalID( + message, + 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + ); + const acxiomId = getDestinationExternalID(message, 'ACXIOM_ID'); + const oracleMoatId = getDestinationExternalID(message, 'ORACLE_MOAT_ID'); + if (!email && !linkedinFirstPartyAdsTrackingUUID && !acxiomId && !oracleMoatId) { + throw new InstrumentationError( + '[LinkedIn Conversion API] no matching user id found. Please provide at least one of the following: email, linkedinFirstPartyAdsTrackingUUID, acxiomId, oracleMoatId', + ); + } + + if (email) { + userIds.push({ idType: 'SHA256_EMAIL', idValue: email }); + } + if (linkedinFirstPartyAdsTrackingUUID) { + userIds.push({ + idType: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + idValue: linkedinFirstPartyAdsTrackingUUID, + }); + } + if (acxiomId) { + userIds.push({ idType: 'ACXIOM_ID', idValue: acxiomId }); + } + if (oracleMoatId) { + userIds.push({ idType: 'ORACLE_MOAT_ID', idValue: oracleMoatId }); + } + return userIds; +}; + +const curateUserInfoObject = (message) => { + const commonCategory = CONFIG_CATEGORIES.USER_INFO; + const commonPayload = constructPayload(message, MAPPING_CONFIG[commonCategory.name]); + if (commonPayload.firstName && commonPayload.lastName) { + return commonPayload; + } + return null; +}; + +function checkIfPricePresent(properties) { + // Check if 'products' exists and is an array + if (Array.isArray(properties?.products)) { + // Use 'some' to check if at least one object has a 'price' field + const hasPrice = properties.products.some((product) => product.hasOwnProperty('price')); + return hasPrice; + } + return !!properties.price; +} + +const calculateConversionObject = (message) => { + const { properties, event } = message; + + const calculateAmount = () => { + if (properties?.products && properties.products.length > 0) { + return properties.products.reduce( + (acc, product) => acc + (product.price || 0) * (product.quantity || 1), + 0, + ); + } + return properties.price * (properties.quantity ?? 1); + }; + if (checkIfPricePresent(properties)) { + const conversionObject = { + currencyCode: properties.currency || 'USD', + amount: `${calculateAmount()}`, + }; + return conversionObject; + } + throw new InstrumentationError( + `[LinkedIn Conversion API]: Cannot map price for event ${event}. Aborting`, + ); +}; + +const deduceConversionRules = (trackEventName, destConfig) => { + let conversionRule; + const { conversionMapping } = destConfig; + if (conversionMapping.length > 0) { + const keyMap = getHashFromArrayWithDuplicate(conversionMapping, 'from', 'to', false); + conversionRule = keyMap[trackEventName]; + } + if (isDefinedAndNotNullAndNotEmpty(conversionRule)) { + const finalEvent = typeof conversionRule === 'string' ? [conversionRule] : [...conversionRule]; + return finalEvent; + } + throw new ConfigurationError( + `[LinkedIn Conversion API] no matching conversion rule found for ${trackEventName}. Please provide a conversion rule. Aborting`, + ); +}; + +const createConversionString = (ruleId) => `urn:lla:llaPartnerConversion:${ruleId}`; + +const generateHeader = (accessToken) => { + const headers = { + 'Content-Type': 'application/json', + 'X-RestLi-Method': API_HEADER_METHOD, + 'X-Restli-Protocol-Version': API_PROTOCOL_VERSION, + 'LinkedIn-Version': API_VERSION, + Authorization: `Bearer ${accessToken}`, + }; + return headers; +}; + +const fetchAndVerifyConversionHappenedAt = (message) => { + const timeStamp = message.timestamp || message.originalTimestamp; + if (timeStamp) { + const start = moment(timeStamp); + if (!start.isValid()) { + throw new InstrumentationError('Invalid timestamp format.'); + } + const current = moment(); + // calculates past event in days + const deltaDay = current.diff(start, 'days', true); + + if (Math.ceil(deltaDay) > 90) { + throw new InstrumentationError('Events must be sent within ninety days of their occurrence.'); + } + } + + const timeInMilliseconds = moment(timeStamp).valueOf(); // `valueOf` returns the time in milliseconds + return timeInMilliseconds; +}; + +function batchResponseBuilder(successfulEvents) { + const constants = { + version: successfulEvents[0].message[0].version, + type: successfulEvents[0].message[0].type, + method: successfulEvents[0].message[0].method, + endpoint: successfulEvents[0].message[0].endpoint, + headers: successfulEvents[0].message[0].headers, + destination: successfulEvents[0].destination, + }; + + const allElements = successfulEvents.flatMap((event) => event.message[0].body.JSON.elements); + const allMetadata = successfulEvents.map((event) => event.metadata); + + // Using lodash to chunk the elements into groups of up to 3 + const chunkedElements = lodash.chunk(allElements, MAX_BATCH_SIZE); + const chunkedMetadata = lodash.chunk(allMetadata, MAX_BATCH_SIZE); + + return chunkedElements.map((elementsBatch, index) => ({ + batchedRequest: { + body: { + JSON: { elements: elementsBatch }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: constants.version, + type: constants.type, + method: constants.method, + endpoint: constants.endpoint, + headers: constants.headers, + params: {}, + files: {}, + }, + metadata: chunkedMetadata[index], + batched: true, + statusCode: 200, + destination: constants.destination, + })); +} + +function constructPartialStatus(errorMessage) { + const errorPattern = /Index: (\d+), ERROR :: (.*?)\n/g; + let match; + const errorMap = {}; + + try { + // eslint-disable-next-line no-cond-assign + while ((match = errorPattern.exec(errorMessage)) !== null) { + const [, index, message] = match; + errorMap[index] = message; + } + + return errorMap; + } catch (e) { + return null; + } +} + +function createResponseArray(metadata, partialStatus) { + const partialStatusArray = Object.entries(partialStatus).map(([index, message]) => [ + Number(index), + message, + ]); + // Convert destPartialStatus to an object for easier lookup + const errorMap = partialStatusArray.reduce((acc, [index, message]) => { + const jobId = metadata[index]?.jobId; // Get the jobId from the metadata array based on the index + if (jobId !== undefined) { + acc[jobId] = message; + } + return acc; + }, {}); + + return metadata.map((item) => { + const error = errorMap[item.jobId]; + return { + statusCode: error ? 400 : 500, + metadata: item, + error: error || 'success', + }; + }); +} + +/** + * + * @param {*} destinationResponse example: {status : 401, response {"status":401,"serviceErrorCode":65601,"code":"REVOKED_ACCESS_TOKEN","message":"The token used in the request has been revoked by the user"}} + * @returns proper auth error category + */ +const getAuthErrCategoryFromStCode = (destinationResponse) => { + const { status, response } = destinationResponse; + if (status === 401) { + if (response.code === 'REVOKED_ACCESS_TOKEN') { + // ACCESS_DENIED + return AUTH_STATUS_INACTIVE; + } + // UNAUTHORIZED + return REFRESH_TOKEN; + } + if (status === 403) { + // ACCESS_DENIED + return AUTH_STATUS_INACTIVE; + } + return ''; +}; + +module.exports = { + formatEmail, + calculateConversionObject, + curateUserInfoObject, + fetchUserIds, + deduceConversionRules, + createConversionString, + generateHeader, + fetchAndVerifyConversionHappenedAt, + batchResponseBuilder, + constructPartialStatus, + createResponseArray, + checkIfPricePresent, + getAuthErrCategoryFromStCode, +}; diff --git a/src/cdk/v2/destinations/linkedin_ads/utils.test.js b/src/cdk/v2/destinations/linkedin_ads/utils.test.js new file mode 100644 index 0000000000..ee52928198 --- /dev/null +++ b/src/cdk/v2/destinations/linkedin_ads/utils.test.js @@ -0,0 +1,293 @@ +const crypto = require('crypto'); +const { + formatEmail, + calculateConversionObject, + fetchUserIds, + curateUserInfoObject, + deduceConversionRules, + generateHeader, + constructPartialStatus, + createResponseArray, + checkIfPricePresent, +} = require('./utils'); +const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); +const { API_HEADER_METHOD, API_PROTOCOL_VERSION, API_VERSION } = require('./config'); + +describe('formatEmail', () => { + // Returns a hashed email when a valid email is passed as argument. + it('should return a hashed email when a valid email is passed as argument', () => { + const email = 'test@example.com'; + const hashedEmail = crypto.createHash('sha256').update(email).digest('hex'); + expect(formatEmail(email, { hashData: true })).toEqual(hashedEmail); + }); + + // Returns null when an empty string is passed as argument. + it('should return null when an empty string is passed as argument', () => { + const email = ''; + expect(formatEmail(email)).toBeNull(); + }); +}); + +describe('calculateConversionObject', () => { + // Returns a conversion object with currency code 'USD' and amount 0 when message properties are empty + it('should throw instrumentation error when message properties are empty', () => { + const message = { properties: {} }; + expect(() => { + fetchUserIds(calculateConversionObject(message)); + }).toThrow(InstrumentationError); + }); + + // Returns a conversion object with currency code 'USD' and amount 0 when message properties price is defined but quantity is 0 + it('should return a conversion object with currency code "USD" and amount 0 when message properties price is defined but quantity is 0', () => { + const message = { properties: { price: 10, quantity: 0 } }; + const conversionObject = calculateConversionObject(message); + expect(conversionObject).toEqual({ currencyCode: 'USD', amount: '0' }); + }); +}); + +describe('fetchUserIds', () => { + // Throws an InstrumentationError when no user id is found in the message and no exception is caught + it('should throw an InstrumentationError when no user id is found in the message and no exception is caught', () => { + const message = {}; + const destConfig = { + hashData: true, + }; + expect(() => { + fetchUserIds(message, destConfig); + }).toThrow(InstrumentationError); + }); + it('should create user Ids array of objects with all allowed values', () => { + const message = { + context: { + traits: { + email: 'abc@gmail.com', + }, + externalId: [ + { + type: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + id: 'abcdefg', + }, + { + type: 'ACXIOM_ID', + id: '123456', + }, + { + type: 'ORACLE_MOAT_ID', + id: '789012', + }, + ], + }, + }; + const destConfig = { + hashData: true, + }; + const userIdArray = fetchUserIds(message, destConfig); + expect(userIdArray).toEqual([ + { + idType: 'SHA256_EMAIL', + idValue: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + { + idType: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + idValue: 'abcdefg', + }, + { + idType: 'ACXIOM_ID', + idValue: '123456', + }, + { + idType: 'ORACLE_MOAT_ID', + idValue: '789012', + }, + ]); + }); +}); + +describe('curateUserInfoObject', () => { + // Returns a non-null object when given a message with both first and last name + it('should return a non-null object when given a message with both first and last name and other properties', () => { + const message = { + context: { + traits: { + firstName: 'John', + lastName: 'Doe', + title: 'Mr.', + companyName: 'RudderTest', + countryCode: 'USA', + }, + }, + }; + const result = curateUserInfoObject(message); + expect(result).toEqual({ + firstName: 'John', + lastName: 'Doe', + title: 'Mr.', + companyName: 'RudderTest', + countryCode: 'USA', + }); + }); + // Returns a null object when given a message with an empty first name + it('should return a null object when given a message without both first and last name', () => { + const message = { + context: { + traits: { + title: 'Mr.', + companyName: 'RudderTest', + countryCode: 'USA', + }, + }, + }; + const result = curateUserInfoObject(message); + expect(result).toEqual(null); + }); +}); + +describe('deduceConversionRules', () => { + // When conversionMapping is empty, return ConfigurationError + it('should return ConfigurationError when conversionMapping is empty', () => { + const trackEventName = 'eventName'; + const destConfig = { + conversionMapping: [], + }; + expect(() => deduceConversionRules(trackEventName, destConfig)).toThrow(ConfigurationError); + }); + + // When conversionMapping is not empty, return the conversion rule + it('should return the conversion rule when conversionMapping is not empty', () => { + const trackEventName = 'eventName'; + const destConfig = { + conversionMapping: [{ from: 'eventName', to: 'conversionEvent' }], + }; + const result = deduceConversionRules(trackEventName, destConfig); + expect(result).toEqual(['conversionEvent']); + }); + + it('should return the conversion rule when conversionMapping is not empty', () => { + const trackEventName = 'eventName'; + const destConfig = { + conversionMapping: [ + { from: 'eventName', to: 'conversionEvent' }, + { from: 'eventName', to: 'conversionEvent2' }, + ], + }; + const result = deduceConversionRules(trackEventName, destConfig); + expect(result).toEqual(['conversionEvent', 'conversionEvent2']); + }); +}); + +describe('generateHeader', () => { + // Returns a headers object with Content-Type, X-RestLi-Method, X-Restli-Protocol-Version, LinkedIn-Version, and Authorization keys when passed a valid access token. + it('should return a headers object with all keys when passed a valid access token', () => { + // Arrange + const accessToken = 'validAccessToken'; + + // Act + const result = generateHeader(accessToken); + + // Assert + expect(result).toEqual({ + 'Content-Type': 'application/json', + 'X-RestLi-Method': API_HEADER_METHOD, + 'X-Restli-Protocol-Version': API_PROTOCOL_VERSION, + 'LinkedIn-Version': API_VERSION, + Authorization: `Bearer ${accessToken}`, + }); + }); + + // Returns a headers object with default values for all keys when passed an invalid access token. + it('should return a headers object with default values for all keys when passed an invalid access token', () => { + // Arrange + const accessToken = 'invalidAccessToken'; + + // Act + const result = generateHeader(accessToken); + + // Assert + expect(result).toEqual({ + 'Content-Type': 'application/json', + 'X-RestLi-Method': API_HEADER_METHOD, + 'X-Restli-Protocol-Version': API_PROTOCOL_VERSION, + 'LinkedIn-Version': API_VERSION, + Authorization: `Bearer ${accessToken}`, + }); + }); +}); + +describe('constructPartialStatus', () => { + // The function correctly constructs a map of error messages when given a string containing error messages. + it('should correctly construct a map of error messages when given a string containing error messages', () => { + const errorMessage = 'Index: 1, ERROR :: Error 1\nIndex: 2, ERROR :: Error 2\n'; + const expectedErrorMap = { + 1: 'Error 1', + 2: 'Error 2', + }; + + const result = constructPartialStatus(errorMessage); + + expect(result).toEqual(expectedErrorMap); + }); + + // The function throws an error when given a non-string input. + it('should throw an error when given a non-string input', () => { + const errorMessage = 123; + const result = constructPartialStatus(errorMessage); + expect(result).toEqual({}); + }); +}); + +describe('createResponseArray', () => { + // Returns an array of objects with statusCode, metadata and error properties + it('should return an array of objects with statusCode, metadata and error properties', () => { + // Arrange + const metadata = [{ jobId: 1 }, { jobId: 2 }, { jobId: 3 }]; + const partialStatus = { + 0: 'Partial status message 1', + 2: 'Partial status message 3', + }; + + // Act + const result = createResponseArray(metadata, partialStatus); + + // Assert + expect(result).toEqual([ + { + statusCode: 400, + metadata: { jobId: 1 }, + error: 'Partial status message 1', + }, + { + statusCode: 500, + metadata: { jobId: 2 }, + error: 'success', + }, + { + statusCode: 400, + metadata: { jobId: 3 }, + error: 'Partial status message 3', + }, + ]); + }); +}); + +describe('checkIfPricePresent', () => { + // Returns true if properties object has a 'price' field + it('should return true when properties object has a price field', () => { + const properties = { price: 10 }; + const result = checkIfPricePresent(properties); + expect(result).toBe(true); + }); + + // Returns true if properties object has a 'products' array with an object containing a 'price' field and a 'price' field in the properties object + it('should return true when properties object has a products array with an object containing a price field and a price field in the properties object', () => { + const properties = { products: [{ price: 10 }, { quantity: 3 }], price: 20 }; + const result = checkIfPricePresent(properties); + expect(result).toBe(true); + }); + + // Returns false if properties object does not have a 'price' field or a 'products' array with an object containing a 'price' field + it('should return false when properties object does not have a price field or a products array with an object containing a price field', () => { + const properties = { quantity: 5 }; + const result = checkIfPricePresent(properties); + expect(result).toBe(false); + }); +}); diff --git a/src/features.json b/src/features.json index fd06b3b241..6d2cac9340 100644 --- a/src/features.json +++ b/src/features.json @@ -68,6 +68,7 @@ "INTERCOM": true, "NINETAILED": true, "KOALA": true, + "LINKEDIN_ADS": true, "BLOOMREACH": true, "MOVABLE_INK": true }, diff --git a/src/v0/util/data/GenericFieldMapping.json b/src/v0/util/data/GenericFieldMapping.json index 87dd5e5e55..0a7b309d89 100644 --- a/src/v0/util/data/GenericFieldMapping.json +++ b/src/v0/util/data/GenericFieldMapping.json @@ -116,5 +116,11 @@ "context.traits.address.postal_code", "context.traits.address.postalCode" ], - "sessionId": ["session_id", "context.sessionId"] + "sessionId": ["session_id", "context.sessionId"], + "countryCode": [ + "traits.countryCode", + "traits.address.countryCode", + "context.traits.address.countryCode", + "context.traits.countryCode" + ] } diff --git a/src/v1/destinations/linkedin_ads/networkHandler.js b/src/v1/destinations/linkedin_ads/networkHandler.js new file mode 100644 index 0000000000..8219e18fcb --- /dev/null +++ b/src/v1/destinations/linkedin_ads/networkHandler.js @@ -0,0 +1,112 @@ +const lodash = require('lodash'); +const { TransformerProxyError } = require('../../../v0/util/errorTypes'); +const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); +const { isHttpStatusSuccess } = require('../../../v0/util/index'); + +const { + processAxiosResponse, + getDynamicErrorType, +} = require('../../../adapters/utils/networkUtils'); +const tags = require('../../../v0/util/tags'); +const { + constructPartialStatus, + createResponseArray, + getAuthErrCategoryFromStCode, +} = require('../../../cdk/v2/destinations/linkedin_ads/utils'); + +// eslint-disable-next-line consistent-return +// ref : +// 1) https://learn.microsoft.com/en-us/linkedin/shared/api-guide/concepts/error-handling +// 2) https://learn.microsoft.com/en-us/linkedin/marketing/integrations/ads-reporting/conversions-api?view=li-lms-2024-02&tabs=http#api-error-details +// statusCode : 422 we have found by trial and error, not documented in their doc + +const responseHandler = (responseParams) => { + const { destinationResponse, rudderJobMetadata } = responseParams; + const message = `[LINKEDIN_CONVERSION_API Response V1 Handler] - Request Processed Successfully`; + let responseWithIndividualEvents = []; + const { response, status } = destinationResponse; + + // even if a single event is unsuccessful, the entire batch will fail, we will filter that event out and retry others + if (!isHttpStatusSuccess(status)) { + const errorMessage = response.message || 'unknown error format'; + responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ + statusCode: status, + metadata, + error: errorMessage, + })); + if (status === 401 || status === 403) { + const finalStatus = status === 401 && response.code !== 'REVOKED_ACCESS_TOKEN' ? 500 : 400; + const finalMessage = + status === 401 + ? 'Invalid or expired access token. Retrying' + : 'Lack of permissions to perform the operation. Aborting'; + throw new TransformerProxyError( + `LinkedIn Conversion API: Error transformer proxy v1 during LinkedIn Conversion API response transformation. ${finalMessage}`, + finalStatus, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(finalStatus), + }, + destinationResponse, + getAuthErrCategoryFromStCode(destinationResponse), + responseWithIndividualEvents, + ); + } + // if the status is 422, we need to parse the error message and construct the response array + if (status === 422) { + const destPartialStatus = constructPartialStatus(response?.message); + // if the error message is not in the expected format, we will abort all of the events + if (!destPartialStatus || lodash.isEmpty(destPartialStatus)) { + throw new TransformerProxyError( + `LinkedIn Conversion API: Error transformer proxy v1 during LinkedIn Conversion API response transformation. Error parsing error message`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + getAuthErrCategoryFromStCode(status), + responseWithIndividualEvents, + ); + } + responseWithIndividualEvents = [...createResponseArray(rudderJobMetadata, destPartialStatus)]; + return { + status, + message, + destinationResponse, + response: responseWithIndividualEvents, + }; + } + throw new TransformerProxyError( + `LinkedIn Conversion API: Error transformer proxy v1 during LinkedIn Conversion API response transformation. ${errorMessage}`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + getAuthErrCategoryFromStCode(status), + responseWithIndividualEvents, + ); + } + + // otherwise all events are successful + responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ + statusCode: 200, + metadata, + error: 'success', + })); + + return { + status, + message, + destinationResponse, + response: responseWithIndividualEvents, + }; +}; + +function networkHandler() { + this.prepareProxy = prepareProxyRequest; + this.proxy = proxyRequest; + this.processAxiosResponse = processAxiosResponse; + this.responseHandler = responseHandler; +} + +module.exports = { networkHandler }; diff --git a/test/integrations/destinations/linkedin_ads/dataDelivery/business.ts b/test/integrations/destinations/linkedin_ads/dataDelivery/business.ts new file mode 100644 index 0000000000..ff4fa4455f --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/dataDelivery/business.ts @@ -0,0 +1,188 @@ +import { generateProxyV1Payload } from '../../../testUtils'; +import { ProxyV1TestData } from '../../../testTypes'; + +export const element = { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '0', + currencyCode: 'USD', + }, + eventId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: 'abc@gmail.com', + }, + ], + }, +}; + +export const wrongFormatElement = { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + currencyCode: 'USD', + }, + eventId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: 'abc@gmail.com', + }, + ], + userInfo: { + city: 'San Francisco', + }, + }, +}; + +export const testJSONData = { + elements: [{ ...element }], +}; + +export const wrongFormattedTestJSONData = { + elements: [{ ...wrongFormatElement }], +}; + +export const testJSONDataWithDifferentTypeConversion = { + elements: [ + { + ...element, + conversion: 'urn:li:partner:differentConversion', + }, + ], +}; + +export const statTags = { + destType: 'LINKEDIN_ADS', + errorCategory: 'network', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', +}; + +export const metadata = { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: false, +}; +export const headerBlockWithCorrectAccessToken = { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', + 'LinkedIn-Version': '202402', + 'X-RestLi-Method': 'BATCH_CREATE', + 'X-Restli-Protocol-Version': '2.0.0', +}; + +const commonRequestParametersWithWrongElemet = { + headers: headerBlockWithCorrectAccessToken, + JSON: wrongFormattedTestJSONData, +}; + +const commonRequestParametersWithDifferentConversion = { + headers: headerBlockWithCorrectAccessToken, + JSON: testJSONDataWithDifferentTypeConversion, +}; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'linkedin_ads_v1_scenario_1', + name: 'linkedin_ads', + description: 'Event fails due to wrong process followed while creating a conversion', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + ...commonRequestParametersWithDifferentConversion, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + "LinkedIn Conversion API: Error transformer proxy v1 during LinkedIn Conversion API response transformation. Incorrect conversions information provided. Conversion's method should be CONVERSIONS_API, indices [0] (0-indexed)", + response: [ + { + error: + '{"message":"Incorrect conversions information provided. Conversion\'s method should be CONVERSIONS_API, indices [0] (0-indexed)","status":400}', + statusCode: 400, + metadata, + }, + ], + statTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'linkedin_ads_v1_scenario_2', + name: 'linkedin_ads', + description: 'Event fails due to wrong format payload sent to linkedin', + successCriteria: 'Should return 400 with appropriate reason of failure', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + ...commonRequestParametersWithWrongElemet, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + destinationResponse: { + response: { + message: + 'Index: 0, ERROR :: /conversionValue/amount :: field is required but not found and has no default value\nERROR :: /user/userInfo/firstName :: field is required but not found and has no default value\nERROR :: /user/userInfo/lastName :: field is required but not found and has no default value\n', + status: 422, + }, + status: 422, + }, + message: + '[LINKEDIN_CONVERSION_API Response V1 Handler] - Request Processed Successfully', + response: [ + { + error: + '/conversionValue/amount :: field is required but not found and has no default value', + statusCode: 400, + metadata, + }, + ], + status: 422, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/linkedin_ads/dataDelivery/data.ts b/test/integrations/destinations/linkedin_ads/dataDelivery/data.ts new file mode 100644 index 0000000000..5bb0a7ef6e --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/dataDelivery/data.ts @@ -0,0 +1,4 @@ +import { testScenariosForV1API, statTags as baseStatTags } from './business'; +import { oauthScenariosV1 } from './oauth'; + +export const data = [...testScenariosForV1API, ...oauthScenariosV1]; diff --git a/test/integrations/destinations/linkedin_ads/dataDelivery/oauth.ts b/test/integrations/destinations/linkedin_ads/dataDelivery/oauth.ts new file mode 100644 index 0000000000..5cc643d972 --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/dataDelivery/oauth.ts @@ -0,0 +1,207 @@ +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; +import { ProxyV1TestData } from '../../../testTypes'; + +export const testJSONData = { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '0', + currencyCode: 'USD', + }, + eventId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: 'abc@gmail.com', + }, + ], + }, + }, + ], +}; +export const statTags = { + destType: 'LINKEDIN_ADS', + errorCategory: 'network', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', +}; + +export const metadata = { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: false, +}; + +export const headerBlockWithCorrectAccessToken = { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', + 'LinkedIn-Version': '202402', + 'X-RestLi-Method': 'BATCH_CREATE', + 'X-Restli-Protocol-Version': '2.0.0', +}; + +const commonRequestParameters = { + headers: headerBlockWithCorrectAccessToken, + JSON: testJSONData, +}; +const commonRequestParametersWithInvalidAccess = { + headers: { ...headerBlockWithCorrectAccessToken, Authorization: 'Bearer invalidToken' }, + JSON: testJSONData, + accessToken: 'invalidToken', +}; + +const commonRequestParametersWithRevokedAccess = { + headers: { ...headerBlockWithCorrectAccessToken, Authorization: 'Bearer revokedToken' }, + JSON: testJSONData, + accessToken: 'revokedToken', +}; + +export const oauthScenariosV1: ProxyV1TestData[] = [ + { + id: 'linkedin_ads_v1_oauth_scenario_1', + name: 'linkedin_ads', + description: 'app event fails due to revoked access token error', + successCriteria: 'Should return 400 with revoked access token error', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + ...commonRequestParametersWithRevokedAccess, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + response: [ + { + error: + '{"status":401,"serviceErrorCode":65601,"code":"REVOKED_ACCESS_TOKEN","message":"The token used in the request has been revoked by the user"}', + statusCode: 400, + metadata: { ...metadata, secret: { accessToken: 'revokedToken' } }, + }, + ], + statTags, + authErrorCategory: 'AUTH_STATUS_INACTIVE', + message: + 'LinkedIn Conversion API: Error transformer proxy v1 during LinkedIn Conversion API response transformation. Invalid or expired access token. Retrying', + status: 400, + }, + }, + }, + }, + }, + { + id: 'linkedin_ads_v1_oauth_scenario_2', + name: 'linkedin_ads', + description: 'app event fails due to invalid access token error', + successCriteria: 'Should return 500 with invalid access token error', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + ...commonRequestParametersWithInvalidAccess, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + response: [ + { + error: + '{"status":401,"serviceErrorCode":65600,"code":"INVALID_ACCESS_TOKEN","message":"Invalid access token"}', + statusCode: 500, + metadata: { ...metadata, secret: { accessToken: 'invalidToken' } }, + }, + ], + statTags: { ...statTags, errorType: 'retryable' }, + authErrorCategory: 'REFRESH_TOKEN', + message: + 'LinkedIn Conversion API: Error transformer proxy v1 during LinkedIn Conversion API response transformation. Invalid or expired access token. Retrying', + status: 500, + }, + }, + }, + }, + }, + { + id: 'linkedin_ads_v1_oauth_scenario_3', + name: 'linkedin_ads', + description: 'success case', + successCriteria: 'Should return 200 response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + ...commonRequestParameters, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: + '[LINKEDIN_CONVERSION_API Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + elements: [ + { + status: 201, + }, + { + status: 201, + }, + ], + }, + status: 200, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/linkedin_ads/network.ts b/test/integrations/destinations/linkedin_ads/network.ts new file mode 100644 index 0000000000..890ad48589 --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/network.ts @@ -0,0 +1,186 @@ +export const headerBlockWithCorrectAccessToken = { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', + 'LinkedIn-Version': '202402', + 'X-RestLi-Method': 'BATCH_CREATE', + 'X-Restli-Protocol-Version': '2.0.0', +}; +export const element = { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '0', + currencyCode: 'USD', + }, + eventId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: 'abc@gmail.com', + }, + ], + }, +}; + +export const testJSONData = { + elements: [{ ...element }], +}; + +export const testJSONDataWithDifferentTypeConversion = { + elements: [ + { + ...element, + conversion: 'urn:li:partner:differentConversion', + }, + ], +}; + +export const wrongFormatElement = { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + currencyCode: 'USD', + }, + eventId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: 'abc@gmail.com', + }, + ], + userInfo: { + city: 'San Francisco', + }, + }, +}; + +export const wrongFormattedTestJSONData = { + elements: [{ ...wrongFormatElement }], +}; + +// MOCK DATA +const businessMockData = [ + { + description: 'Mock response from destination depicting request with a revoked access token', + httpReq: { + method: 'post', + url: 'https://api.linkedin.com/rest/conversionEvents', + headers: { ...headerBlockWithCorrectAccessToken, Authorization: 'Bearer revokedToken' }, + data: testJSONData, + }, + httpRes: { + data: { + status: 401, + serviceErrorCode: 65601, + code: 'REVOKED_ACCESS_TOKEN', + message: 'The token used in the request has been revoked by the user', + }, + status: 401, + statusText: 'OK', + }, + }, + { + description: 'Mock response from destination depicting request with an invalid access token', + httpReq: { + method: 'post', + url: 'https://api.linkedin.com/rest/conversionEvents', + headers: { ...headerBlockWithCorrectAccessToken, Authorization: 'Bearer invalidToken' }, + data: testJSONData, + }, + httpRes: { + data: { + status: 401, + serviceErrorCode: 65600, + code: 'INVALID_ACCESS_TOKEN', + message: 'Invalid access token', + }, + status: 401, + statusText: 'OK', + }, + }, + { + description: + 'Mock response from destination depicting a correct request with a valid access token', + httpReq: { + method: 'post', + url: 'https://api.linkedin.com/rest/conversionEvents', + headers: headerBlockWithCorrectAccessToken, + data: testJSONData, + }, + httpRes: { + data: { + elements: [ + { + status: 201, + }, + { + status: 201, + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response from destination depicting request with a conversion created differently than choosing direct API', + httpReq: { + method: 'post', + url: 'https://api.linkedin.com/rest/conversionEvents', + headers: headerBlockWithCorrectAccessToken, + data: testJSONDataWithDifferentTypeConversion, + }, + httpRes: { + data: { + message: + "Incorrect conversions information provided. Conversion's method should be CONVERSIONS_API, indices [0] (0-indexed)", + status: 400, + }, + status: 400, + statusText: 'OK', + }, + }, + { + description: + 'Mock response from destination depicting request with a conversion created differently than choosing direct API', + httpReq: { + method: 'post', + url: 'https://api.linkedin.com/rest/conversionEvents', + headers: headerBlockWithCorrectAccessToken, + data: testJSONDataWithDifferentTypeConversion, + }, + httpRes: { + data: { + message: + "Incorrect conversions information provided. Conversion's method should be CONVERSIONS_API, indices [0] (0-indexed)", + status: 400, + }, + status: 400, + statusText: 'OK', + }, + }, + { + description: + 'Mock response from destination depicting request with a conversion created differently than choosing direct API', + httpReq: { + method: 'post', + url: 'https://api.linkedin.com/rest/conversionEvents', + headers: headerBlockWithCorrectAccessToken, + data: wrongFormattedTestJSONData, + }, + httpRes: { + data: { + message: + 'Index: 0, ERROR :: /conversionValue/amount :: field is required but not found and has no default value\nERROR :: /user/userInfo/firstName :: field is required but not found and has no default value\nERROR :: /user/userInfo/lastName :: field is required but not found and has no default value\n', + status: 422, + }, + status: 422, + statusText: 'OK', + }, + }, +]; + +export const networkCallsData = [...businessMockData]; diff --git a/test/integrations/destinations/linkedin_ads/processor/configLevelFeaturesTestData.ts b/test/integrations/destinations/linkedin_ads/processor/configLevelFeaturesTestData.ts new file mode 100644 index 0000000000..287e35e5a7 --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/processor/configLevelFeaturesTestData.ts @@ -0,0 +1,219 @@ +import { + generateMetadata, + generateTrackPayload, + overrideDestination, + transformResultBuilder, +} from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'linkedin_ads', + DisplayName: 'LinkedIn Ads', + Config: { + cdkV2Enabled: true, + }, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + hashData: true, + conversionMapping: [ + { + from: 'ABC Searched', + to: '1234567', + }, + { + from: 'spin_result', + to: '23456', + }, + { + from: 'ABC Searched', + to: '34567', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + Enabled: true, +}; + +const commonUserTraits = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', +}; + +const commonUserProperties = { + price: 400, + additional_bet_index: 0, + eventId: '12345', +}; + +const commonTimestamp = new Date('2023-10-14'); + +const commonHeader = { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', + 'LinkedIn-Version': '202402', + 'X-RestLi-Method': 'BATCH_CREATE', + 'X-Restli-Protocol-Version': '2.0.0', +}; + +export const configLevelFeaturesTestData: ProcessorTestData[] = [ + { + id: 'linkedin_ads-config-test-1', + name: 'linkedin_ads', + description: 'Track call : hashData is set to false and no deduplication key is provided', + scenario: 'Business', + successCriteria: 'email provided will not be hashed and eventId will be mapped from messageId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { hashData: false }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '400', + currencyCode: 'USD', + }, + eventId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: 'abc@gmail.com', + }, + ], + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-config-test-2', + name: 'linkedin_ads', + description: 'Track call : hashData is set to true and deduplication key is provided', + scenario: 'Business', + successCriteria: + 'email provided will be hashed and eventId will be mapped from deduplication key properties.eventId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + deduplicationKey: `properties.eventId`, + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '400', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/linkedin_ads/processor/data.ts b/test/integrations/destinations/linkedin_ads/processor/data.ts new file mode 100644 index 0000000000..edd6d1f1b5 --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/processor/data.ts @@ -0,0 +1,11 @@ +import { trackTestData } from './trackTestData'; +import { validationTestData } from './validationTestData'; +import { configLevelFeaturesTestData } from './configLevelFeaturesTestData'; + +export const mockFns = (_) => { + // @ts-ignore + jest.useFakeTimers().setSystemTime(new Date('2023-10-15')); +}; +export const data = [...trackTestData, ...validationTestData, ...configLevelFeaturesTestData].map( + (d) => ({ ...d, mockFns }), +); diff --git a/test/integrations/destinations/linkedin_ads/processor/trackTestData.ts b/test/integrations/destinations/linkedin_ads/processor/trackTestData.ts new file mode 100644 index 0000000000..f9dfc528db --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/processor/trackTestData.ts @@ -0,0 +1,718 @@ +import { generateMetadata, generateTrackPayload, transformResultBuilder } from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'linkedin_ads', + DisplayName: 'LinkedIn Ads', + Config: { + cdkV2Enabled: true, + }, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + hashData: true, + deduplicationKey: 'properties.eventId', + conversionMapping: [ + { + from: 'ABC Searched', + to: '1234567', + }, + { + from: 'spin_result', + to: '23456', + }, + { + from: 'ABC Searched', + to: '34567', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + Enabled: true, +}; + +const commonUserTraits = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', +}; + +const commonUserProperties = { + price: 400, + additional_bet_index: 0, + eventId: '12345', +}; + +const commonPropertiesWithProducts = { + revenue: 400, + additional_bet_index: 0, + eventId: '12345', + products: [ + { + product_id: '123', + name: 'abc', + category: 'def', + brand: 'xyz', + variant: 'pqr', + price: 100, + quantity: 2, + }, + { + product_id: '456', + name: 'def', + category: 'abc', + brand: 'pqr', + variant: 'xyz', + price: 200, + quantity: 3, + }, + ], +}; + +const commonPropertiesWithProductsPriceNotPresentInAll = { + revenue: 400, + additional_bet_index: 0, + eventId: '12345', + products: [ + { + product_id: '123', + name: 'abc', + category: 'def', + brand: 'xyz', + variant: 'pqr', + quantity: 2, + }, + { + product_id: '456', + name: 'def', + category: 'abc', + brand: 'pqr', + variant: 'xyz', + price: 200, + quantity: 3, + }, + ], +}; + +const commonTimestamp = new Date('2023-10-14'); + +const commonStatTags = { + destinationId: 'default-destinationId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + implementation: 'cdkV2', + destType: 'LINKEDIN_ADS', + module: 'destination', + feature: 'processor', + workspaceId: 'default-workspaceId', +}; + +const commonHeader = { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', + 'LinkedIn-Version': '202402', + 'X-RestLi-Method': 'BATCH_CREATE', + 'X-Restli-Protocol-Version': '2.0.0', +}; + +export const trackTestData: ProcessorTestData[] = [ + { + id: 'linkedin_ads-track-test-1', + name: 'linkedin_ads', + description: 'Track call : particular track event mapped to a specific conversion rule', + scenario: 'Business', + successCriteria: + 'event will respect the UI mapping and create a conversion event with the mapped conversion rule', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '400', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-track-test-2', + name: 'linkedin_ads', + description: 'Track call : event is mapped with more than one conversion rules ', + scenario: 'Business', + successCriteria: + 'event will respect the UI mapping and create a conversion event with the mapped conversion rule and club the two conversions in a single elements array', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'ABC Searched', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:1234567', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '400', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + }, + }, + { + conversion: 'urn:lla:llaPartnerConversion:34567', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '400', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-track-test-3', + name: 'linkedin_ads', + description: 'Track call : track event containing multiple allowed user identifiqers', + scenario: 'Business', + successCriteria: + 'event will respect the UI mapping and create a conversion event with the mapped conversion rule', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonUserProperties, + externalId: [ + { + id: 'test@rudderlabs.com', + type: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + }, + { + id: 'test@rudderlabs.com', + type: 'ACXIOM_ID', + }, + { + id: 'test@rudderlabs.com', + type: 'ORACLE_MOAT_ID', + }, + ], + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '400', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + { + idType: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + idValue: 'test@rudderlabs.com', + }, + { + idType: 'ACXIOM_ID', + idValue: 'test@rudderlabs.com', + }, + { + idType: 'ORACLE_MOAT_ID', + idValue: 'test@rudderlabs.com', + }, + ], + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-track-test-4', + name: 'linkedin_ads', + description: 'Track call : event not containing any of the allowed user identifiers', + scenario: 'Business', + successCriteria: + 'Error will be thrown as the event does not contain any of the allowed user identifiers', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'ABC Searched', + properties: commonUserProperties, + context: { + traits: { + firstName: 'John', + }, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '[LinkedIn Conversion API] no matching user id found. Please provide at least one of the following: email, linkedinFirstPartyAdsTrackingUUID, acxiomId, oracleMoatId: Workflow: procWorkflow, Step: commonFields, ChildStep: undefined, OriginalError: [LinkedIn Conversion API] no matching user id found. Please provide at least one of the following: email, linkedinFirstPartyAdsTrackingUUID, acxiomId, oracleMoatId', + metadata: generateMetadata(1), + statTags: commonStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-track-test-5', + name: 'linkedin_ads', + description: 'Track call : track event containing product array', + scenario: 'Business', + successCriteria: + 'the amount will be summation of product * quantity for all the products in the array', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonPropertiesWithProducts, + externalId: [ + { + id: 'test@rudderlabs.com', + type: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + }, + { + id: 'test@rudderlabs.com', + type: 'ACXIOM_ID', + }, + { + id: 'test@rudderlabs.com', + type: 'ORACLE_MOAT_ID', + }, + ], + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '800', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + { + idType: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + idValue: 'test@rudderlabs.com', + }, + { + idType: 'ACXIOM_ID', + idValue: 'test@rudderlabs.com', + }, + { + idType: 'ORACLE_MOAT_ID', + idValue: 'test@rudderlabs.com', + }, + ], + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-track-test-6', + name: 'linkedin_ads', + description: 'Track call : track event containing first name and last name in traits', + scenario: 'Business', + successCriteria: + 'output event will contain userInfo object only because first name and last name are present in traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonUserProperties, + context: { + traits: { ...commonUserTraits, firstName: 'John', lastName: 'Doe' }, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '400', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + userInfo: { + firstName: 'John', + lastName: 'Doe', + }, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-track-test-7', + name: 'linkedin_ads', + description: + 'Track call : track event containing product array where not all products contains price field', + scenario: 'Business', + successCriteria: + 'the amount will be summation of product * quantity for all the products in the array', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonPropertiesWithProductsPriceNotPresentInAll, + externalId: [ + { + id: 'test@rudderlabs.com', + type: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + }, + { + id: 'test@rudderlabs.com', + type: 'ACXIOM_ID', + }, + { + id: 'test@rudderlabs.com', + type: 'ORACLE_MOAT_ID', + }, + ], + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '600', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + { + idType: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + idValue: 'test@rudderlabs.com', + }, + { + idType: 'ACXIOM_ID', + idValue: 'test@rudderlabs.com', + }, + { + idType: 'ORACLE_MOAT_ID', + idValue: 'test@rudderlabs.com', + }, + ], + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/linkedin_ads/processor/validationTestData.ts b/test/integrations/destinations/linkedin_ads/processor/validationTestData.ts new file mode 100644 index 0000000000..4579cf68ee --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/processor/validationTestData.ts @@ -0,0 +1,323 @@ +import { generateMetadata, generateTrackPayload, overrideDestination } from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'linkedin_ads', + DisplayName: 'LinkedIn Ads', + Config: { + cdkV2Enabled: true, + }, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + hashData: true, + conversionMapping: [ + { + from: 'ABC Searched', + to: '1234567', + }, + { + from: 'spin_result', + to: '23456', + }, + { + from: 'ABC Searched', + to: '34567', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + Enabled: true, +}; + +const commonUserTraits = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', +}; + +const commonUserProperties = { + additional_bet_index: 0, + eventId: '12345', +}; + +const commonUserPropertiesWithProductWithoutPrice = { + additional_bet_index: 0, + eventId: '12345', + products: [ + { + productId: '12345', + }, + { + productId: '123456', + }, + ], +}; + +const commonStats = { + destinationId: 'default-destinationId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + implementation: 'cdkV2', + destType: 'LINKEDIN_ADS', + module: 'destination', + feature: 'processor', + workspaceId: 'default-workspaceId', +}; + +const commonTimestamp = new Date('2023-10-14'); +const olderTimestamp = new Date('2023-07-13'); + +export const validationTestData: ProcessorTestData[] = [ + { + id: 'linkedin_ads-validation-test-1', + name: 'linkedin_ads', + description: 'Track call : event is older than 90 days', + scenario: 'Business', + successCriteria: 'shoud throw error with status code 400 and error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: { ...commonUserProperties, price: 400 }, + context: { + traits: commonUserTraits, + }, + timestamp: olderTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { hashData: false }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Events must be sent within ninety days of their occurrence.: Workflow: procWorkflow, Step: commonFields, ChildStep: undefined, OriginalError: Events must be sent within ninety days of their occurrence.', + metadata: generateMetadata(1), + statTags: { + destinationId: 'default-destinationId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + implementation: 'cdkV2', + destType: 'LINKEDIN_ADS', + module: 'destination', + feature: 'processor', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-validation-test-2', + name: 'linkedin_ads', + description: 'Track call : event not mapped to conversion rule in UI', + scenario: 'Business', + successCriteria: + 'should throw error with status code 400 and error message no matching conversion rule found for random event. Please provide a conversion rule. Aborting', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'random event', + properties: { ...commonUserProperties, price: 400 }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + deduplicationKey: `properties.eventId`, + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '[LinkedIn Conversion API] no matching conversion rule found for random event. Please provide a conversion rule. Aborting: Workflow: procWorkflow, Step: deduceConversionEventRules, ChildStep: undefined, OriginalError: [LinkedIn Conversion API] no matching conversion rule found for random event. Please provide a conversion rule. Aborting', + metadata: generateMetadata(1), + statTags: { ...commonStats, errorType: 'configuration' }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-validation-test-3', + name: 'linkedin_ads', + description: '[Error]: Check for unsupported message type', + scenario: 'Framework', + successCriteria: + 'Response should contain error message and status code should be 400, as we are sending a message type which is not supported by linkedin_ads destination and the error message should be Event type random is not supported', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: commonDestination, + metadata: generateMetadata(1), + message: { + userId: 'user123', + type: 'random', + groupId: 'XUepkK', + traits: { + subscribe: true, + }, + context: { + traits: { + email: 'test@rudderstack.com', + phone: '+12 345 678 900', + consent: 'email', + }, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'message type random is not supported: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message type random is not supported', + metadata: generateMetadata(1), + statTags: commonStats, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-validation-test-4', + name: 'linkedin_ads', + description: 'Track call : properties without product array and no price', + scenario: 'Business', + successCriteria: + 'should throw error with status code 400 and error message regarding price is a mandatory field for linkedin conversions', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'random event', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + deduplicationKey: `properties.eventId`, + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '[LinkedIn Conversion API]: Cannot map price for event random event. Aborting: Workflow: procWorkflow, Step: commonFields, ChildStep: undefined, OriginalError: [LinkedIn Conversion API]: Cannot map price for event random event. Aborting', + metadata: generateMetadata(1), + statTags: commonStats, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-validation-test-5', + name: 'linkedin_ads', + description: 'Track call : properties with product array and no price', + scenario: 'Business', + successCriteria: + 'should throw error with status code 400 and error message regarding price is a mandatory field for linkedin conversions', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'random event', + properties: commonUserPropertiesWithProductWithoutPrice, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + deduplicationKey: `properties.eventId`, + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '[LinkedIn Conversion API]: Cannot map price for event random event. Aborting: Workflow: procWorkflow, Step: commonFields, ChildStep: undefined, OriginalError: [LinkedIn Conversion API]: Cannot map price for event random event. Aborting', + metadata: generateMetadata(1), + statTags: commonStats, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/linkedin_ads/router/data.ts b/test/integrations/destinations/linkedin_ads/router/data.ts new file mode 100644 index 0000000000..cf7defe6af --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/router/data.ts @@ -0,0 +1,462 @@ +export const mockFns = (_) => { + // @ts-ignore + jest.useFakeTimers().setSystemTime(new Date('2023-10-15')); +}; + +const config = { + hashData: true, + deduplicationKey: 'properties.eventId', + conversionMapping: [ + { + from: 'ABC Searched', + to: '1234567', + }, + { + from: 'spin_result', + to: '23456', + }, + { + from: 'ABC Searched', + to: '34567', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], +}; + +const commonDestination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'linkedin_ads', + DisplayName: 'LinkedIn Ads', + Config: { + cdkV2Enabled: true, + }, + }, + WorkspaceID: '123', + Transformations: [], + Config: config, + Enabled: true, +}; + +export const data = [ + { + id: 'linkedin_ads-track-test-1', + name: 'linkedin_ads', + description: 'Track call : custom event calls with simple user properties and traits', + scenario: 'Business', + successCriteria: + 'event not respecting the internal mapping and as well as UI mapping should be considered as a custom event and should be sent as it is', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'track', + event: 'ABC Searched', + sentAt: '2020-08-14T05: 30: 30.118Z', + channel: 'web', + context: { + source: 'test', + userAgent: 'chrome', + traits: { + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + email: 'abc@gmail.com', + phone: '+1234589947', + gender: 'non-binary', + db: '19950715', + lastname: 'Rudderlabs', + firstName: 'Test', + address: { + city: 'Kolkata', + state: 'WB', + zip: '700114', + country: 'IN', + }, + }, + device: { + advertisingId: 'abc123', + }, + library: { + name: 'rudder-sdk-ruby-sync', + version: '1.0.6', + }, + }, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2024-02-10T12:16:07.251Z', + properties: { + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, + currency: 'USD', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + requestIP: '123.0.0.0', + optOutType: 'LDP', + clickId: 'dummy_clickId', + + shipping: 3, + subtotal: 22.5, + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', + }, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { + All: true, + }, + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 1, + secret: { + accessToken: 'dummyToken', + }, + }, + destination: commonDestination, + }, + { + message: { + type: 'track', + event: 'ABC Searched', + sentAt: '2020-08-14T05: 30: 30.118Z', + channel: 'web', + context: { + source: 'test', + userAgent: 'chrome', + traits: { + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + email: 'abc@gmail.com', + phone: '+1234589947', + gender: 'non-binary', + db: '19950715', + lastname: 'Rudderlabs', + firstName: 'Test', + address: { + city: 'Kolkata', + state: 'WB', + zip: '700114', + country: 'IN', + }, + }, + device: { + advertisingId: 'abc123', + }, + library: { + name: 'rudder-sdk-ruby-sync', + version: '1.0.6', + }, + }, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2024-02-10T12:16:07.251Z', + properties: { + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, + currency: 'USD', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + requestIP: '123.0.0.0', + optOutType: 'LDP', + clickId: 'dummy_clickId', + + shipping: 3, + subtotal: 22.5, + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', + }, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { + All: true, + }, + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 2, + secret: { + accessToken: 'dummyToken', + }, + }, + destination: commonDestination, + }, + { + message: { + type: 'track', + event: 'spin_result', + sentAt: '2020-08-14T05: 30: 30.118Z', + channel: 'web', + context: { + source: 'test', + userAgent: 'chrome', + traits: { + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + phone: '+1234589947', + gender: 'non-binary', + db: '19950715', + lastname: 'Rudderlabs', + firstName: 'Test', + address: { + city: 'Kolkata', + state: 'WB', + zip: '700114', + country: 'IN', + }, + }, + device: { + advertisingId: 'abc123', + }, + library: { + name: 'rudder-sdk-ruby-sync', + version: '1.0.6', + }, + }, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2024-02-10T12:16:07.251Z', + properties: { + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, + currency: 'USD', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + requestIP: '123.0.0.0', + optOutType: 'LDP', + clickId: 'dummy_clickId', + + shipping: 3, + subtotal: 22.5, + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', + }, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { + All: true, + }, + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 3, + secret: { + accessToken: 'dummyToken', + }, + }, + destination: commonDestination, + }, + ], + destType: 'linkedin_ads', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 3, + secret: { + accessToken: 'dummyToken', + }, + }, + ], + destination: { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'linkedin_ads', + DisplayName: 'LinkedIn Ads', + Config: { + cdkV2Enabled: true, + }, + }, + WorkspaceID: '123', + Transformations: [], + Config: config, + Enabled: true, + }, + batched: false, + statusCode: 400, + error: + '[LinkedIn Conversion API] no matching user id found. Please provide at least one of the following: email, linkedinFirstPartyAdsTrackingUUID, acxiomId, oracleMoatId', + statTags: { + destType: 'LINKEDIN_ADS', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'router', + implementation: 'cdkV2', + module: 'destination', + }, + }, + { + batchedRequest: { + body: { + JSON: { + elements: [ + { + conversionHappenedAt: 1707567367251, + eventId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + conversionValue: { + currencyCode: 'USD', + amount: '50', + }, + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + userInfo: { + firstName: 'Test', + lastName: 'Rudderlabs', + }, + }, + conversion: 'urn:lla:llaPartnerConversion:1234567', + }, + { + conversionHappenedAt: 1707567367251, + eventId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + conversionValue: { + currencyCode: 'USD', + amount: '50', + }, + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + userInfo: { + firstName: 'Test', + lastName: 'Rudderlabs', + }, + }, + conversion: 'urn:lla:llaPartnerConversion:34567', + }, + { + conversionHappenedAt: 1707567367251, + eventId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + conversionValue: { + currencyCode: 'USD', + amount: '50', + }, + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + userInfo: { + firstName: 'Test', + lastName: 'Rudderlabs', + }, + }, + conversion: 'urn:lla:llaPartnerConversion:1234567', + }, + { + conversionHappenedAt: 1707567367251, + eventId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + conversionValue: { + currencyCode: 'USD', + amount: '50', + }, + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + userInfo: { + firstName: 'Test', + lastName: 'Rudderlabs', + }, + }, + conversion: 'urn:lla:llaPartnerConversion:34567', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.linkedin.com/rest/conversionEvents', + headers: { + 'Content-Type': 'application/json', + 'X-RestLi-Method': 'BATCH_CREATE', + 'X-Restli-Protocol-Version': '2.0.0', + 'LinkedIn-Version': '202402', + Authorization: 'Bearer dummyToken', + }, + params: {}, + files: {}, + }, + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 1, + secret: { + accessToken: 'dummyToken', + }, + }, + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 2, + secret: { + accessToken: 'dummyToken', + }, + }, + ], + batched: true, + statusCode: 200, + destination: commonDestination, + }, + ], + }, + }, + }, + }, +].map((d) => ({ ...d, mockFns })); diff --git a/test/integrations/testUtils.ts b/test/integrations/testUtils.ts index 13a76702f9..c16aeff98c 100644 --- a/test/integrations/testUtils.ts +++ b/test/integrations/testUtils.ts @@ -484,7 +484,7 @@ export const generateProxyV1Payload = ( workspaceId: 'default-workspaceId', sourceId: 'default-sourceId', secret: { - accessToken: 'default-accessToken', + accessToken: payloadParameters.accessToken || 'default-accessToken', }, dontBatch: false, }, From 84cdf5f3f9970332359d0284156bd79b7c6701e0 Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Mon, 1 Apr 2024 21:24:00 +0530 Subject: [PATCH 069/240] chore: fix deployment conflicts (#3230) --- .github/workflows/prepare-for-prod-dt-deploy.yml | 7 +++---- .github/workflows/prepare-for-prod-ut-deploy.yml | 7 +++---- .github/workflows/prepare-for-staging-deploy.yml | 3 +-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/prepare-for-prod-dt-deploy.yml b/.github/workflows/prepare-for-prod-dt-deploy.yml index a5ca48e3f8..fedbac8ba1 100644 --- a/.github/workflows/prepare-for-prod-dt-deploy.yml +++ b/.github/workflows/prepare-for-prod-dt-deploy.yml @@ -118,10 +118,9 @@ jobs: yq eval -i ".user-transformer.image.repository=\"$TF_IMAGE_REPOSITORY\"" multi-tenant/multi-tenant.yaml git add multi-tenant/multi-tenant.yaml - cd ../../../../config-be-rudder-transformer - yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" values.prod.yaml - yq eval -i ".config-be-rudder-transformer.image.repository=\"$TF_IMAGE_REPOSITORY\"" values.prod.yaml - git add values.prod.yaml + cd ../../../../config-be-rudder-transformer/environment/prod + yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" base.yaml + git add base.yaml git commit -m "chore: upgrade shared transformers to $TAG_NAME" git push -u origin shared-transformer-$TAG_NAME diff --git a/.github/workflows/prepare-for-prod-ut-deploy.yml b/.github/workflows/prepare-for-prod-ut-deploy.yml index c2900d61da..a6f7271d9c 100644 --- a/.github/workflows/prepare-for-prod-ut-deploy.yml +++ b/.github/workflows/prepare-for-prod-ut-deploy.yml @@ -102,11 +102,10 @@ jobs: cd rudder-devops git checkout -b shared-user-transformer-$UT_TAG_NAME - cd helm-charts/config-be-rudder-transformer + cd helm-charts/config-be-rudder-transformer/environment/prod - yq eval -i ".config-be-user-transformer.image.tag=\"$UT_TAG_NAME\"" values.prod.yaml - yq eval -i ".config-be-user-transformer.image.repository=\"$TF_IMAGE_REPOSITORY\"" values.prod.yaml - git add values.prod.yaml + yq eval -i ".config-be-user-transformer.image.tag=\"$UT_TAG_NAME\"" base.yaml + git add base.yaml git commit -m "chore: upgrade shared user-transformers to $UT_TAG_NAME" git push -u origin shared-user-transformer-$UT_TAG_NAME diff --git a/.github/workflows/prepare-for-staging-deploy.yml b/.github/workflows/prepare-for-staging-deploy.yml index 3e0b3aac19..200ea51fff 100644 --- a/.github/workflows/prepare-for-staging-deploy.yml +++ b/.github/workflows/prepare-for-staging-deploy.yml @@ -115,8 +115,7 @@ jobs: cd ../../../../config-be-rudder-transformer/environment/staging yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" base.yaml yq eval -i ".config-be-user-transformer.image.tag=\"$TAG_NAME\"" base.yaml - git add values.staging.yaml - + git add base.yaml git commit -m "chore: upgrade staging env transformers to \"$TAG_NAME\"" git push -u origin $BRANCH_NAME gh pr create --fill From 73701915b8e24b0a00afc48ddef7c687ecf02055 Mon Sep 17 00:00:00 2001 From: Anant Jain Date: Mon, 1 Apr 2024 21:32:44 +0530 Subject: [PATCH 070/240] feat: snapchat conversion: add event level_complete --- .../snapchat_conversion/config.js | 1 + .../snapchat_conversion/processor/data.ts | 134 ++++++++++++++++++ 2 files changed, 135 insertions(+) diff --git a/src/v0/destinations/snapchat_conversion/config.js b/src/v0/destinations/snapchat_conversion/config.js index e0126ea3b1..1cce713fbb 100644 --- a/src/v0/destinations/snapchat_conversion/config.js +++ b/src/v0/destinations/snapchat_conversion/config.js @@ -55,6 +55,7 @@ const eventNameMapping = { save: 'SAVE', subscribe: 'SUBSCRIBE', complete_tutorial: 'COMPLETE_TUTORIAL', + level_complete: 'LEVEL_COMPLETE', invite: 'INVITE', login: 'LOGIN', share: 'SHARE', diff --git a/test/integrations/destinations/snapchat_conversion/processor/data.ts b/test/integrations/destinations/snapchat_conversion/processor/data.ts index b0d14208cc..7f01dbac26 100644 --- a/test/integrations/destinations/snapchat_conversion/processor/data.ts +++ b/test/integrations/destinations/snapchat_conversion/processor/data.ts @@ -4600,6 +4600,140 @@ export const data = [ }, }, }, + { + name: 'snapchat_conversion', + description: 'test event mapping from destination config', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', + originalTimestamp: '2022-04-22T10:57:58Z', + anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', + context: { + traits: { + email: 'test@email.com', + phone: '+91 2111111 ', + }, + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + device: { + advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', + id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', + manufacturer: 'Google', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + locale: 'en-US', + os: { + name: 'iOS', + version: '14.4.1', + }, + screen: { + density: 2, + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', + }, + type: 'track', + event: 'Custom Event', + properties: { + query: 't-shirts', + event_conversion_type: 'web', + }, + integrations: { + All: true, + }, + sentAt: '2022-04-22T10:57:58Z', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: false, + }, + }, + Config: { + pixelId: 'dummyPixelId', + apiKey: 'dummyApiKey', + rudderEventsToSnapEvents: [ + { + from: 'Custom Event', + to: 'LEVEL_COMPLETE', + }, + ], + }, + }, + metadata: { + jobId: 47, + destinationId: 'd2', + workspaceId: 'w2', + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + metadata: { + jobId: 47, + destinationId: 'd2', + workspaceId: 'w2', + }, + output: { + version: '1', + type: 'REST', + userId: '', + method: 'POST', + endpoint: 'https://tr.snapchat.com/v2/conversion', + headers: { + Authorization: 'Bearer dummyApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_type: 'LEVEL_COMPLETE', + hashed_email: '73062d872926c2a556f17b36f50e328ddf9bff9d403939bd14b6c3b7f5a33fc2', + hashed_phone_number: + 'bc77d64d7045fe44795ed926df37231a0cfb6ec6b74588c512790e9f143cc492', + hashed_mobile_ad_id: + 'f9779d734aaee50f16ee0011260bae7048f1d9a128c62b6a661077875701edd2', + hashed_idfv: '54bd0b26a3d39dad90f5149db49b9fd9ba885f8e35d1d94cae69273f5e657b9f', + user_agent: + 'mozilla/5.0 (macintosh; intel mac os x 10_15_2) applewebkit/537.36 (khtml, like gecko) chrome/79.0.3945.88 safari/537.36', + timestamp: '1650625078', + event_conversion_type: 'OFFLINE', + pixel_id: 'dummyPixelId', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + statusCode: 200, + }, + ], + }, + }, + }, ].map((tc) => ({ ...tc, mockFns: (_) => { From d563e64a1b706663153a4df9a7d6eb0f3df45a8a Mon Sep 17 00:00:00 2001 From: Dilip Kola <33080863+koladilip@users.noreply.github.com> Date: Mon, 1 Apr 2024 21:38:11 +0530 Subject: [PATCH 071/240] chore: add destType to http_request_duration stats (#3217) --- package-lock.json | 135 +++++++++++++++++++++++++---------------- package.json | 4 +- src/middleware.js | 2 + src/util/prometheus.js | 2 +- 4 files changed, 87 insertions(+), 56 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5701e64a62..463f86ab61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,8 +19,8 @@ "@koa/router": "^12.0.0", "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.6", - "@rudderstack/integrations-lib": "^0.2.4", - "@rudderstack/workflow-engine": "^0.7.2", + "@rudderstack/integrations-lib": "^0.2.7", + "@rudderstack/workflow-engine": "^0.7.5", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", @@ -4446,33 +4446,35 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@pyroscope/nodejs": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@pyroscope/nodejs/-/nodejs-0.2.6.tgz", - "integrity": "sha512-F37ROH//HzO7zKm2S7CtNG8OAp+i4ADg4erQR9D57BrSgi8+3Jjp5s5PWqyJABC6IzsABgGrentPobBDr8QdsA==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pyroscope/nodejs/-/nodejs-0.2.9.tgz", + "integrity": "sha512-pIw4pIqcNZTZxTUuV0OUI18UZEmx9lT2GaT75ny6FKVe2L1gxAwTCf5TKk8VsnUGY66buUkyaTHcTm7fy0BP/Q==", "dependencies": { - "axios": "^0.26.1", + "axios": "^0.28.0", "debug": "^4.3.3", "form-data": "^4.0.0", - "pprof": "^3.2.0", + "pprof": "^4.0.0", "regenerator-runtime": "^0.13.11", "source-map": "^0.7.3" }, "engines": { - "node": "^12.20.0 || >=14.13.1" + "node": ">=v18" } }, "node_modules/@pyroscope/nodejs/node_modules/axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.28.0.tgz", + "integrity": "sha512-Tu7NYoGY4Yoc7I+Npf9HhUMtEEpV7ZiLH9yndTCoNhcpBH0kwcvFbzYN9/u5QKI5A6uefjsNNWaz5olJVYS62Q==", "dependencies": { - "follow-redirects": "^1.14.8" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/@rudderstack/integrations-lib": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@rudderstack/integrations-lib/-/integrations-lib-0.2.4.tgz", - "integrity": "sha512-32Zose9aOPNWd4EyUNuS5YY+Vq4LYMuDcabJ+s3t1ZfHHMfISlDNF02b60MWgOrU8PARYC+siDs5wgA6xfZpzQ==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@rudderstack/integrations-lib/-/integrations-lib-0.2.7.tgz", + "integrity": "sha512-F0QVIT2vpSeI+GcUk7AwxMJrmM5SsRk8AS6oH4nHkjjfDoKjdh9rrDVzhXKUYF//FAi32ecmSsW+/6ioB8louw==", "dependencies": { "axios": "^1.4.0", "axios-mock-adapter": "^1.22.0", @@ -4496,13 +4498,13 @@ "integrity": "sha512-+iH40g+ZA2ANgwjOITdEdZJLZV+ljR28Akn/dRoDia591tMu7PptyvDaAvl+m1DijWXddpLQ8SX9xaEcIdmqlw==" }, "node_modules/@rudderstack/workflow-engine": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.7.2.tgz", - "integrity": "sha512-aXQvoXMekvXxxDG6Yc5P5l3PJIwqVA+EmJ2w4SnQ94BUHhbsybPjgGvyzD17MUTAdWEOtqS38SuzLflBs/5T4g==", + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.7.5.tgz", + "integrity": "sha512-HmhxiF/gZorrEEmVvQYopIN6xicQ7kr0mHtw2fPqXmHIFLr9MnEyefo4+MPw/Re9iNFbXNQC9uKkYd7lLHbAyw==", "dependencies": { "@aws-crypto/sha256-js": "^5.0.0", "@rudderstack/json-template-engine": "^0.8.4", - "jsonata": "^2.0.3", + "jsonata": "^2.0.4", "lodash": "^4.17.21", "object-sizeof": "^2.6.3", "yaml": "^2.3.2" @@ -8907,9 +8909,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "engines": { "node": ">=8" } @@ -10498,9 +10500,9 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -14513,9 +14515,9 @@ } }, "node_modules/jsonata": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/jsonata/-/jsonata-2.0.3.tgz", - "integrity": "sha512-Up2H81MUtjqI/dWwWX7p4+bUMfMrQJVMN/jW6clFMTiYP528fBOBNtRu944QhKTs3+IsVWbgMeUTny5fw2VMUA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/jsonata/-/jsonata-2.0.4.tgz", + "integrity": "sha512-vfavX4/G/yrYxE+UrmT/oUJ3ph7KqUrb0R7b0LVRcntQwxw+Z5kA1pNUIQzX5hF04Oe1eKxyoIPsmXtc2LgJTQ==", "engines": { "node": ">= 8" } @@ -15274,6 +15276,11 @@ "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", "dev": true }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, "node_modules/lodash.startcase": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", @@ -16522,9 +16529,9 @@ "dev": true }, "node_modules/nan": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", - "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==" + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", + "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==" }, "node_modules/nanoid": { "version": "3.3.7", @@ -17523,24 +17530,23 @@ "dev": true }, "node_modules/pprof": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/pprof/-/pprof-3.2.1.tgz", - "integrity": "sha512-KnextTM3EHQ2zqN8fUjB0VpE+njcVR7cOfo7DjJSLKzIbKTPelDtokI04ScR/Vd8CLDj+M99tsaKV+K6FHzpzA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pprof/-/pprof-4.0.0.tgz", + "integrity": "sha512-Yhfk7Y0G1MYsy97oXxmSG5nvbM1sCz9EALiNhW/isAv5Xf7svzP+1RfGeBlS6mLSgRJvgSLh6Mi5DaisQuPttw==", "hasInstallScript": true, "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.0", + "@mapbox/node-pre-gyp": "^1.0.9", "bindings": "^1.2.1", "delay": "^5.0.0", "findit2": "^2.2.3", - "nan": "^2.14.0", + "nan": "^2.17.0", "p-limit": "^3.0.0", - "pify": "^5.0.0", "protobufjs": "~7.2.4", - "source-map": "^0.7.3", + "source-map": "~0.8.0-beta.0", "split": "^1.0.1" }, "engines": { - "node": ">=10.4.1" + "node": ">=14.0.0" } }, "node_modules/pprof-format": { @@ -17548,15 +17554,38 @@ "resolved": "https://registry.npmjs.org/pprof-format/-/pprof-format-2.0.7.tgz", "integrity": "sha512-1qWaGAzwMpaXJP9opRa23nPnt2Egi7RMNoNBptEE/XwHbcn4fC2b/4U4bKc5arkGkIh2ZabpF2bEb+c5GNHEKA==" }, - "node_modules/pprof/node_modules/pify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", - "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", - "engines": { - "node": ">=10" + "node_modules/pprof/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "dependencies": { + "whatwg-url": "^7.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 8" + } + }, + "node_modules/pprof/node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/pprof/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "node_modules/pprof/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" } }, "node_modules/precinct": { @@ -17966,9 +17995,9 @@ } }, "node_modules/protobufjs": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", - "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", + "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -19889,9 +19918,9 @@ } }, "node_modules/tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", diff --git a/package.json b/package.json index 5c1d9c1848..6fb75c3e39 100644 --- a/package.json +++ b/package.json @@ -64,8 +64,8 @@ "@koa/router": "^12.0.0", "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.6", - "@rudderstack/integrations-lib": "^0.2.4", - "@rudderstack/workflow-engine": "^0.7.2", + "@rudderstack/integrations-lib": "^0.2.7", + "@rudderstack/workflow-engine": "^0.7.5", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", diff --git a/src/middleware.js b/src/middleware.js index 53aabc90e3..543b3af8d1 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -1,4 +1,5 @@ const Pyroscope = require('@pyroscope/nodejs'); +const { getDestTypeFromContext } = require('@rudderstack/integrations-lib'); const stats = require('./util/stats'); function initPyroscope() { @@ -26,6 +27,7 @@ function durationMiddleware() { method: ctx.method, code: ctx.status, route: ctx.request.url, + destType: getDestTypeFromContext(ctx), }; stats.timing('http_request_duration', startTime, labels); }; diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 09d0359b5d..882dff9e75 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -611,7 +611,7 @@ class Prometheus { name: 'http_request_duration', help: 'Incoming HTTP requests duration in seconds', type: 'histogram', - labelNames: ['method', 'route', 'code'], + labelNames: ['method', 'route', 'code', 'destType'], }, { name: 'tp_batch_size', From 042dd6d16236dede8126984ea590fa167d4a4a87 Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Tue, 2 Apr 2024 09:35:23 +0530 Subject: [PATCH 072/240] fix: merge conflict with main (#3233) fix: deployment file paths (#3216) Signed-off-by: Sai Sankeerth Co-authored-by: Sai Sankeerth --- .github/workflows/prepare-for-staging-deploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/prepare-for-staging-deploy.yml b/.github/workflows/prepare-for-staging-deploy.yml index 200ea51fff..46cd731d19 100644 --- a/.github/workflows/prepare-for-staging-deploy.yml +++ b/.github/workflows/prepare-for-staging-deploy.yml @@ -116,6 +116,7 @@ jobs: yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" base.yaml yq eval -i ".config-be-user-transformer.image.tag=\"$TAG_NAME\"" base.yaml git add base.yaml + git commit -m "chore: upgrade staging env transformers to \"$TAG_NAME\"" git push -u origin $BRANCH_NAME gh pr create --fill From d93bb9f88fc4f543d6f28299d52be6c1c5b8bb89 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 2 Apr 2024 04:07:06 +0000 Subject: [PATCH 073/240] chore(release): 1.61.0 --- CHANGELOG.md | 21 +++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d3a37e122..12e0da0674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,27 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.61.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.60.0...v1.61.0) (2024-04-02) + + +### Features + +* consent field support for ga4 ([#3213](https://github.com/rudderlabs/rudder-transformer/issues/3213)) ([92515a5](https://github.com/rudderlabs/rudder-transformer/commit/92515a5fd8a2798c48010078f62b360ec6a49979)) +* consent field support for gaoc for API v15 and upgrade the api version from v14 to v16 ([#3121](https://github.com/rudderlabs/rudder-transformer/issues/3121)) ([2aac2a6](https://github.com/rudderlabs/rudder-transformer/commit/2aac2a62547b7a7c617735fc3d6e88e0a1bed76e)), closes [#3190](https://github.com/rudderlabs/rudder-transformer/issues/3190) +* onboard new destination bloomreach ([#3185](https://github.com/rudderlabs/rudder-transformer/issues/3185)) ([d9b7e1f](https://github.com/rudderlabs/rudder-transformer/commit/d9b7e1f70565d59979aee3e62f60e39edb9a23c7)) +* onboarding linkedin conversion api ([#3194](https://github.com/rudderlabs/rudder-transformer/issues/3194)) ([eb7b197](https://github.com/rudderlabs/rudder-transformer/commit/eb7b197322c617b14c2579de8cb4d4dacf8e1df3)) +* update movable ink batch size ([#3223](https://github.com/rudderlabs/rudder-transformer/issues/3223)) ([667095f](https://github.com/rudderlabs/rudder-transformer/commit/667095fa8316cd95a066f15b848ad503c6b4af80)) + + +### Bug Fixes + +* deployment file paths ([#3216](https://github.com/rudderlabs/rudder-transformer/issues/3216)) ([808727d](https://github.com/rudderlabs/rudder-transformer/commit/808727de17e400ed102a843ab3b30f81f8900f24)) +* fixed userId mapping, now mapping to uid instead of id ([#3192](https://github.com/rudderlabs/rudder-transformer/issues/3192)) ([70a468b](https://github.com/rudderlabs/rudder-transformer/commit/70a468bf16ecd5ee0b6fecee4b837895d19c525f)) +* merge conflict with main ([#3233](https://github.com/rudderlabs/rudder-transformer/issues/3233)) ([042dd6d](https://github.com/rudderlabs/rudder-transformer/commit/042dd6d16236dede8126984ea590fa167d4a4a87)), closes [#3216](https://github.com/rudderlabs/rudder-transformer/issues/3216) +* ninetailed: remove page support ([#3218](https://github.com/rudderlabs/rudder-transformer/issues/3218)) ([2f30c56](https://github.com/rudderlabs/rudder-transformer/commit/2f30c56af62e983d09b5d4f2da9a0ba22f5c1612)) +* shopify invalid_event metric prometheus label ([#3200](https://github.com/rudderlabs/rudder-transformer/issues/3200)) ([345c87d](https://github.com/rudderlabs/rudder-transformer/commit/345c87d7c530c621ae3fd6c504d64e5a14e31f22)) +* update correct staging deployment file ([#3189](https://github.com/rudderlabs/rudder-transformer/issues/3189)) ([ac7e887](https://github.com/rudderlabs/rudder-transformer/commit/ac7e8879fcd230dae09f757a759fb42e4cdc09d0)) + ## [1.60.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.60.0) (2024-03-20) diff --git a/package-lock.json b/package-lock.json index 463f86ab61..6d708b6f51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.60.0", + "version": "1.61.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.60.0", + "version": "1.61.0", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index 6fb75c3e39..1f933cd1fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.60.0", + "version": "1.61.0", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From 4651af42982fd82541b9cacd2f79101a9b977e97 Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Tue, 2 Apr 2024 10:30:14 +0530 Subject: [PATCH 074/240] chore:update mapping name value --- .../destinations/snapchat_conversion/processor/data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integrations/destinations/snapchat_conversion/processor/data.ts b/test/integrations/destinations/snapchat_conversion/processor/data.ts index 7f01dbac26..7de7ed9b8d 100644 --- a/test/integrations/destinations/snapchat_conversion/processor/data.ts +++ b/test/integrations/destinations/snapchat_conversion/processor/data.ts @@ -4671,7 +4671,7 @@ export const data = [ rudderEventsToSnapEvents: [ { from: 'Custom Event', - to: 'LEVEL_COMPLETE', + to: 'level_complete', }, ], }, From bcde02127cdbc1a2cb370a645f8dc88b66557b95 Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Tue, 2 Apr 2024 11:07:59 +0530 Subject: [PATCH 075/240] chore: cleanup changelogs --- CHANGELOG.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12e0da0674..64257f99ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,12 +16,9 @@ All notable changes to this project will be documented in this file. See [standa ### Bug Fixes -* deployment file paths ([#3216](https://github.com/rudderlabs/rudder-transformer/issues/3216)) ([808727d](https://github.com/rudderlabs/rudder-transformer/commit/808727de17e400ed102a843ab3b30f81f8900f24)) * fixed userId mapping, now mapping to uid instead of id ([#3192](https://github.com/rudderlabs/rudder-transformer/issues/3192)) ([70a468b](https://github.com/rudderlabs/rudder-transformer/commit/70a468bf16ecd5ee0b6fecee4b837895d19c525f)) -* merge conflict with main ([#3233](https://github.com/rudderlabs/rudder-transformer/issues/3233)) ([042dd6d](https://github.com/rudderlabs/rudder-transformer/commit/042dd6d16236dede8126984ea590fa167d4a4a87)), closes [#3216](https://github.com/rudderlabs/rudder-transformer/issues/3216) * ninetailed: remove page support ([#3218](https://github.com/rudderlabs/rudder-transformer/issues/3218)) ([2f30c56](https://github.com/rudderlabs/rudder-transformer/commit/2f30c56af62e983d09b5d4f2da9a0ba22f5c1612)) * shopify invalid_event metric prometheus label ([#3200](https://github.com/rudderlabs/rudder-transformer/issues/3200)) ([345c87d](https://github.com/rudderlabs/rudder-transformer/commit/345c87d7c530c621ae3fd6c504d64e5a14e31f22)) -* update correct staging deployment file ([#3189](https://github.com/rudderlabs/rudder-transformer/issues/3189)) ([ac7e887](https://github.com/rudderlabs/rudder-transformer/commit/ac7e8879fcd230dae09f757a759fb42e4cdc09d0)) ## [1.60.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.60.0) (2024-03-20) From 476b401d204544c4e146ab0ea806127b0828d1a6 Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Tue, 2 Apr 2024 11:13:39 +0530 Subject: [PATCH 076/240] chore: update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64257f99ea..8cd19561b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file. See [standa ### Features * consent field support for ga4 ([#3213](https://github.com/rudderlabs/rudder-transformer/issues/3213)) ([92515a5](https://github.com/rudderlabs/rudder-transformer/commit/92515a5fd8a2798c48010078f62b360ec6a49979)) -* consent field support for gaoc for API v15 and upgrade the api version from v14 to v16 ([#3121](https://github.com/rudderlabs/rudder-transformer/issues/3121)) ([2aac2a6](https://github.com/rudderlabs/rudder-transformer/commit/2aac2a62547b7a7c617735fc3d6e88e0a1bed76e)), closes [#3190](https://github.com/rudderlabs/rudder-transformer/issues/3190) +* consent field support for gaoc and upgrade the api version from v14 to v16 ([#3121](https://github.com/rudderlabs/rudder-transformer/issues/3121)) ([2aac2a6](https://github.com/rudderlabs/rudder-transformer/commit/2aac2a62547b7a7c617735fc3d6e88e0a1bed76e)), closes [#3190](https://github.com/rudderlabs/rudder-transformer/issues/3190) * onboard new destination bloomreach ([#3185](https://github.com/rudderlabs/rudder-transformer/issues/3185)) ([d9b7e1f](https://github.com/rudderlabs/rudder-transformer/commit/d9b7e1f70565d59979aee3e62f60e39edb9a23c7)) * onboarding linkedin conversion api ([#3194](https://github.com/rudderlabs/rudder-transformer/issues/3194)) ([eb7b197](https://github.com/rudderlabs/rudder-transformer/commit/eb7b197322c617b14c2579de8cb4d4dacf8e1df3)) * update movable ink batch size ([#3223](https://github.com/rudderlabs/rudder-transformer/issues/3223)) ([667095f](https://github.com/rudderlabs/rudder-transformer/commit/667095fa8316cd95a066f15b848ad503c6b4af80)) From aaddac148350edc092675377981098ce63f2771e Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Tue, 2 Apr 2024 19:06:13 +0530 Subject: [PATCH 077/240] chore: no success should return empty array for linkedin ads (#3238) chore: no success should return empty array --- .../v2/destinations/linkedin_ads/rtWorkflow.yaml | 13 +++++-------- src/cdk/v2/destinations/linkedin_ads/utils.js | 3 +++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cdk/v2/destinations/linkedin_ads/rtWorkflow.yaml b/src/cdk/v2/destinations/linkedin_ads/rtWorkflow.yaml index 8b81790de2..dda322e45e 100644 --- a/src/cdk/v2/destinations/linkedin_ads/rtWorkflow.yaml +++ b/src/cdk/v2/destinations/linkedin_ads/rtWorkflow.yaml @@ -1,6 +1,8 @@ bindings: - path: ./utils - path: ./config + - name: handleRtTfSingleEventError + path: ../../../../v0/util/index steps: - name: validateInput @@ -23,14 +25,9 @@ steps: })[] - name: failedEvents template: | - $.outputs.transform#idx.error.({ - "metadata": ^[idx].metadata[], - "destination": ^[idx].destination, - "batched": false, - "statusCode": .status, - "error": .message, - "statTags": .originalError.statTags - })[] + $.outputs.transform#idx.error.( + $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) + )[] - name: batchSuccessfulEvents description: Batches the successfulEvents diff --git a/src/cdk/v2/destinations/linkedin_ads/utils.js b/src/cdk/v2/destinations/linkedin_ads/utils.js index f3f7783962..69fea4299d 100644 --- a/src/cdk/v2/destinations/linkedin_ads/utils.js +++ b/src/cdk/v2/destinations/linkedin_ads/utils.js @@ -162,6 +162,9 @@ const fetchAndVerifyConversionHappenedAt = (message) => { }; function batchResponseBuilder(successfulEvents) { + if (successfulEvents.length === 0) { + return []; + } const constants = { version: successfulEvents[0].message[0].version, type: successfulEvents[0].message[0].type, From a96d954d348e63464acff3756f0a0006eac6cd1c Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Wed, 3 Apr 2024 10:50:35 +0530 Subject: [PATCH 078/240] chore: update CHANGELOG.md with snapchat changes --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cd19561b1..b7a83098ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ All notable changes to this project will be documented in this file. See [standa * fixed userId mapping, now mapping to uid instead of id ([#3192](https://github.com/rudderlabs/rudder-transformer/issues/3192)) ([70a468b](https://github.com/rudderlabs/rudder-transformer/commit/70a468bf16ecd5ee0b6fecee4b837895d19c525f)) * ninetailed: remove page support ([#3218](https://github.com/rudderlabs/rudder-transformer/issues/3218)) ([2f30c56](https://github.com/rudderlabs/rudder-transformer/commit/2f30c56af62e983d09b5d4f2da9a0ba22f5c1612)) * shopify invalid_event metric prometheus label ([#3200](https://github.com/rudderlabs/rudder-transformer/issues/3200)) ([345c87d](https://github.com/rudderlabs/rudder-transformer/commit/345c87d7c530c621ae3fd6c504d64e5a14e31f22)) +* fix: snapchat conversion: add event level_complete ([#3231](https://github.com/rudderlabs/rudder-transformer/issues/3231)) ([39368a0](https://github.com/rudderlabs/rudder-transformer/commit/39368a09e48acc324faa855186bc623e5c347881)) ## [1.60.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.60.0) (2024-03-20) From db55624703f00f4e4716d95cddff3b097267161f Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Wed, 3 Apr 2024 12:29:40 +0530 Subject: [PATCH 079/240] chore: revert "fix: fixed userId mapping, now mapping to uid instead of id" (#3243) --- src/cdk/v2/destinations/fullstory/procWorkflow.yaml | 2 +- test/integrations/destinations/fullstory/processor/data.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdk/v2/destinations/fullstory/procWorkflow.yaml b/src/cdk/v2/destinations/fullstory/procWorkflow.yaml index 26da955623..1a54e8688c 100644 --- a/src/cdk/v2/destinations/fullstory/procWorkflow.yaml +++ b/src/cdk/v2/destinations/fullstory/procWorkflow.yaml @@ -76,7 +76,7 @@ steps: "use_most_recent": .message.properties.useMostRecent, }; $.context.payload.user = { - "uid": .message.properties.userId ?? .message.userId, + "id": .message.properties.userId ?? .message.userId, } - name: cleanPayload diff --git a/test/integrations/destinations/fullstory/processor/data.ts b/test/integrations/destinations/fullstory/processor/data.ts index 9c8d29c7e8..d206b4a84f 100644 --- a/test/integrations/destinations/fullstory/processor/data.ts +++ b/test/integrations/destinations/fullstory/processor/data.ts @@ -149,7 +149,7 @@ export const data = [ id: 's001', }, user: { - uid: 'u001', + id: 'u001', }, }, JSON_ARRAY: {}, From 626e771881f5857995ad3748f8d62bc7afd949fa Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 3 Apr 2024 07:01:18 +0000 Subject: [PATCH 080/240] chore(release): 1.61.1 --- CHANGELOG.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7a83098ce..1670fa232d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [1.61.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.61.0...v1.61.1) (2024-04-03) + ## [1.61.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.60.0...v1.61.0) (2024-04-02) diff --git a/package-lock.json b/package-lock.json index 6d708b6f51..a223d51eec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.61.0", + "version": "1.61.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.61.0", + "version": "1.61.1", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index 1f933cd1fa..84a0a1ad14 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.61.0", + "version": "1.61.1", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From be041a649b4447e352a531ac8da59814c3c66dc5 Mon Sep 17 00:00:00 2001 From: krishnachaitanya Date: Wed, 3 Apr 2024 12:39:49 +0530 Subject: [PATCH 081/240] chore: update slack mention --- .github/workflows/publish-new-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 233e99577d..15d7b20fd1 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -99,7 +99,7 @@ jobs: channel-id: ${{ secrets.SLACK_RELEASE_CHANNEL_ID }} payload: | { - "text": "*<${{env.RELEASES_URL}}v${{ steps.extract-version.outputs.release_version }}|v${{ steps.extract-version.outputs.release_version }}>*\nCC: <@U03KG4BK1L1> <@U02AE5GMMHV> <@U01LVJ30QEB>", + "text": "*<${{env.RELEASES_URL}}v${{ steps.extract-version.outputs.release_version }}|v${{ steps.extract-version.outputs.release_version }}>*\nCC: <@U03KG4BK1L1> <@U024YF8CR53> <@U01LVJ30QEB>", "blocks": [ { "type": "header", @@ -115,7 +115,7 @@ jobs: "type": "section", "text": { "type": "mrkdwn", - "text": "*<${{env.RELEASES_URL}}v${{ steps.extract-version.outputs.release_version }}|v${{ steps.extract-version.outputs.release_version }}>*\nCC: <@U03KG4BK1L1> <@U02AE5GMMHV> <@U01LVJ30QEB>" + "text": "*<${{env.RELEASES_URL}}v${{ steps.extract-version.outputs.release_version }}|v${{ steps.extract-version.outputs.release_version }}>*\nCC: <@U03KG4BK1L1> <@U024YF8CR53> <@U01LVJ30QEB>" } } ] From 5d654b326b8a2e39413f9541da541ab86b2a56fa Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Wed, 3 Apr 2024 14:59:11 +0530 Subject: [PATCH 082/240] fix: email mappings --- .../v2/destinations/bluecore/data/bluecoreCommonConfig.json | 2 +- src/v0/destinations/active_campaign/data/ACIdentify.json | 2 +- src/v0/destinations/blueshift/data/blueshiftGroupConfig.json | 2 +- .../destinations/blueshift/data/blueshiftIdentifyConfig.json | 2 +- src/v0/destinations/blueshift/data/blueshiftTrackConfig.json | 2 +- src/v0/destinations/custify/data/CUSTIFYIdentifyConfig.json | 2 +- src/v0/destinations/custify/data/CUSTIFYTrackConfig.json | 2 +- .../data/FbOfflineConversionsTrackConfig.json | 2 +- .../freshmarketer/data/FRESHMARKETERIdentifyConfig.json | 2 +- src/v0/destinations/freshsales/data/identifyConfig.json | 2 +- .../google_adwords_enhanced_conversions/data/trackConfig.json | 2 +- src/v0/destinations/impact/data/ImpactConversionConfig.json | 2 +- src/v0/destinations/impact/data/ImpactPageLoadConfig.json | 2 +- src/v0/destinations/klaviyo/data/KlaviyoGroup.json | 2 +- src/v0/destinations/klaviyo/data/KlaviyoIdentify.json | 2 +- src/v0/destinations/klaviyo/data/KlaviyoProfile.json | 2 +- src/v0/destinations/mailjet/data/MailJetIdentifyConfig.json | 2 +- src/v0/destinations/mp/data/MPIdentifyConfig.json | 2 +- .../destinations/pinterest_tag/data/pinterestUserConfig.json | 2 +- src/v0/destinations/revenue_cat/data/RCIdentifyConfig.json | 2 +- src/v0/destinations/rockerbox/data/RockerboxTrackConfig.json | 2 +- src/v0/destinations/sendgrid/data/SendgridIdentify.json | 2 +- .../serenytics/data/SerenyticsIdentifyConfig.json | 2 +- src/v0/destinations/user/data/USERGroupConfig.json | 2 +- src/v0/destinations/user/data/USERIdentifyConfig.json | 2 +- src/v0/destinations/vero/data/VeroIdentifyConfig.json | 2 +- src/v0/destinations/vero/data/VeroTrackConfig.json | 2 +- .../destinations/webengage/data/WEBENGAGEIdentifyConfig.json | 2 +- test/integrations/destinations/blueshift/processor/data.ts | 2 +- .../integrations/destinations/freshmarketer/processor/data.ts | 4 ++-- test/integrations/destinations/freshsales/processor/data.ts | 4 ++-- test/integrations/destinations/mailjet/processor/data.ts | 2 +- 32 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/cdk/v2/destinations/bluecore/data/bluecoreCommonConfig.json b/src/cdk/v2/destinations/bluecore/data/bluecoreCommonConfig.json index be74c7c4b3..536f77a045 100644 --- a/src/cdk/v2/destinations/bluecore/data/bluecoreCommonConfig.json +++ b/src/cdk/v2/destinations/bluecore/data/bluecoreCommonConfig.json @@ -35,7 +35,7 @@ }, { "destKey": "properties.customer.email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/active_campaign/data/ACIdentify.json b/src/v0/destinations/active_campaign/data/ACIdentify.json index 6cee9e72c6..134955d52e 100644 --- a/src/v0/destinations/active_campaign/data/ACIdentify.json +++ b/src/v0/destinations/active_campaign/data/ACIdentify.json @@ -1,7 +1,7 @@ [ { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": true }, diff --git a/src/v0/destinations/blueshift/data/blueshiftGroupConfig.json b/src/v0/destinations/blueshift/data/blueshiftGroupConfig.json index 41e846c509..e246a059d9 100644 --- a/src/v0/destinations/blueshift/data/blueshiftGroupConfig.json +++ b/src/v0/destinations/blueshift/data/blueshiftGroupConfig.json @@ -13,7 +13,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/blueshift/data/blueshiftIdentifyConfig.json b/src/v0/destinations/blueshift/data/blueshiftIdentifyConfig.json index cd099b2cc3..506c313aa1 100644 --- a/src/v0/destinations/blueshift/data/blueshiftIdentifyConfig.json +++ b/src/v0/destinations/blueshift/data/blueshiftIdentifyConfig.json @@ -1,7 +1,7 @@ [ { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": true }, diff --git a/src/v0/destinations/blueshift/data/blueshiftTrackConfig.json b/src/v0/destinations/blueshift/data/blueshiftTrackConfig.json index 5fb1be691a..9841450754 100644 --- a/src/v0/destinations/blueshift/data/blueshiftTrackConfig.json +++ b/src/v0/destinations/blueshift/data/blueshiftTrackConfig.json @@ -7,7 +7,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/custify/data/CUSTIFYIdentifyConfig.json b/src/v0/destinations/custify/data/CUSTIFYIdentifyConfig.json index 4a7a785e1d..00a8ea0584 100644 --- a/src/v0/destinations/custify/data/CUSTIFYIdentifyConfig.json +++ b/src/v0/destinations/custify/data/CUSTIFYIdentifyConfig.json @@ -7,7 +7,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/custify/data/CUSTIFYTrackConfig.json b/src/v0/destinations/custify/data/CUSTIFYTrackConfig.json index f68e9f18ac..1751c84230 100644 --- a/src/v0/destinations/custify/data/CUSTIFYTrackConfig.json +++ b/src/v0/destinations/custify/data/CUSTIFYTrackConfig.json @@ -7,7 +7,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/facebook_offline_conversions/data/FbOfflineConversionsTrackConfig.json b/src/v0/destinations/facebook_offline_conversions/data/FbOfflineConversionsTrackConfig.json index fc5f6c48fa..3f2fa1875f 100644 --- a/src/v0/destinations/facebook_offline_conversions/data/FbOfflineConversionsTrackConfig.json +++ b/src/v0/destinations/facebook_offline_conversions/data/FbOfflineConversionsTrackConfig.json @@ -39,7 +39,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/freshmarketer/data/FRESHMARKETERIdentifyConfig.json b/src/v0/destinations/freshmarketer/data/FRESHMARKETERIdentifyConfig.json index da1a8bccbe..d8abb484b2 100644 --- a/src/v0/destinations/freshmarketer/data/FRESHMARKETERIdentifyConfig.json +++ b/src/v0/destinations/freshmarketer/data/FRESHMARKETERIdentifyConfig.json @@ -1,7 +1,7 @@ [ { "destKey": "emails", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": true }, diff --git a/src/v0/destinations/freshsales/data/identifyConfig.json b/src/v0/destinations/freshsales/data/identifyConfig.json index 9be9d9d855..94a15fd43d 100644 --- a/src/v0/destinations/freshsales/data/identifyConfig.json +++ b/src/v0/destinations/freshsales/data/identifyConfig.json @@ -1,7 +1,7 @@ [ { "destKey": "emails", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": true }, diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/data/trackConfig.json b/src/v0/destinations/google_adwords_enhanced_conversions/data/trackConfig.json index 562a77f0e8..bf5485270b 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/data/trackConfig.json +++ b/src/v0/destinations/google_adwords_enhanced_conversions/data/trackConfig.json @@ -51,7 +51,7 @@ }, { "destKey": "conversionAdjustments[0].userIdentifiers[0].hashedEmail", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false, "metadata": { diff --git a/src/v0/destinations/impact/data/ImpactConversionConfig.json b/src/v0/destinations/impact/data/ImpactConversionConfig.json index 7ae0cc72eb..a898e6a98c 100644 --- a/src/v0/destinations/impact/data/ImpactConversionConfig.json +++ b/src/v0/destinations/impact/data/ImpactConversionConfig.json @@ -120,7 +120,7 @@ }, { "destKey": "CustomerEmail", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/impact/data/ImpactPageLoadConfig.json b/src/v0/destinations/impact/data/ImpactPageLoadConfig.json index 0ca0a26e69..700557d3e0 100644 --- a/src/v0/destinations/impact/data/ImpactPageLoadConfig.json +++ b/src/v0/destinations/impact/data/ImpactPageLoadConfig.json @@ -7,7 +7,7 @@ }, { "destKey": "CustomerEmail", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/klaviyo/data/KlaviyoGroup.json b/src/v0/destinations/klaviyo/data/KlaviyoGroup.json index baf6bcee86..b03cc9ee0a 100644 --- a/src/v0/destinations/klaviyo/data/KlaviyoGroup.json +++ b/src/v0/destinations/klaviyo/data/KlaviyoGroup.json @@ -1,7 +1,7 @@ [ { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/klaviyo/data/KlaviyoIdentify.json b/src/v0/destinations/klaviyo/data/KlaviyoIdentify.json index b358919bc1..fd46b6cda9 100644 --- a/src/v0/destinations/klaviyo/data/KlaviyoIdentify.json +++ b/src/v0/destinations/klaviyo/data/KlaviyoIdentify.json @@ -7,7 +7,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/klaviyo/data/KlaviyoProfile.json b/src/v0/destinations/klaviyo/data/KlaviyoProfile.json index 329ecd978f..03f155e787 100644 --- a/src/v0/destinations/klaviyo/data/KlaviyoProfile.json +++ b/src/v0/destinations/klaviyo/data/KlaviyoProfile.json @@ -1,7 +1,7 @@ [ { "destKey": "$email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/mailjet/data/MailJetIdentifyConfig.json b/src/v0/destinations/mailjet/data/MailJetIdentifyConfig.json index e32463e034..106560a447 100644 --- a/src/v0/destinations/mailjet/data/MailJetIdentifyConfig.json +++ b/src/v0/destinations/mailjet/data/MailJetIdentifyConfig.json @@ -1,7 +1,7 @@ [ { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": true, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/mp/data/MPIdentifyConfig.json b/src/v0/destinations/mp/data/MPIdentifyConfig.json index 679ce66d7f..b5e4f51970 100644 --- a/src/v0/destinations/mp/data/MPIdentifyConfig.json +++ b/src/v0/destinations/mp/data/MPIdentifyConfig.json @@ -7,7 +7,7 @@ }, { "destKey": "$email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/pinterest_tag/data/pinterestUserConfig.json b/src/v0/destinations/pinterest_tag/data/pinterestUserConfig.json index 0ab963ac3c..b95f539e7c 100644 --- a/src/v0/destinations/pinterest_tag/data/pinterestUserConfig.json +++ b/src/v0/destinations/pinterest_tag/data/pinterestUserConfig.json @@ -1,7 +1,7 @@ [ { "destKey": "em", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/revenue_cat/data/RCIdentifyConfig.json b/src/v0/destinations/revenue_cat/data/RCIdentifyConfig.json index f7e6fa7acc..b31f1355a6 100644 --- a/src/v0/destinations/revenue_cat/data/RCIdentifyConfig.json +++ b/src/v0/destinations/revenue_cat/data/RCIdentifyConfig.json @@ -13,7 +13,7 @@ }, { "destKey": "$email.value", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/rockerbox/data/RockerboxTrackConfig.json b/src/v0/destinations/rockerbox/data/RockerboxTrackConfig.json index e405f1477d..cc3a4d5ec5 100644 --- a/src/v0/destinations/rockerbox/data/RockerboxTrackConfig.json +++ b/src/v0/destinations/rockerbox/data/RockerboxTrackConfig.json @@ -12,7 +12,7 @@ "sourceFromGenericMap": false }, { - "sourceKeys": "email", + "sourceKeys": "emailOnly", "destKey": "email", "required": false, "sourceFromGenericMap": true diff --git a/src/v0/destinations/sendgrid/data/SendgridIdentify.json b/src/v0/destinations/sendgrid/data/SendgridIdentify.json index e70378dd28..bfafd0da01 100644 --- a/src/v0/destinations/sendgrid/data/SendgridIdentify.json +++ b/src/v0/destinations/sendgrid/data/SendgridIdentify.json @@ -1,7 +1,7 @@ [ { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": true, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/serenytics/data/SerenyticsIdentifyConfig.json b/src/v0/destinations/serenytics/data/SerenyticsIdentifyConfig.json index 4c50cb4a54..9161a7c698 100644 --- a/src/v0/destinations/serenytics/data/SerenyticsIdentifyConfig.json +++ b/src/v0/destinations/serenytics/data/SerenyticsIdentifyConfig.json @@ -35,7 +35,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/user/data/USERGroupConfig.json b/src/v0/destinations/user/data/USERGroupConfig.json index c87653442d..c31020b68f 100644 --- a/src/v0/destinations/user/data/USERGroupConfig.json +++ b/src/v0/destinations/user/data/USERGroupConfig.json @@ -17,7 +17,7 @@ "required": true }, { - "sourceKeys": "email", + "sourceKeys": "emailOnly", "destKey": "email", "required": false, "sourceFromGenericMap": true diff --git a/src/v0/destinations/user/data/USERIdentifyConfig.json b/src/v0/destinations/user/data/USERIdentifyConfig.json index 2b89c2a3a6..f87bdd01fd 100644 --- a/src/v0/destinations/user/data/USERIdentifyConfig.json +++ b/src/v0/destinations/user/data/USERIdentifyConfig.json @@ -7,7 +7,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/vero/data/VeroIdentifyConfig.json b/src/v0/destinations/vero/data/VeroIdentifyConfig.json index 8ea456cc1c..8ec589aaed 100644 --- a/src/v0/destinations/vero/data/VeroIdentifyConfig.json +++ b/src/v0/destinations/vero/data/VeroIdentifyConfig.json @@ -5,7 +5,7 @@ "sourceFromGenericMap": true }, { - "sourceKeys": "email", + "sourceKeys": "emailOnly", "destKey": "email", "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/vero/data/VeroTrackConfig.json b/src/v0/destinations/vero/data/VeroTrackConfig.json index a3a23a6e66..04974c5eb2 100644 --- a/src/v0/destinations/vero/data/VeroTrackConfig.json +++ b/src/v0/destinations/vero/data/VeroTrackConfig.json @@ -5,7 +5,7 @@ "sourceFromGenericMap": true }, { - "sourceKeys": "email", + "sourceKeys": "emailOnly", "destKey": "identity.email", "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/webengage/data/WEBENGAGEIdentifyConfig.json b/src/v0/destinations/webengage/data/WEBENGAGEIdentifyConfig.json index a99fc47a3f..4e04703fc0 100644 --- a/src/v0/destinations/webengage/data/WEBENGAGEIdentifyConfig.json +++ b/src/v0/destinations/webengage/data/WEBENGAGEIdentifyConfig.json @@ -55,7 +55,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/test/integrations/destinations/blueshift/processor/data.ts b/test/integrations/destinations/blueshift/processor/data.ts index d489a38bd6..be23521621 100644 --- a/test/integrations/destinations/blueshift/processor/data.ts +++ b/test/integrations/destinations/blueshift/processor/data.ts @@ -739,7 +739,7 @@ export const data = [ body: [ { statusCode: 400, - error: 'Missing required value from "email"', + error: 'Missing required value from "emailOnly"', statTags: { errorCategory: 'dataValidation', errorType: 'instrumentation', diff --git a/test/integrations/destinations/freshmarketer/processor/data.ts b/test/integrations/destinations/freshmarketer/processor/data.ts index ed920faef0..07112d762f 100644 --- a/test/integrations/destinations/freshmarketer/processor/data.ts +++ b/test/integrations/destinations/freshmarketer/processor/data.ts @@ -375,7 +375,7 @@ export const data = [ status: 200, body: [ { - error: 'Missing required value from "email"', + error: 'Missing required value from "emailOnly"', statTags: { destType: 'FRESHMARKETER', errorCategory: 'dataValidation', @@ -964,7 +964,7 @@ export const data = [ status: 200, body: [ { - error: 'Missing required value from "email"', + error: 'Missing required value from "emailOnly"', statTags: { destType: 'FRESHMARKETER', errorCategory: 'dataValidation', diff --git a/test/integrations/destinations/freshsales/processor/data.ts b/test/integrations/destinations/freshsales/processor/data.ts index eca3b88d9d..7c0eca0926 100644 --- a/test/integrations/destinations/freshsales/processor/data.ts +++ b/test/integrations/destinations/freshsales/processor/data.ts @@ -505,7 +505,7 @@ export const data = [ status: 200, body: [ { - error: 'Missing required value from "email"', + error: 'Missing required value from "emailOnly"', statTags: { destType: 'FRESHSALES', errorCategory: 'dataValidation', @@ -1094,7 +1094,7 @@ export const data = [ status: 200, body: [ { - error: 'Missing required value from "email"', + error: 'Missing required value from "emailOnly"', statTags: { destType: 'FRESHSALES', errorCategory: 'dataValidation', diff --git a/test/integrations/destinations/mailjet/processor/data.ts b/test/integrations/destinations/mailjet/processor/data.ts index 71e06dc14e..03c3232e72 100644 --- a/test/integrations/destinations/mailjet/processor/data.ts +++ b/test/integrations/destinations/mailjet/processor/data.ts @@ -141,7 +141,7 @@ export const data = [ status: 200, body: [ { - error: 'Missing required value from "email"', + error: 'Missing required value from "emailOnly"', statTags: { destType: 'MAILJET', errorCategory: 'dataValidation', From e18b1c48c9add98440cf7e6b0a996284bffde559 Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Thu, 4 Apr 2024 11:08:16 +0530 Subject: [PATCH 083/240] chore: resolves high packag vulnerabilities (#3202) chore: update @pyroscope/nodejs to 0.2.9 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index a223d51eec..65659a745c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@datadog/pprof": "^3.1.0", "@koa/router": "^12.0.0", "@ndhoule/extend": "^2.0.0", - "@pyroscope/nodejs": "^0.2.6", + "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.7", "@rudderstack/workflow-engine": "^0.7.5", "@shopify/jest-koa-mocks": "^5.1.1", @@ -4462,9 +4462,9 @@ } }, "node_modules/@pyroscope/nodejs/node_modules/axios": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.28.0.tgz", - "integrity": "sha512-Tu7NYoGY4Yoc7I+Npf9HhUMtEEpV7ZiLH9yndTCoNhcpBH0kwcvFbzYN9/u5QKI5A6uefjsNNWaz5olJVYS62Q==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.28.1.tgz", + "integrity": "sha512-iUcGA5a7p0mVb4Gm/sy+FSECNkPFT4y7wt6OM/CDpO/OnNCvSs3PoMG8ibrC9jRoGYU0gUK5pXVC4NPXq6lHRQ==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", diff --git a/package.json b/package.json index 84a0a1ad14..c91d836c34 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@datadog/pprof": "^3.1.0", "@koa/router": "^12.0.0", "@ndhoule/extend": "^2.0.0", - "@pyroscope/nodejs": "^0.2.6", + "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.7", "@rudderstack/workflow-engine": "^0.7.5", "@shopify/jest-koa-mocks": "^5.1.1", From d1a8e2f1a5bbddc11ccfab3261590f5642aff4b5 Mon Sep 17 00:00:00 2001 From: Lokesh Date: Mon, 8 Apr 2024 13:31:51 +0530 Subject: [PATCH 084/240] Add cost tracking labels to openfaas fn pods --- src/util/openfaas/index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/openfaas/index.js b/src/util/openfaas/index.js index 878fa706d9..8a9623d9c8 100644 --- a/src/util/openfaas/index.js +++ b/src/util/openfaas/index.js @@ -27,6 +27,7 @@ const FAAS_AST_VID = 'ast'; const FAAS_AST_FN_NAME = 'fn-ast'; const CUSTOM_NETWORK_POLICY_WORKSPACE_IDS = process.env.CUSTOM_NETWORK_POLICY_WORKSPACE_IDS || ''; const customNetworkPolicyWorkspaceIds = CUSTOM_NETWORK_POLICY_WORKSPACE_IDS.split(','); +const CUSTOMER_TIER = process.env.CUSTOMER_TIER || 'shared'; // Initialise node cache const functionListCache = new NodeCache(); @@ -151,6 +152,10 @@ const deployFaasFunction = async ( 'com.openfaas.scale.min': FAAS_MIN_PODS_IN_TEXT, transformationId: trMetadata.transformationId, workspaceId: trMetadata.workspaceId, + team: 'data-managment', + service: 'openfaas', + customer: 'shared', + 'customer-tier': CUSTOMER_TIER }; if ( trMetadata.workspaceId && From 58aec0a036f10d2020fecf9f8f1f83ee96ff315c Mon Sep 17 00:00:00 2001 From: lokey Date: Mon, 8 Apr 2024 13:43:52 +0530 Subject: [PATCH 085/240] Update src/util/openfaas/index.js Co-authored-by: Jayachand --- src/util/openfaas/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/openfaas/index.js b/src/util/openfaas/index.js index 8a9623d9c8..ff95a93903 100644 --- a/src/util/openfaas/index.js +++ b/src/util/openfaas/index.js @@ -152,8 +152,8 @@ const deployFaasFunction = async ( 'com.openfaas.scale.min': FAAS_MIN_PODS_IN_TEXT, transformationId: trMetadata.transformationId, workspaceId: trMetadata.workspaceId, - team: 'data-managment', - service: 'openfaas', + team: 'data-management', + service: 'openfaas-fn', customer: 'shared', 'customer-tier': CUSTOMER_TIER }; From 218f08c61662950df05946ea0cf59b9e1e0f6b62 Mon Sep 17 00:00:00 2001 From: Jayc Date: Mon, 8 Apr 2024 14:28:28 +0530 Subject: [PATCH 086/240] chore: adding cost labels to openfaas fn pods --- src/util/openfaas/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/openfaas/index.js b/src/util/openfaas/index.js index ff95a93903..00b3720e13 100644 --- a/src/util/openfaas/index.js +++ b/src/util/openfaas/index.js @@ -155,7 +155,7 @@ const deployFaasFunction = async ( team: 'data-management', service: 'openfaas-fn', customer: 'shared', - 'customer-tier': CUSTOMER_TIER + 'customer-tier': CUSTOMER_TIER, }; if ( trMetadata.workspaceId && From 8eb4c4e9b8daebbaeb1d12ff0c17915fe19c2b50 Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Mon, 8 Apr 2024 17:10:16 +0530 Subject: [PATCH 087/240] fix: shopify: send 500 for identifier call in case of failure (#3235) Co-authored-by: Krishna Chaitanya --- src/util/redis/testData/shopify_source.json | 7 ++----- src/v0/sources/shopify/transform.js | 12 ++++-------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/util/redis/testData/shopify_source.json b/src/util/redis/testData/shopify_source.json index 04b80b8fc9..2120475baf 100644 --- a/src/util/redis/testData/shopify_source.json +++ b/src/util/redis/testData/shopify_source.json @@ -65,11 +65,8 @@ } }, "output": { - "outputToSource": { - "body": "T0s=", - "contentType": "text/plain" - }, - "statusCode": 200 + "error": "Error: Error setting value in Redis due Error: Connection is Closed", + "statusCode": 500 } }, { diff --git a/src/v0/sources/shopify/transform.js b/src/v0/sources/shopify/transform.js index 93e3ed0c72..b6d2a4c6cf 100644 --- a/src/v0/sources/shopify/transform.js +++ b/src/v0/sources/shopify/transform.js @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ const lodash = require('lodash'); const get = require('get-value'); +const { RedisError } = require('@rudderstack/integrations-lib'); const stats = require('../../../util/stats'); const { getShopifyTopic, @@ -249,16 +250,11 @@ const processIdentifierEvent = async (event, metricMetadata) => { source: metricMetadata.source, writeKey: metricMetadata.writeKey, }); + // returning 500 as status code in case of redis failure + throw new RedisError(`${e}`, 500); } } - const result = { - outputToSource: { - body: Buffer.from('OK').toString('base64'), - contentType: 'text/plain', - }, - statusCode: 200, - }; - return result; + return NO_OPERATION_SUCCESS; }; const process = async (event) => { const metricMetadata = { From 9daf1c989258bd410d5780c1b11c4f6df9654af5 Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Mon, 8 Apr 2024 18:19:49 +0530 Subject: [PATCH 088/240] fix: hs bugsnag error (#3252) * fix: hs bugsnag error * fix: addressed review comments --- src/v0/destinations/hs/util.js | 2 + .../destinations/hs/processor/data.ts | 53 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/v0/destinations/hs/util.js b/src/v0/destinations/hs/util.js index 359c93dc1a..838da08b3b 100644 --- a/src/v0/destinations/hs/util.js +++ b/src/v0/destinations/hs/util.js @@ -20,6 +20,7 @@ const { getDestinationExternalIDInfoForRetl, getValueFromMessage, isNull, + validateEventName, } = require('../../util'); const { CONTACT_PROPERTY_MAP_ENDPOINT, @@ -435,6 +436,7 @@ const getEventAndPropertiesFromConfig = (message, destination, payload) => { if (!hubspotEvents) { throw new InstrumentationError('Event and property mappings are required for track call'); } + validateEventName(event); event = event.trim().toLowerCase(); let eventName; let eventProperties; diff --git a/test/integrations/destinations/hs/processor/data.ts b/test/integrations/destinations/hs/processor/data.ts index f45f3a719b..0867f2cb54 100644 --- a/test/integrations/destinations/hs/processor/data.ts +++ b/test/integrations/destinations/hs/processor/data.ts @@ -5373,4 +5373,57 @@ export const data = [ }, }, }, + { + name: 'hs', + description: 'if event name is anything other than string we throw error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + traits: { + email: 'testhubspot2@email.com', + firstname: 'Test Hubspot', + }, + }, + type: 'track', + originalTimestamp: '2019-10-15T09:35:31.291Z', + userId: '12345', + event: { name: 'event' }, + properties: { + user_actual_role: 'system_admin, system_user', + user_actual_id: 12345, + }, + sentAt: '2019-10-14T11:15:53.296Z', + }, + destination: destination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Event is a required field and should be a string', + statTags: { + destType: 'HS', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, ]; From ad235e785bf6039e11231a915be098130b25ec3b Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Mon, 8 Apr 2024 18:30:41 +0530 Subject: [PATCH 089/240] feat: for reddit adding currency and value for addToCart, viewConent event as well (#3239) * feat: adding currency and value for addToCart, viewConent event as well * fix: addressed review comments * fix: util test cases update --- .../v2/destinations/reddit/procWorkflow.yaml | 7 +- src/cdk/v2/destinations/reddit/utils.js | 53 ++++++++ src/cdk/v2/destinations/reddit/utils.test.js | 121 ++++++++++++++++++ .../destinations/reddit/processor/data.ts | 6 + .../destinations/reddit/router/data.ts | 8 ++ 5 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 src/cdk/v2/destinations/reddit/utils.test.js diff --git a/src/cdk/v2/destinations/reddit/procWorkflow.yaml b/src/cdk/v2/destinations/reddit/procWorkflow.yaml index 59725c1257..7b989f15e4 100644 --- a/src/cdk/v2/destinations/reddit/procWorkflow.yaml +++ b/src/cdk/v2/destinations/reddit/procWorkflow.yaml @@ -12,6 +12,7 @@ bindings: path: ../../../../v0/util/index - name: OAuthSecretError path: '@rudderstack/integrations-lib' + - path: ./utils steps: - name: validateInput @@ -56,14 +57,14 @@ steps: const event_type = (eventNames.length === 0 || eventNames[0]==="") ? ({"tracking_type": "Custom", "custom_event_name": event}): ({tracking_type: eventNames[0]}); - name: customFields - condition: $.outputs.prepareTrackPayload.eventType.tracking_type === "Purchase" + condition: $.outputs.prepareTrackPayload.eventType.tracking_type in ['Purchase', 'AddToCart', 'ViewContent'] reference: 'https://ads-api.reddit.com/docs/v2/#tag/Conversions/paths/~1api~1v2.0~1conversions~1events~1%7Baccount_id%7D/post' template: | - const revenue_in_cents = .message.properties.revenue ? Math.round(Number(.message.properties.revenue)*100) + const revenue_in_cents = $.populateRevenueField($.outputs.prepareTrackPayload.eventType.tracking_type,^.message.properties) const customFields = .message.().({ "currency": .properties.currency, "value_decimal": revenue_in_cents ? revenue_in_cents / 100, - "item_count": (Array.isArray(.properties.products) && .properties.products.length) || (.properties.itemCount && Number(.properties.itemCount)), + "item_count": $.outputs.prepareTrackPayload.eventType.tracking_type === 'Purchase' ? (Array.isArray(.properties.products) && .properties.products.length) || (.properties.itemCount && Number(.properties.itemCount)) : null, "value": revenue_in_cents, "conversion_id": .properties.conversionId || .messageId, }); diff --git a/src/cdk/v2/destinations/reddit/utils.js b/src/cdk/v2/destinations/reddit/utils.js index c108603235..f562d31313 100644 --- a/src/cdk/v2/destinations/reddit/utils.js +++ b/src/cdk/v2/destinations/reddit/utils.js @@ -28,6 +28,59 @@ const batchEvents = (successfulEvents) => { const batchedEvents = batchEventChunks(eventChunks); return batchedEvents; }; + +const calculateDefaultRevenue = (properties) => { + // Check if working with products array + if (properties?.products && properties.products.length > 0) { + // Check if all product prices are undefined + if (properties.products.every((product) => product.price === undefined)) { + return null; // Return null if all product prices are undefined + } + // Proceed with calculation if not all prices are undefined + return properties.products.reduce( + (acc, product) => acc + (product.price || 0) * (product.quantity || 1), + 0, + ); + } + // For single product scenario, check if price is undefined + if (properties.price === undefined) { + return null; // Return null if price is undefined + } + // Proceed with calculation if price is defined + return properties.price * (properties.quantity ?? 1); +}; + +const populateRevenueField = (eventType, properties) => { + let revenueInCents; + switch (eventType) { + case 'Purchase': + revenueInCents = + properties.revenue && !Number.isNaN(properties.revenue) + ? Math.round(Number(properties?.revenue) * 100) + : null; + break; + case 'AddToCart': + revenueInCents = + properties.price && !Number.isNaN(properties.price) + ? Math.round(Number(properties?.price) * Number(properties?.quantity || 1) * 100) + : null; + break; + default: + // for viewContent + // eslint-disable-next-line no-case-declarations + const revenue = calculateDefaultRevenue(properties); + revenueInCents = revenue ? revenue * 100 : null; + break; + } + + if (lodash.isNaN(revenueInCents)) { + return null; + } + // Return the value as it is if it's not NaN + return revenueInCents; +}; module.exports = { batchEvents, + populateRevenueField, + calculateDefaultRevenue, }; diff --git a/src/cdk/v2/destinations/reddit/utils.test.js b/src/cdk/v2/destinations/reddit/utils.test.js new file mode 100644 index 0000000000..7cfa87e38f --- /dev/null +++ b/src/cdk/v2/destinations/reddit/utils.test.js @@ -0,0 +1,121 @@ +const { calculateDefaultRevenue, populateRevenueField } = require('./utils'); + +describe('calculateDefaultRevenue', () => { + // Calculates revenue for a single product with defined price and quantity + it('should calculate revenue for a single product with defined price and quantity', () => { + const properties = { + price: 10, + quantity: 2, + }; + + const result = calculateDefaultRevenue(properties); + + expect(result).toBe(20); + }); + + // Returns null for properties parameter being undefined + it('should return null for price parameter being undefined', () => { + const properties = { products: [{ quantity: 1 }] }; + + const result = calculateDefaultRevenue(properties); + + expect(result).toBeNull(); + }); + + // Calculates revenue for a single product with defined price and default quantity + it('should calculate revenue for a single product with defined price and default quantity', () => { + const properties = { + price: 10, + }; + + const result = calculateDefaultRevenue(properties); + + expect(result).toBe(10); + }); + + // Calculates revenue for multiple products with defined prices and quantities + it('should calculate revenue for multiple products with defined prices and quantities', () => { + const properties = { + products: [{ price: 10, quantity: 2 }, { quantity: 3 }], + }; + + const result = calculateDefaultRevenue(properties); + + expect(result).toBe(20); + }); + + // Calculates revenue for multiple products with defined prices and default quantities + it('should calculate revenue for multiple products with defined prices and default quantities', () => { + const properties = { + products: [{ price: 10 }, { price: 5 }], + }; + + const result = calculateDefaultRevenue(properties); + + expect(result).toBe(15); + }); +}); + +describe('populateRevenueField', () => { + // Returns revenue in cents for Purchase event type with valid revenue property + it('should return revenue in cents when Purchase event type has valid revenue property', () => { + const eventType = 'Purchase'; + const properties = { + revenue: '10.50', + }; + const expected = 1050; + + const result = populateRevenueField(eventType, properties); + + expect(result).toBe(expected); + }); + + // Returns null for Purchase event type with revenue property as non-numeric string + it('should return null when Purchase event type has revenue property as non-numeric string', () => { + const eventType = 'Purchase'; + const properties = { + revenue: 'invalid', + }; + const expected = null; + + const result = populateRevenueField(eventType, properties); + + expect(result).toBe(expected); + }); + + // Returns revenue in cents for AddToCart event type with valid price and quantity properties + it('should return revenue in cents when AddToCart event type has valid price and quantity properties', () => { + const eventType = 'AddToCart'; + const properties = { + price: '10.50', + quantity: 2, + }; + const expected = 2100; + + const result = populateRevenueField(eventType, properties); + + expect(result).toBe(expected); + }); + + // Returns revenue in cents for ViewContent event type with valid properties + it('should return revenue in cents when ViewContent event type has valid properties', () => { + const eventType = 'ViewContent'; + const properties = { + products: [ + { + price: '10.50', + quantity: 2, + }, + { + price: '5.25', + quantity: 3, + }, + ], + }; + const expected = 3675; + + const result = populateRevenueField(eventType, properties); + + expect(result).toBe(expected); + }); +}); diff --git a/test/integrations/destinations/reddit/processor/data.ts b/test/integrations/destinations/reddit/processor/data.ts index 49e0cd2baa..a97ae23d2a 100644 --- a/test/integrations/destinations/reddit/processor/data.ts +++ b/test/integrations/destinations/reddit/processor/data.ts @@ -443,6 +443,8 @@ export const data = [ }, event_metadata: { item_count: 0, + value: 2600, + value_decimal: 26, products: [ { id: '017c6f5d5cf86a4b22432066', @@ -581,6 +583,8 @@ export const data = [ }, event_metadata: { item_count: 5, + value: 24995, + value_decimal: 249.95, products: [ { id: '622c6f5d5cf86a4c77358033', @@ -847,6 +851,8 @@ export const data = [ }, event_metadata: { item_count: 5, + value: 24995, + value_decimal: 249.95, products: [ { id: '622c6f5d5cf86a4c77358033', diff --git a/test/integrations/destinations/reddit/router/data.ts b/test/integrations/destinations/reddit/router/data.ts index 723afff374..b5bed48ae7 100644 --- a/test/integrations/destinations/reddit/router/data.ts +++ b/test/integrations/destinations/reddit/router/data.ts @@ -90,6 +90,8 @@ export const data = [ properties: { list_id: 'list1', category: "What's New", + value: 2600, + value_decimal: 26, products: [ { product_id: '017c6f5d5cf86a4b22432066', @@ -230,6 +232,8 @@ export const data = [ }, event_metadata: { item_count: 0, + value: 2600, + value_decimal: 26, products: [ { id: '017c6f5d5cf86a4b22432066', @@ -259,6 +263,8 @@ export const data = [ }, event_metadata: { item_count: 5, + value: 24995, + value_decimal: 249.95, products: [ { id: '622c6f5d5cf86a4c77358033', @@ -349,6 +355,8 @@ export const data = [ properties: { list_id: 'list1', category: "What's New", + value: 2600, + value_decimal: 26, products: [ { product_id: '017c6f5d5cf86a4b22432066', From 48cf9f282b803382dd2961b4b834d08a06eaab63 Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Mon, 8 Apr 2024 19:08:58 +0530 Subject: [PATCH 090/240] feat: adding product level tracking for awin (#3246) * feat: adding product level tracking for awin * fix: address review comments --- src/v0/destinations/awin/transform.js | 14 +- src/v0/destinations/awin/utils.js | 51 ++++ src/v0/destinations/awin/utils.test.js | 165 ++++++++++ test/integrations/destinations/awin/data.ts | 315 ++++++++++++++++++++ 4 files changed, 542 insertions(+), 3 deletions(-) create mode 100644 src/v0/destinations/awin/utils.test.js diff --git a/src/v0/destinations/awin/transform.js b/src/v0/destinations/awin/transform.js index 49a115c1ff..0d7fd95c33 100644 --- a/src/v0/destinations/awin/transform.js +++ b/src/v0/destinations/awin/transform.js @@ -2,10 +2,12 @@ const { InstrumentationError, ConfigurationError } = require('@rudderstack/integ const { BASE_URL, ConfigCategory, mappingConfig } = require('./config'); const { defaultRequestConfig, constructPayload, simpleProcessRouterDest } = require('../../util'); -const { getParams } = require('./utils'); +const { getParams, trackProduct } = require('./utils'); const responseBuilder = (message, { Config }) => { const { advertiserId, eventsToTrack } = Config; + const { event, properties } = message; + let finalParams = {}; const payload = constructPayload(message, mappingConfig[ConfigCategory.TRACK.name]); @@ -17,8 +19,14 @@ const responseBuilder = (message, { Config }) => { }); // if the event is present in eventsList - if (eventsList.includes(message.event)) { + if (eventsList.includes(event)) { params = getParams(payload.params, advertiserId); + const productTrackObject = trackProduct(properties, advertiserId, params.parts); + + finalParams = { + ...params, + ...productTrackObject, + }; } else { throw new InstrumentationError( "Event is not present in 'Events to Track' list. Aborting message.", @@ -27,7 +35,7 @@ const responseBuilder = (message, { Config }) => { } } const response = defaultRequestConfig(); - response.params = params; + response.params = finalParams; response.endpoint = BASE_URL; return response; diff --git a/src/v0/destinations/awin/utils.js b/src/v0/destinations/awin/utils.js index 1616227c55..be5f22474d 100644 --- a/src/v0/destinations/awin/utils.js +++ b/src/v0/destinations/awin/utils.js @@ -1,3 +1,5 @@ +const lodash = require('lodash'); + /** * Returns final params * @param {*} params @@ -24,6 +26,55 @@ const getParams = (parameters, advertiserId) => { return params; }; +const areAllValuesDefined = (obj) => + lodash.every(lodash.values(obj), (value) => !lodash.isUndefined(value)); + +const buildProductPayloadString = (payload) => { + // URL-encode each value, and join back with the same key. + const encodedPayload = Object.entries(payload).reduce((acc, [key, value]) => { + // Encode each value. Assuming that all values are either strings or can be + // safely converted to strings. + acc[key] = encodeURIComponent(value); + return acc; + }, {}); + + return `AW:P|${encodedPayload.advertiserId}|${encodedPayload.orderReference}|${encodedPayload.productId}|${encodedPayload.productName}|${encodedPayload.productItemPrice}|${encodedPayload.productQuantity}|${encodedPayload.productSku}|${encodedPayload.commissionGroupCode}|${encodedPayload.productCategory}`; +}; + +// ref: https://wiki.awin.com/index.php/Advertiser_Tracking_Guide/Product_Level_Tracking#PLT_Via_Conversion_Pixel +const trackProduct = (properties, advertiserId, commissionParts) => { + const transformedProductInfoObj = {}; + if ( + properties?.products && + Array.isArray(properties?.products) && + properties.products.length > 0 + ) { + const productsArray = properties.products; + let productIndex = 0; + productsArray.forEach((product) => { + const productPayloadNew = { + advertiserId, + orderReference: properties.order_id || properties.orderId, + productId: product.product_id || product.productId, + productName: product.name, + productItemPrice: product.price, + productQuantity: product.quantity, + productSku: product.sku || '', + commissionGroupCode: commissionParts || 'DEFAULT', + productCategory: product.category || '', + }; + if (areAllValuesDefined(productPayloadNew)) { + transformedProductInfoObj[`bd[${productIndex}]`] = + buildProductPayloadString(productPayloadNew); + productIndex += 1; + } + }); + } + return transformedProductInfoObj; +}; + module.exports = { getParams, + trackProduct, + buildProductPayloadString, }; diff --git a/src/v0/destinations/awin/utils.test.js b/src/v0/destinations/awin/utils.test.js new file mode 100644 index 0000000000..e60c07e96c --- /dev/null +++ b/src/v0/destinations/awin/utils.test.js @@ -0,0 +1,165 @@ +const { buildProductPayloadString, trackProduct } = require('./utils'); + +describe('buildProductPayloadString', () => { + // Should correctly build the payload string with all fields provided + it('should correctly build the payload string with all fields provided', () => { + const payload = { + advertiserId: '123', + orderReference: 'order123', + productId: 'prod123', + productName: 'Product 1', + productItemPrice: '10.99', + productQuantity: '2', + productSku: 'sku123', + commissionGroupCode: 'DEFAULT', + productCategory: 'Category 1', + }; + + const expected = 'AW:P|123|order123|prod123|Product%201|10.99|2|sku123|DEFAULT|Category%201'; + const result = buildProductPayloadString(payload); + + expect(result).toBe(expected); + }); + + // Should correctly handle extremely long string values for all fields + it('should correctly handle extremely long string values for all fields', () => { + const payload = { + advertiserId: '123', + orderReference: 'order123', + productId: 'prod123', + productName: 'Product 1'.repeat(100000), + productItemPrice: '10.99'.repeat(100000), + productQuantity: '2'.repeat(100000), + productSku: 'sku123'.repeat(100000), + commissionGroupCode: 'DEFAULT', + productCategory: 'Category 1'.repeat(100000), + }; + + const expected = `AW:P|123|order123|prod123|${encodeURIComponent('Product 1'.repeat(100000))}|${encodeURIComponent('10.99'.repeat(100000))}|${encodeURIComponent('2'.repeat(100000))}|${encodeURIComponent('sku123'.repeat(100000))}|DEFAULT|${encodeURIComponent('Category 1'.repeat(100000))}`; + const result = buildProductPayloadString(payload); + + expect(result).toBe(expected); + }); +}); + +describe('trackProduct', () => { + // Given a valid 'properties' object with a non-empty 'products' array, it should transform each product into a valid payload string and return an object with the transformed products. + it("should transform each product into a valid payload string and return an object with the transformed products when given a valid 'properties' object with a non-empty 'products' array", () => { + // Arrange + const properties = { + products: [ + { + product_id: '123', + name: 'Product 1', + price: 10, + quantity: 1, + sku: 'SKU123', + category: 'Category 1', + }, + { + product_id: '456', + name: 'Product 2', + price: 20, + quantity: 2, + sku: 'SKU456', + category: 'Category 2', + }, + ], + order_id: 'order123', + }; + const advertiserId = 'advertiser123'; + const commissionParts = 'COMMISSION'; + + // Act + const result = trackProduct(properties, advertiserId, commissionParts); + + // Assert + expect(result).toEqual({ + 'bd[0]': 'AW:P|advertiser123|order123|123|Product%201|10|1|SKU123|COMMISSION|Category%201', + 'bd[1]': 'AW:P|advertiser123|order123|456|Product%202|20|2|SKU456|COMMISSION|Category%202', + }); + }); + + // Given an invalid 'properties' object, it should return an empty object. + it("should return an empty object when given an invalid 'properties' object", () => { + // Arrange + const properties = {}; + const advertiserId = 'advertiser123'; + const commissionParts = 'COMMISSION'; + + // Act + const result = trackProduct(properties, advertiserId, commissionParts); + + // Assert + expect(result).toEqual({}); + }); + + it('should ignore the product which has missing properties', () => { + // Arrange + const properties = { + products: [ + { + price: 10, + quantity: 1, + sku: 'SKU123', + category: 'Category 1', + }, + { + product_id: '456', + name: 'Product 2', + price: 20, + quantity: 2, + sku: 'SKU456', + category: 'Category 2', + }, + ], + order_id: 'order123', + }; + const advertiserId = 'advertiser123'; + const commissionParts = 'COMMISSION'; + + // Act + const result = trackProduct(properties, advertiserId, commissionParts); + + // Assert + expect(result).toEqual({ + 'bd[0]': 'AW:P|advertiser123|order123|456|Product%202|20|2|SKU456|COMMISSION|Category%202', + }); + }); + + it('category and sku if undefined we put blank', () => { + // Arrange + const properties = { + products: [ + { + product_id: '123', + name: 'Product 1', + price: 10, + quantity: 1, + sku: undefined, + category: 'Category 1', + }, + { + product_id: '456', + name: 'Product 2', + price: 20, + quantity: 2, + sku: 'SKU456', + category: undefined, + }, + ], + order_id: 'order123', + }; + const advertiserId = 'advertiser123'; + const commissionParts = 'COMMISSION'; + + // Act + const result = trackProduct(properties, advertiserId, commissionParts); + + // Assert + expect(result).toEqual({ + 'bd[0]': 'AW:P|advertiser123|order123|123|Product%201|10|1||COMMISSION|Category%201', + 'bd[1]': 'AW:P|advertiser123|order123|456|Product%202|20|2|SKU456|COMMISSION|', + }); + }); +}); diff --git a/test/integrations/destinations/awin/data.ts b/test/integrations/destinations/awin/data.ts index 64c8fbc2a1..8ca294b5fb 100644 --- a/test/integrations/destinations/awin/data.ts +++ b/test/integrations/destinations/awin/data.ts @@ -843,4 +843,319 @@ export const data = [ }, }, }, + { + name: 'awin', + description: 'Track call- with product array', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + advertiserId: '1234', + eventsToTrack: [ + { + eventName: 'abc', + }, + { + eventName: 'prop2', + }, + { + eventName: 'prop3', + }, + ], + }, + }, + message: { + type: 'track', + event: 'prop2', + sentAt: '2022-01-20T13:39:21.033Z', + userId: 'user123456001', + channel: 'web', + properties: { + currency: 'INR', + voucherCode: '1bcu1', + amount: 500, + commissionGroup: 'sales', + cks: 'new', + testMode: '1', + order_id: 'QW123', + products: [ + { + product_id: '123', + name: 'Product 1', + price: 10, + quantity: 1, + sku: undefined, + category: 'Category 1', + }, + { + product_id: '456', + name: 'Product 2', + price: 20, + quantity: 2, + sku: 'SKU456', + category: undefined, + }, + ], + }, + context: { + os: { + name: '', + version: '', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.2.20', + namespace: 'com.rudderlabs.javascript', + }, + page: { + url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + path: '/Testing/App_for_LaunchDarkly/ourSdk.html', + title: 'Document', + search: '', + tab_url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + referrer: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/', + initial_referrer: '$direct', + referring_domain: '127.0.0.1:7307', + initial_referring_domain: '', + }, + locale: 'en-US', + screen: { + width: 1440, + height: 900, + density: 2, + innerWidth: 536, + innerHeight: 689, + }, + traits: { + city: 'Pune', + name: 'First User', + email: 'firstUser@testmail.com', + title: 'VP', + gender: 'female', + avatar: 'https://i.pravatar.cc/300', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.2.20', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36', + }, + rudderId: '553b5522-c575-40a7-8072-9741c5f9a647', + messageId: '831f1fa5-de84-4f22-880a-4c3f23fc3f04', + anonymousId: 'bf412108-0357-4330-b119-7305e767823c', + integrations: { + All: true, + }, + originalTimestamp: '2022-01-20T13:39:21.032Z', + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.awin1.com/sread.php', + headers: {}, + params: { + amount: 500, + ch: 'aw', + parts: 'sales:500', + cr: 'INR', + tt: 'ss', + tv: '2', + vc: '1bcu1', + cks: 'new', + merchant: '1234', + testmode: '1', + ref: 'QW123', + 'bd[0]': 'AW:P|1234|QW123|123|Product%201|10|1||sales%3A500|Category%201', + 'bd[1]': 'AW:P|1234|QW123|456|Product%202|20|2|SKU456|sales%3A500|', + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'awin', + description: 'Track call- with product array where important keys might be missing.', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + advertiserId: '1234', + eventsToTrack: [ + { + eventName: 'abc', + }, + { + eventName: 'prop2', + }, + { + eventName: 'prop3', + }, + ], + }, + }, + message: { + type: 'track', + event: 'prop2', + sentAt: '2022-01-20T13:39:21.033Z', + userId: 'user123456001', + channel: 'web', + properties: { + currency: 'INR', + voucherCode: '1bcu1', + amount: 500, + commissionGroup: 'sales', + cks: 'new', + testMode: '1', + order_id: 'QW123', + products: [ + { + price: 10, + quantity: 1, + sku: undefined, + category: 'Category 1', + }, + { + product_id: '456', + name: 'Product 2', + price: 20, + quantity: 2, + sku: 'SKU456', + category: undefined, + }, + ], + }, + context: { + os: { + name: '', + version: '', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.2.20', + namespace: 'com.rudderlabs.javascript', + }, + page: { + url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + path: '/Testing/App_for_LaunchDarkly/ourSdk.html', + title: 'Document', + search: '', + tab_url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + referrer: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/', + initial_referrer: '$direct', + referring_domain: '127.0.0.1:7307', + initial_referring_domain: '', + }, + locale: 'en-US', + screen: { + width: 1440, + height: 900, + density: 2, + innerWidth: 536, + innerHeight: 689, + }, + traits: { + city: 'Pune', + name: 'First User', + email: 'firstUser@testmail.com', + title: 'VP', + gender: 'female', + avatar: 'https://i.pravatar.cc/300', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.2.20', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36', + }, + rudderId: '553b5522-c575-40a7-8072-9741c5f9a647', + messageId: '831f1fa5-de84-4f22-880a-4c3f23fc3f04', + anonymousId: 'bf412108-0357-4330-b119-7305e767823c', + integrations: { + All: true, + }, + originalTimestamp: '2022-01-20T13:39:21.032Z', + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.awin1.com/sread.php', + headers: {}, + params: { + amount: 500, + ch: 'aw', + parts: 'sales:500', + cr: 'INR', + tt: 'ss', + tv: '2', + vc: '1bcu1', + cks: 'new', + merchant: '1234', + testmode: '1', + ref: 'QW123', + 'bd[0]': 'AW:P|1234|QW123|456|Product%202|20|2|SKU456|sales%3A500|', + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, ]; From 3748f24e21634fc74c5e5b3761551c64c8e69942 Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Mon, 8 Apr 2024 23:42:22 +0530 Subject: [PATCH 091/240] feat: rakuten: adding a default value for tr (#3240) * feat: rakuten: adding a default value for tr * chore: add common config * fix: use empty space as default value * chore: address comments --- .../rakuten/data/propertiesMapping.json | 6 +- .../rakuten/processor/commonConfig.ts | 24 ++++ .../destinations/rakuten/processor/track.ts | 126 ++++++++++++++++++ .../processor/transformationFailure.ts | 6 +- 4 files changed, 157 insertions(+), 5 deletions(-) diff --git a/src/cdk/v2/destinations/rakuten/data/propertiesMapping.json b/src/cdk/v2/destinations/rakuten/data/propertiesMapping.json index db5d36fc4d..1a69f53205 100644 --- a/src/cdk/v2/destinations/rakuten/data/propertiesMapping.json +++ b/src/cdk/v2/destinations/rakuten/data/propertiesMapping.json @@ -6,8 +6,10 @@ }, { "sourceKeys": ["properties.tr", "properties.ran_site_id", "properties.ranSiteID"], - "required": true, - "destKey": "tr" + "destKey": "tr", + "metadata": { + "defaultValue": " " + } }, { "sourceKeys": ["properties.land", "properties.land_time", "properties.landTime"], diff --git a/test/integrations/destinations/rakuten/processor/commonConfig.ts b/test/integrations/destinations/rakuten/processor/commonConfig.ts index e7e2af7fbd..96bc6669c2 100644 --- a/test/integrations/destinations/rakuten/processor/commonConfig.ts +++ b/test/integrations/destinations/rakuten/processor/commonConfig.ts @@ -63,3 +63,27 @@ export const commonProperties = { storeId: '12345', storecat: 'Electronics', }; +export const commonPropertiesWithoutRansiteID = { + orderId: 'SampleOrderId', + landTime: '20240129_1200', + date: '20240129_1300', + altord: 'SampleAlternateOrderId', + currency: 'INR', + creditCardType: 'Visa', + commReason: 'SampleCommReason', + isComm: 'Y', + consumed: '20240129_1400', + coupon: 'SampleCoupon', + custId: 'SampleCustomerId', + custScore: 'A', + custStatus: 'New', + dId: 'SampleDeviceId', + disamt: '50.00', + ordStatus: 'Pending', + segment: 'SampleSegment', + shipcountry: 'USA', + shipped: '20240129_1500', + sitename: 'SampleSiteName', + storeId: '12345', + storecat: 'Electronics', +}; diff --git a/test/integrations/destinations/rakuten/processor/track.ts b/test/integrations/destinations/rakuten/processor/track.ts index 49b26e4658..74d09b8d4c 100644 --- a/test/integrations/destinations/rakuten/processor/track.ts +++ b/test/integrations/destinations/rakuten/processor/track.ts @@ -4,6 +4,7 @@ import { commonProperties, endpoint, singleProductWithAllProperties, + commonPropertiesWithoutRansiteID, } from './commonConfig'; import { transformResultBuilder } from '../../../testUtils'; export const trackSuccess = [ @@ -449,4 +450,129 @@ export const trackSuccess = [ }, }, }, + { + id: 'rakuten-test-track-success-5', + name: 'rakuten', + description: 'Track call no ranSiteId in input', + scenario: 'Business', + successCriteria: + 'Response should contain only properties with tr as empty string (ransiteId) and product payload and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'product purchased', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + channel: 'mobile', + rudderId: 'b7b24f86-f7bf-46d8-b2b4-ccafc080239c', + messageId: '1611588776408-ee5a3212-fbf9-4cbb-bbad-3ed0f7c6a2ce', + properties: { + ...commonPropertiesWithoutRansiteID, + products: [ + { ...singleProductWithAllProperties }, + { + sku: 'custom sku 1', + quantity: 5, + amount: 25, + name: 'name_1', + }, + { + sku: 'custom sku 2', + name: 'SampleProduct', + quantity: 1, + amount: 30, + coupon: 'SALE50', + }, + ], + }, + anonymousId: '9c6bd77ea9da3e68', + integrations: { + All: true, + }, + originalTimestamp: '2021-01-25T15:32:56.409Z', + }, + metadata: { + destinationId: 'dummyDestId', + jobId: '1', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + metadata: { + destinationId: 'dummyDestId', + jobId: '1', + }, + output: transformResultBuilder({ + method: 'GET', + endpoint, + headers: commonOutputHeaders, + params: { + mid: 'dummyMarketingId', + xml: 1, + source: 'rudderstack', + amtlist: '2000|2500|3000', + brandlist: 'SampleBrand||', + catidlist: '12345||', + catlist: 'Electronics||', + couponlist: 'SALE20||SALE50', + disamtlist: '10.5||', + distypelist: 'Percentage||', + ismarketplacelist: 'N||', + sequencelist: '123||', + shipbylist: 'Express||', + shipidlist: 'SHIP123||', + qlist: '5|5|1', + marginlist: '0.15||', + markdownlist: '5||', + taxexemptlist: 'N||', + namelist: 'SampleProduct|name_1|SampleProduct', + skulist: 'ABC123|custom sku 1|custom sku 2', + issalelist: 'Y||', + itmstatuslist: 'In Stock||', + isclearancelist: 'Y||', + ord: 'SampleOrderId', + tr: ' ', + land: '20240129_1200', + date: '20240129_1300', + altord: 'SampleAlternateOrderId', + cur: 'INR', + cc: 'Visa', + commreason: 'SampleCommReason', + iscomm: 'Y', + consumed: '20240129_1400', + coupon: 'SampleCoupon', + custid: 'SampleCustomerId', + custscore: 'A', + custstatus: 'New', + did: 'SampleDeviceId', + disamt: '50.00', + ordstatus: 'Pending', + segment: 'SampleSegment', + shipcountry: 'USA', + shipped: '20240129_1500', + sitename: 'SampleSiteName', + storeid: '12345', + storecat: 'Electronics', + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/rakuten/processor/transformationFailure.ts b/test/integrations/destinations/rakuten/processor/transformationFailure.ts index e35ab26b69..5485efae03 100644 --- a/test/integrations/destinations/rakuten/processor/transformationFailure.ts +++ b/test/integrations/destinations/rakuten/processor/transformationFailure.ts @@ -202,9 +202,9 @@ export const transformationFailures = [ { id: 'rakuten-test-5', name: 'rakuten', - description: 'No eligible property available for required field tr present', + description: 'No eligible property available for required field land present', scenario: 'Framework', - successCriteria: 'Transformationn Error for required field tr not present', + successCriteria: 'Transformationn Error for required field land not present', feature: 'processor', module: 'destination', version: 'v0', @@ -245,7 +245,7 @@ export const transformationFailures = [ body: [ { error: - 'Missing required value from ["properties.tr","properties.ran_site_id","properties.ranSiteID"]: Workflow: procWorkflow, Step: prepareTrackPayload, ChildStep: undefined, OriginalError: Missing required value from ["properties.tr","properties.ran_site_id","properties.ranSiteID"]', + 'Missing required value from ["properties.land","properties.land_time","properties.landTime"]: Workflow: procWorkflow, Step: prepareTrackPayload, ChildStep: undefined, OriginalError: Missing required value from ["properties.land","properties.land_time","properties.landTime"]', metadata: { destinationId: 'dummyDestId', jobId: '1', From 9214594bab2c86a4ae6f75e12531f778490cf127 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Tue, 9 Apr 2024 13:18:09 +0530 Subject: [PATCH 092/240] feat: do away myaxios (#3222) * feat: do away myaxios --- src/v0/destinations/sfmc/transform.js | 73 ++-- .../integrations/destinations/sfmc/network.ts | 50 +++ .../destinations/sfmc/processor/data.ts | 322 ++++++++++++++++++ 3 files changed, 401 insertions(+), 44 deletions(-) diff --git a/src/v0/destinations/sfmc/transform.js b/src/v0/destinations/sfmc/transform.js index 53925bc7ed..bf474ff3f0 100644 --- a/src/v0/destinations/sfmc/transform.js +++ b/src/v0/destinations/sfmc/transform.js @@ -7,8 +7,8 @@ const { isDefinedAndNotNull, isEmpty, } = require('@rudderstack/integrations-lib'); -const myAxios = require('../../../util/myAxios'); const { EventType } = require('../../../constants'); +const { handleHttpRequest } = require('../../../adapters/network'); const { CONFIG_CATEGORIES, MAPPING_CONFIG, ENDPOINTS } = require('./config'); const { removeUndefinedAndNullValues, @@ -22,10 +22,8 @@ const { getHashFromArray, simpleProcessRouterDest, } = require('../../util'); -const { - getDynamicErrorType, - nodeSysErrorToStatus, -} = require('../../../adapters/utils/networkUtils'); +const { getDynamicErrorType } = require('../../../adapters/utils/networkUtils'); +const { isHttpStatusSuccess } = require('../../util'); const tags = require('../../util/tags'); const { JSON_MIME_TYPE } = require('../../util/constant'); @@ -34,51 +32,38 @@ const CONTACT_KEY_KEY = 'Contact Key'; // DOC: https://developer.salesforce.com/docs/atlas.en-us.mc-app-development.meta/mc-app-development/access-token-s2s.htm const getToken = async (clientId, clientSecret, subdomain) => { - try { - const resp = await myAxios.post( - `https://${subdomain}.${ENDPOINTS.GET_TOKEN}`, - { - grant_type: 'client_credentials', - client_id: clientId, - client_secret: clientSecret, - }, - { - 'Content-Type': JSON_MIME_TYPE, - }, - { - destType: 'sfmc', - feature: 'transformation', - endpointPath: '/token', - requestMethod: 'POST', - module: 'router', - }, - ); - if (resp && resp.data) { - return resp.data.access_token; - } - const status = resp.status || 400; + const { processedResponse: processedResponseSfmc } = await handleHttpRequest( + 'post', + `https://${subdomain}.${ENDPOINTS.GET_TOKEN}`, + { + grant_type: 'client_credentials', + client_id: clientId, + client_secret: clientSecret, + }, + { + 'Content-Type': JSON_MIME_TYPE, + }, + { + destType: 'sfmc', + feature: 'transformation', + endpointPath: '/token', + requestMethod: 'POST', + module: 'router', + }, + ); + + if (!isHttpStatusSuccess(processedResponseSfmc.status)) { throw new NetworkError( 'Could not retrieve access token', - status, + processedResponseSfmc.status || 400, { - [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(processedResponseSfmc.status || 400), }, - resp, + processedResponseSfmc.response, ); - } catch (error) { - if (!isEmpty(error.response)) { - const status = error.status || 400; - throw new NetworkError(`Authorization Failed ${error.response.statusText}`, status, { - [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), - }); - } else { - const httpError = nodeSysErrorToStatus(error.code); - const status = httpError.status || 400; - throw new NetworkError(`Authorization Failed ${httpError.message}`, status, { - [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), - }); - } } + + return processedResponseSfmc.response.access_token; }; // DOC : https://developer.salesforce.com/docs/atlas.en-us.noversion.mc-apis.meta/mc-apis/createContacts.htm diff --git a/test/integrations/destinations/sfmc/network.ts b/test/integrations/destinations/sfmc/network.ts index 7564d8c6d5..93854e3691 100644 --- a/test/integrations/destinations/sfmc/network.ts +++ b/test/integrations/destinations/sfmc/network.ts @@ -11,4 +11,54 @@ export const networkCallsData = [ }, }, }, + { + httpReq: { + url: 'https://testHandleHttpRequest401.auth.marketingcloudapis.com/v2/token', + method: 'POST', + }, + httpRes: { + status: 401, + data: { + error: 'invalid_client', + error_description: + 'Invalid client ID. Use the client ID in Marketing Cloud Installed Packages.', + error_uri: 'https://developer.salesforce.com/docs', + }, + }, + }, + { + httpReq: { + url: 'https://testHandleHttpRequest429.auth.marketingcloudapis.com/v2/token', + method: 'POST', + }, + httpRes: { + status: 429, + data: { + message: 'Your requests are temporarily blocked.', + errorcode: 50200, + documentation: + 'https://developer.salesforce.com/docs/atlas.en-us.mc-apis.meta/mc-apis/error-handling.htm', + }, + }, + }, + { + httpReq: { + url: 'https://testHandleHttpRequest-dns.auth.marketingcloudapis.com/v2/token', + method: 'POST', + }, + httpRes: { + data: {}, + status: 400, + }, + }, + { + httpReq: { + url: 'https://testHandleHttpRequest-null.auth.marketingcloudapis.com/v2/token', + method: 'POST', + }, + httpRes: { + data: null, + status: 500, + }, + }, ]; diff --git a/test/integrations/destinations/sfmc/processor/data.ts b/test/integrations/destinations/sfmc/processor/data.ts index b2839908ad..883032d223 100644 --- a/test/integrations/destinations/sfmc/processor/data.ts +++ b/test/integrations/destinations/sfmc/processor/data.ts @@ -1894,4 +1894,326 @@ export const data = [ }, }, }, + { + name: 'sfmc', + description: 'Tests 401 un authenticated code from sfmc', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + event: 'message event', + type: 'track', + userId: '12345', + properties: { + id: 'id101', + contactId: 'cid101', + email: 'testemail@gmail.com', + accountNumber: '99110099', + patronName: 'SP', + }, + }, + destination: { + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'SFMC', + DestinationDefinition: { + ID: '1pYpYSeQd8OeN6xPdw6VGDzqUd1', + Name: 'SFMC', + DisplayName: 'Salesforce Marketing Cloud', + Config: { + destConfig: [], + excludeKeys: [], + includeKeys: [], + saveDestinationResponse: false, + supportedSourceTypes: [], + transformAt: 'processor', + }, + ResponseRules: {}, + }, + Config: { + clientId: 'testHandleHttpRequest401', + clientSecret: 'testHandleHttpRequest401', + createOrUpdateContacts: false, + eventDelivery: true, + eventDeliveryTS: 1615371070621, + eventToExternalKey: [ + { + from: 'Event Name', + to: 'C500FD37-155C-49BD-A21B-AFCEF3D1A9CB', + }, + { + from: 'Watch', + to: 'C500FD37-155C-49BD-A21B-AFCEF3D1A9CB', + }, + ], + eventToPrimaryKey: [ + { + from: 'userId', + to: 'User Key', + }, + { + from: 'watch', + to: 'Guest Key, Contact Key', + }, + ], + eventToUUID: [ + { + event: 'Event Name', + uuid: true, + }, + ], + eventToDefinitionMapping: [ + { + from: 'message event', + to: 'test-event-definition', + }, + ], + externalKey: 'f3ffa19b-e0b3-4967-829f-549b781080e6', + subDomain: 'testHandleHttpRequest401', + }, + Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '{"message":"Could not retrieve access token","destinationResponse":{"error":"invalid_client","error_description":"Invalid client ID. Use the client ID in Marketing Cloud Installed Packages.","error_uri":"https://developer.salesforce.com/docs"}}', + statTags: { + destType: 'SFMC', + errorCategory: 'network', + errorType: 'aborted', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 401, + }, + ], + }, + }, + }, + { + name: 'sfmc', + description: 'Tests 429 status code from sfmc', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + event: 'message event', + type: 'track', + userId: '12345', + properties: { + id: 'id101', + contactId: 'cid101', + email: 'testemail@gmail.com', + accountNumber: '99110099', + patronName: 'SP', + }, + }, + destination: { + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'SFMC', + DestinationDefinition: { + ID: '1pYpYSeQd8OeN6xPdw6VGDzqUd1', + Name: 'SFMC', + DisplayName: 'Salesforce Marketing Cloud', + Config: { + destConfig: [], + excludeKeys: [], + includeKeys: [], + saveDestinationResponse: false, + supportedSourceTypes: [], + transformAt: 'processor', + }, + ResponseRules: {}, + }, + Config: { + clientId: 'testHandleHttpRequest429', + clientSecret: 'testHandleHttpRequest429', + subDomain: 'testHandleHttpRequest429', + }, + Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '{"message":"Could not retrieve access token","destinationResponse":{"message":"Your requests are temporarily blocked.","errorcode":50200,"documentation":"https://developer.salesforce.com/docs/atlas.en-us.mc-apis.meta/mc-apis/error-handling.htm"}}', + statTags: { + destType: 'SFMC', + errorCategory: 'network', + errorType: 'throttled', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 429, + }, + ], + }, + }, + }, + { + name: 'sfmc', + description: 'Tests DNS lookup failure for sfmc', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + event: 'message event', + type: 'track', + userId: '12345', + properties: { + id: 'id101', + contactId: 'cid101', + email: 'testemail@gmail.com', + accountNumber: '99110099', + patronName: 'SP', + }, + }, + destination: { + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'SFMC', + DestinationDefinition: { + ID: '1pYpYSeQd8OeN6xPdw6VGDzqUd1', + Name: 'SFMC', + DisplayName: 'Salesforce Marketing Cloud', + Config: { + destConfig: [], + excludeKeys: [], + includeKeys: [], + saveDestinationResponse: false, + supportedSourceTypes: [], + transformAt: 'processor', + }, + ResponseRules: {}, + }, + Config: { + clientId: 'testHandleHttpRequest-dns', + clientSecret: 'testHandleHttpRequest-dns', + subDomain: 'testHandleHttpRequest-dns', + }, + Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: '{"message":"Could not retrieve access token","destinationResponse":{}}', + statTags: { + destType: 'SFMC', + errorCategory: 'network', + errorType: 'aborted', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'sfmc', + description: 'Test 500 status failure for sfmc', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + event: 'message event', + type: 'track', + userId: '12345', + properties: { + id: 'id101', + contactId: 'cid101', + email: 'testemail@gmail.com', + accountNumber: '99110099', + patronName: 'SP', + }, + }, + destination: { + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'SFMC', + DestinationDefinition: { + ID: '1pYpYSeQd8OeN6xPdw6VGDzqUd1', + Name: 'SFMC', + DisplayName: 'Salesforce Marketing Cloud', + Config: { + destConfig: [], + excludeKeys: [], + includeKeys: [], + saveDestinationResponse: false, + supportedSourceTypes: [], + transformAt: 'processor', + }, + ResponseRules: {}, + }, + Config: { + clientId: 'testHandleHttpRequest-null', + clientSecret: 'testHandleHttpRequest-null', + subDomain: 'testHandleHttpRequest-null', + }, + Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Could not retrieve access token', + statTags: { + destType: 'SFMC', + errorCategory: 'network', + errorType: 'retryable', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 500, + }, + ], + }, + }, + }, ]; From 9c6b251a6c784cc391f27e846a008fbe2901e2c8 Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Tue, 9 Apr 2024 17:24:41 +0530 Subject: [PATCH 093/240] fix: fixed userId mapping, now mapping to uid instead of id (#3262) --- src/cdk/v2/destinations/fullstory/procWorkflow.yaml | 2 +- test/integrations/destinations/fullstory/processor/data.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdk/v2/destinations/fullstory/procWorkflow.yaml b/src/cdk/v2/destinations/fullstory/procWorkflow.yaml index 1a54e8688c..26da955623 100644 --- a/src/cdk/v2/destinations/fullstory/procWorkflow.yaml +++ b/src/cdk/v2/destinations/fullstory/procWorkflow.yaml @@ -76,7 +76,7 @@ steps: "use_most_recent": .message.properties.useMostRecent, }; $.context.payload.user = { - "id": .message.properties.userId ?? .message.userId, + "uid": .message.properties.userId ?? .message.userId, } - name: cleanPayload diff --git a/test/integrations/destinations/fullstory/processor/data.ts b/test/integrations/destinations/fullstory/processor/data.ts index d206b4a84f..9c8d29c7e8 100644 --- a/test/integrations/destinations/fullstory/processor/data.ts +++ b/test/integrations/destinations/fullstory/processor/data.ts @@ -149,7 +149,7 @@ export const data = [ id: 's001', }, user: { - id: 'u001', + uid: 'u001', }, }, JSON_ARRAY: {}, From 6e3274bfba9e7838d1f81d845a070427b67e75f5 Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Tue, 9 Apr 2024 17:31:10 +0530 Subject: [PATCH 094/240] fix: marketo bulk ignore null while checking data type mismatch (#3263) --- .../marketo_bulk_upload/marketo_bulk_upload.util.test.js | 6 ++---- src/v0/destinations/marketo_bulk_upload/util.js | 8 ++++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/v0/destinations/marketo_bulk_upload/marketo_bulk_upload.util.test.js b/src/v0/destinations/marketo_bulk_upload/marketo_bulk_upload.util.test.js index aa4b3aacc4..13e1b3a09a 100644 --- a/src/v0/destinations/marketo_bulk_upload/marketo_bulk_upload.util.test.js +++ b/src/v0/destinations/marketo_bulk_upload/marketo_bulk_upload.util.test.js @@ -514,7 +514,7 @@ describe('checkEventStatusViaSchemaMatching', () => { }); // The function correctly handles events with null values. - it('should correctly handle events with null values', () => { + it('should ignore event properties with null values', () => { const event = { input: [ { @@ -537,8 +537,6 @@ describe('checkEventStatusViaSchemaMatching', () => { const result = checkEventStatusViaSchemaMatching(event, fieldSchemaMapping); - expect(result).toEqual({ - job1: 'invalid id', - }); + expect(result).toEqual({}); }); }); diff --git a/src/v0/destinations/marketo_bulk_upload/util.js b/src/v0/destinations/marketo_bulk_upload/util.js index 4c99ba7483..033239b5e4 100644 --- a/src/v0/destinations/marketo_bulk_upload/util.js +++ b/src/v0/destinations/marketo_bulk_upload/util.js @@ -3,6 +3,7 @@ const { RetryableError, NetworkError, TransformationError, + isDefinedAndNotNull, } = require('@rudderstack/integrations-lib'); const { handleHttpRequest } = require('../../../adapters/network'); const tags = require('../../util/tags'); @@ -360,7 +361,6 @@ const getFieldSchemaMap = async (accessToken, munchkinId) => { module: 'router', }, ); - if (fieldSchemaMapping.response.errors) { handleCommonErrorResponse( fieldSchemaMapping, @@ -411,7 +411,11 @@ const checkEventStatusViaSchemaMatching = (event, fieldMap) => { const expectedDataType = SCHEMA_DATA_TYPE_MAP[fieldMap[paramName]]; const actualDataType = typeof paramValue; - if (!mismatchedFields[job_id] && actualDataType !== expectedDataType) { + if ( + isDefinedAndNotNull(paramValue) && + !mismatchedFields[job_id] && + actualDataType !== expectedDataType + ) { mismatchedFields[job_id] = `invalid ${paramName}`; } }); From c204113eab37a782f217488d0d626a8d6df345d3 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Tue, 9 Apr 2024 18:05:33 +0530 Subject: [PATCH 095/240] feat: logger upgrade in services, dest, source (#3228) * feat: logger upgrade in services, dest, source --- .eslintrc.json | 1 + package-lock.json | 43 +---------- package.json | 2 +- src/cdk/v2/handler.ts | 10 ++- src/controllers/bulkUpload.ts | 38 +++------- src/controllers/delivery.ts | 33 ++++---- src/controllers/destination.ts | 59 +++++++------- src/controllers/regulation.ts | 13 ++-- src/controllers/source.ts | 18 ++--- src/controllers/userTransform.ts | 26 +++---- src/index.ts | 14 ++-- src/interfaces/DestinationService.ts | 7 +- src/interfaces/SourceService.ts | 2 + src/services/comparator.ts | 9 ++- .../__tests__/nativeIntegration.test.ts | 11 ++- src/services/destination/cdkV2Integration.ts | 36 +++++---- src/services/destination/nativeIntegration.ts | 76 ++++++++++++------- .../destination/postTransformation.ts | 46 +++++++---- src/services/misc.ts | 22 +++++- .../__tests__/nativeIntegration.test.ts | 23 ++++-- src/services/source/nativeIntegration.ts | 14 ++-- src/util/redis/redisConnector.test.js | 3 +- .../campaign_manager/transform.js | 3 +- src/v0/destinations/mailchimp/utils.js | 7 +- src/v0/destinations/twitter_ads/transform.js | 3 +- src/v0/sources/canny/transform.js | 11 ++- src/v0/sources/shopify/transform.js | 7 +- src/v0/sources/shopify/util.js | 3 +- .../__tests__/pinterestConversion-cdk.test.ts | 25 +++++- test/apitests/service.api.test.ts | 8 +- 30 files changed, 311 insertions(+), 262 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 7258c5c536..556470697d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -26,6 +26,7 @@ "off", { "cases": { "camelCase": true, "pascalCase": true, "kebabCase": true } } ], + "import/no-import-module-exports": "off", "unicorn/no-instanceof-array": "error", "unicorn/no-static-only-class": "error", "unicorn/consistent-destructuring": "error", diff --git a/package-lock.json b/package-lock.json index 65659a745c..fd42692109 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "@koa/router": "^12.0.0", "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", - "@rudderstack/integrations-lib": "^0.2.7", + "@rudderstack/integrations-lib": "^0.2.8", "@rudderstack/workflow-engine": "^0.7.5", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", @@ -118,41 +118,6 @@ "typescript": "^5.0.4" } }, - "../rudder-integrations-lib": { - "name": "@rudderstack/integrations-lib", - "version": "0.1.10", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@rudderstack/workflow-engine": "^0.5.7", - "axios": "^1.4.0", - "axios-mock-adapter": "^1.22.0", - "crypto": "^1.0.1", - "get-value": "^3.0.1", - "handlebars": "^4.7.8", - "lodash": "^4.17.21", - "moment": "^2.29.4", - "moment-timezone": "^0.5.43", - "set-value": "^4.1.0", - "sha256": "^0.2.0", - "tslib": "^2.4.0", - "winston": "^3.11.0" - }, - "devDependencies": { - "@types/get-value": "^3.0.3", - "@types/jest": "^29.5.4", - "@types/lodash": "^4.14.195", - "@types/node": "^20.3.3", - "@types/set-value": "^4.0.1", - "@types/sha256": "^0.2.0", - "jest": "^29.4.3", - "pre-commit": "^1.2.2", - "prettier": "^2.8.4", - "ts-jest": "^29.0.5", - "ts-node": "^10.9.1", - "typescript": "^5.1.6" - } - }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", @@ -4472,9 +4437,9 @@ } }, "node_modules/@rudderstack/integrations-lib": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@rudderstack/integrations-lib/-/integrations-lib-0.2.7.tgz", - "integrity": "sha512-F0QVIT2vpSeI+GcUk7AwxMJrmM5SsRk8AS6oH4nHkjjfDoKjdh9rrDVzhXKUYF//FAi32ecmSsW+/6ioB8louw==", + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@rudderstack/integrations-lib/-/integrations-lib-0.2.8.tgz", + "integrity": "sha512-5CJoFFCRDhG7busCGVktKqEEXO0DbFqJ56TOT+jyDdoTf8sZ7SsSJ4NCZYmSplZrbQGj2R+aArnQnpxA4hPGmA==", "dependencies": { "axios": "^1.4.0", "axios-mock-adapter": "^1.22.0", diff --git a/package.json b/package.json index c91d836c34..c839bc8acc 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@koa/router": "^12.0.0", "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", - "@rudderstack/integrations-lib": "^0.2.7", + "@rudderstack/integrations-lib": "^0.2.8", "@rudderstack/workflow-engine": "^0.7.5", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", diff --git a/src/cdk/v2/handler.ts b/src/cdk/v2/handler.ts index edd14e7298..c437247f74 100644 --- a/src/cdk/v2/handler.ts +++ b/src/cdk/v2/handler.ts @@ -1,9 +1,9 @@ import { - WorkflowEngine, - WorkflowEngineFactory, - TemplateType, ExecutionBindings, StepOutput, + TemplateType, + WorkflowEngine, + WorkflowEngineFactory, } from '@rudderstack/workflow-engine'; import { FixMe } from '../../util/types'; @@ -11,9 +11,9 @@ import tags from '../../v0/util/tags'; import { getErrorInfo, + getPlatformBindingsPaths, getRootPathForDestination, getWorkflowPath, - getPlatformBindingsPaths, isCdkV2Destination, } from './utils'; @@ -82,10 +82,12 @@ export async function processCdkV2Workflow( destType: string, parsedEvent: FixMe, feature: string, + logger: FixMe, requestMetadata: NonNullable = {}, bindings: Record = {}, ) { try { + logger.debug(`Processing cdkV2 workflow`); const workflowEngine = await getCachedWorkflowEngine(destType, feature, bindings); return await executeWorkflow(workflowEngine, parsedEvent, requestMetadata); } catch (error) { diff --git a/src/controllers/bulkUpload.ts b/src/controllers/bulkUpload.ts index dbd77dc07f..28556dd5df 100644 --- a/src/controllers/bulkUpload.ts +++ b/src/controllers/bulkUpload.ts @@ -1,10 +1,10 @@ /* eslint-disable global-require, import/no-dynamic-require, @typescript-eslint/no-unused-vars */ +import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import { client as errNotificationClient } from '../util/errorNotifier'; -import logger from '../logger'; import { + getDestFileUploadHandler, getJobStatusHandler, getPollStatusHandler, - getDestFileUploadHandler, } from '../util/fetchDestinationHandlers'; import { CatchErr, ContextBodySimple } from '../util/types'; // TODO: To be refactored and redisgned @@ -31,10 +31,7 @@ const getReqMetadata = (ctx) => { }; export const fileUpload = async (ctx) => { - logger.debug( - 'Native(Bulk-Upload): Request to transformer:: /fileUpload route', - JSON.stringify(ctx.request.body), - ); + logger.debug('Native(Bulk-Upload): Request to transformer:: /fileUpload route', ctx.request.body); const getReqMetadataFileUpload = () => { try { const reqBody = ctx.request.body; @@ -69,18 +66,12 @@ export const fileUpload = async (ctx) => { }); } ctx.body = response; - logger.debug( - 'Native(Bulk-Upload): Response from transformer:: /fileUpload route', - JSON.stringify(ctx.body), - ); + logger.debug('Native(Bulk-Upload): Response from transformer:: /fileUpload route', ctx.body); return ctx.body; }; export const pollStatus = async (ctx) => { - logger.debug( - 'Native(Bulk-Upload): Request to transformer:: /pollStatus route', - JSON.stringify(ctx.request.body), - ); + logger.debug('Native(Bulk-Upload): Request to transformer:: /pollStatus route', ctx.request.body); const { destType }: ContextBodySimple = ctx.request.body; const destFileUploadHandler = getPollStatusHandler('v0', destType.toLowerCase()); @@ -104,17 +95,14 @@ export const pollStatus = async (ctx) => { }); } ctx.body = response; - logger.debug( - 'Native(Bulk-Upload): Request from transformer:: /pollStatus route', - JSON.stringify(ctx.body), - ); + logger.debug('Native(Bulk-Upload): Request from transformer:: /pollStatus route', ctx.body); return ctx.body; }; export const getWarnJobStatus = async (ctx) => { logger.debug( 'Native(Bulk-Upload): Request to transformer:: /getWarningJobs route', - JSON.stringify(ctx.request.body), + ctx.request.body, ); const { destType }: ContextBodySimple = ctx.request.body; @@ -140,17 +128,14 @@ export const getWarnJobStatus = async (ctx) => { }); } ctx.body = response; - logger.debug( - 'Native(Bulk-Upload): Request from transformer:: /getWarningJobs route', - JSON.stringify(ctx.body), - ); + logger.debug('Native(Bulk-Upload): Request from transformer:: /getWarningJobs route', ctx.body); return ctx.body; }; export const getFailedJobStatus = async (ctx) => { logger.debug( 'Native(Bulk-Upload): Request to transformer:: /getFailedJobs route', - JSON.stringify(ctx.request.body), + ctx.request.body, ); const { destType }: ContextBodySimple = ctx.request.body; @@ -176,9 +161,6 @@ export const getFailedJobStatus = async (ctx) => { }); } ctx.body = response; - logger.debug( - 'Native(Bulk-Upload): Request from transformer:: /getFailedJobs route', - JSON.stringify(ctx.body), - ); + logger.debug('Native(Bulk-Upload): Request from transformer:: /getFailedJobs route', ctx.body); return ctx.body; }; diff --git a/src/controllers/delivery.ts b/src/controllers/delivery.ts index 4334dc33b2..8ac7c9902a 100644 --- a/src/controllers/delivery.ts +++ b/src/controllers/delivery.ts @@ -1,28 +1,30 @@ /* eslint-disable prefer-destructuring */ /* eslint-disable sonarjs/no-duplicate-string */ +import { + isDefinedAndNotNullAndNotEmpty, + structuredLogger as logger, +} from '@rudderstack/integrations-lib'; import { Context } from 'koa'; -import { isDefinedAndNotNullAndNotEmpty } from '@rudderstack/integrations-lib'; +import { ServiceSelector } from '../helpers/serviceSelector'; +import { DeliveryTestService } from '../services/delivertTest/deliveryTest'; +import { DestinationPostTransformationService } from '../services/destination/postTransformation'; import { MiscService } from '../services/misc'; import { - DeliveryV1Response, DeliveryV0Response, + DeliveryV1Response, ProcessorTransformationOutput, ProxyV0Request, ProxyV1Request, } from '../types/index'; -import { ServiceSelector } from '../helpers/serviceSelector'; -import { DeliveryTestService } from '../services/delivertTest/deliveryTest'; -import { ControllerUtility } from './util'; -import logger from '../logger'; -import { DestinationPostTransformationService } from '../services/destination/postTransformation'; -import tags from '../v0/util/tags'; import { FixMe } from '../util/types'; +import tags from '../v0/util/tags'; +import { ControllerUtility } from './util'; const NON_DETERMINABLE = 'Non-determinable'; export class DeliveryController { public static async deliverToDestination(ctx: Context) { - logger.debug('Native(Delivery):: Request to transformer::', JSON.stringify(ctx.request.body)); + logger.info('Native(Delivery):: Request to transformer::', ctx.request.body); let deliveryResponse: DeliveryV0Response; const requestMetadata = MiscService.getRequestMetadata(ctx); const deliveryRequest = ctx.request.body as ProxyV0Request; @@ -52,12 +54,12 @@ export class DeliveryController { ctx.body = { output: deliveryResponse }; ControllerUtility.deliveryPostProcess(ctx, deliveryResponse.status); - logger.debug('Native(Delivery):: Response from transformer::', JSON.stringify(ctx.body)); + logger.debug('Native(Delivery):: Response from transformer::', ctx.body); return ctx; } public static async deliverToDestinationV1(ctx: Context) { - logger.debug('Native(Delivery):: Request to transformer::', JSON.stringify(ctx.request.body)); + logger.debug('Native(Delivery):: Request to transformer::', ctx.request.body); let deliveryResponse: DeliveryV1Response; const requestMetadata = MiscService.getRequestMetadata(ctx); const deliveryRequest = ctx.request.body as ProxyV1Request; @@ -91,15 +93,12 @@ export class DeliveryController { ControllerUtility.deliveryPostProcess(ctx); } - logger.debug('Native(Delivery):: Response from transformer::', JSON.stringify(ctx.body)); + logger.debug('Native(Delivery):: Response from transformer::', ctx.body); return ctx; } public static async testDestinationDelivery(ctx: Context) { - logger.debug( - 'Native(Delivery-Test):: Request to transformer::', - JSON.stringify(ctx.request.body), - ); + logger.debug('Native(Delivery-Test):: Request to transformer::', ctx.request.body); const { destination }: { destination: string } = ctx.params; const { version }: { version: string } = ctx.params; const { @@ -117,7 +116,7 @@ export class DeliveryController { ); ctx.body = { output: response }; ControllerUtility.postProcess(ctx); - logger.debug('Native(Delivery-Test):: Response from transformer::', JSON.stringify(ctx.body)); + logger.debug('Native(Delivery-Test):: Response from transformer::', ctx.body); return ctx; } } diff --git a/src/controllers/destination.ts b/src/controllers/destination.ts index d8b3c94524..35606ea62e 100644 --- a/src/controllers/destination.ts +++ b/src/controllers/destination.ts @@ -1,29 +1,27 @@ +import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import { Context } from 'koa'; -import { MiscService } from '../services/misc'; -import { DestinationPreTransformationService } from '../services/destination/preTransformation'; +import { ServiceSelector } from '../helpers/serviceSelector'; import { DestinationPostTransformationService } from '../services/destination/postTransformation'; +import { DestinationPreTransformationService } from '../services/destination/preTransformation'; +import { MiscService } from '../services/misc'; import { + ErrorDetailer, ProcessorTransformationRequest, - RouterTransformationRequest, ProcessorTransformationResponse, + RouterTransformationRequest, RouterTransformationResponse, } from '../types/index'; -import { ServiceSelector } from '../helpers/serviceSelector'; -import { ControllerUtility } from './util'; +import { DynamicConfigParser } from '../util/dynamicConfigParser'; import stats from '../util/stats'; -import logger from '../logger'; import { getIntegrationVersion } from '../util/utils'; -import tags from '../v0/util/tags'; -import { DynamicConfigParser } from '../util/dynamicConfigParser'; import { checkInvalidRtTfEvents } from '../v0/util'; +import tags from '../v0/util/tags'; +import { ControllerUtility } from './util'; export class DestinationController { public static async destinationTransformAtProcessor(ctx: Context) { const startTime = new Date(); - logger.debug( - 'Native(Process-Transform):: Requst to transformer::', - JSON.stringify(ctx.request.body), - ); + logger.debug('Native(Process-Transform):: Requst to transformer::', ctx.request.body); let resplist: ProcessorTransformationResponse[]; const requestMetadata = MiscService.getRequestMetadata(ctx); let events = ctx.request.body as ProcessorTransformationRequest[]; @@ -35,6 +33,9 @@ export class DestinationController { ...metaTags, }); const integrationService = ServiceSelector.getDestinationService(events); + const loggerWithCtx = logger.child({ + ...MiscService.getLoggableData(events[0]?.metadata as unknown as ErrorDetailer), + }); try { integrationService.init(); events = DestinationPreTransformationService.preProcess( @@ -50,6 +51,7 @@ export class DestinationController { destination, version, requestMetadata, + loggerWithCtx, ); } catch (error: any) { resplist = events.map((ev) => { @@ -69,10 +71,7 @@ export class DestinationController { } ctx.body = resplist; ControllerUtility.postProcess(ctx); - logger.debug( - 'Native(Process-Transform):: Response from transformer::', - JSON.stringify(ctx.body), - ); + loggerWithCtx.debug('Native(Process-Transform):: Response from transformer::', ctx.body); stats.histogram('dest_transform_output_events', resplist.length, { destination, version, @@ -94,10 +93,7 @@ export class DestinationController { public static async destinationTransformAtRouter(ctx: Context) { const startTime = new Date(); - logger.debug( - 'Native(Router-Transform):: Requst to transformer::', - JSON.stringify(ctx.request.body), - ); + logger.debug('Native(Router-Transform):: Requst to transformer::', ctx.request.body); const requestMetadata = MiscService.getRequestMetadata(ctx); const routerRequest = ctx.request.body as RouterTransformationRequest; const destination = routerRequest.destType; @@ -117,6 +113,9 @@ export class DestinationController { return ctx; } const metaTags = MiscService.getMetaTags(events[0].metadata); + const loggerWithCtx = logger.child({ + ...MiscService.getLoggableData(events[0]?.metadata as unknown as ErrorDetailer), + }); stats.histogram('dest_transform_input_events', events.length, { destination, version: 'v0', @@ -133,6 +132,7 @@ export class DestinationController { destination, getIntegrationVersion(), requestMetadata, + loggerWithCtx, ); } catch (error: any) { const metaTO = integrationService.getTags( @@ -155,10 +155,7 @@ export class DestinationController { version: 'v0', ...metaTags, }); - logger.debug( - 'Native(Router-Transform):: Response from transformer::', - JSON.stringify(ctx.body), - ); + loggerWithCtx.debug('Native(Router-Transform):: Response from transformer::', ctx.body); stats.timing('dest_transform_request_latency', startTime, { destination, version: 'v0', @@ -169,15 +166,15 @@ export class DestinationController { } public static batchProcess(ctx: Context) { - logger.debug( - 'Native(Process-Transform-Batch):: Requst to transformer::', - JSON.stringify(ctx.request.body), - ); + logger.info('Native(Process-Transform-Batch):: Requst to transformer::', ctx.request.body); const startTime = new Date(); const requestMetadata = MiscService.getRequestMetadata(ctx); const routerRequest = ctx.request.body as RouterTransformationRequest; const destination = routerRequest.destType; let events = routerRequest.input; + const loggerWithCtx = logger.child({ + ...MiscService.getLoggableData(events[0]?.metadata as unknown as ErrorDetailer), + }); const integrationService = ServiceSelector.getDestinationService(events); try { events = DestinationPreTransformationService.preProcess(events, ctx); @@ -187,6 +184,7 @@ export class DestinationController { destination, getIntegrationVersion(), requestMetadata, + loggerWithCtx, ); ctx.body = resplist; } catch (error: any) { @@ -204,10 +202,7 @@ export class DestinationController { ctx.body = [errResp]; } ControllerUtility.postProcess(ctx); - logger.debug( - 'Native(Process-Transform-Batch):: Response from transformer::', - JSON.stringify(ctx.body), - ); + loggerWithCtx.debug('Native(Process-Transform-Batch):: Response from transformer::', ctx.body); stats.timing('dest_transform_request_latency', startTime, { destination, feature: tags.FEATURES.BATCH, diff --git a/src/controllers/regulation.ts b/src/controllers/regulation.ts index 318b5ed4e7..4b8f87e3fa 100644 --- a/src/controllers/regulation.ts +++ b/src/controllers/regulation.ts @@ -1,19 +1,16 @@ +import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import { Context } from 'koa'; -import logger from '../logger'; -import { UserDeletionRequest, UserDeletionResponse } from '../types'; import { ServiceSelector } from '../helpers/serviceSelector'; -import tags from '../v0/util/tags'; -import stats from '../util/stats'; import { DestinationPostTransformationService } from '../services/destination/postTransformation'; +import { UserDeletionRequest, UserDeletionResponse } from '../types'; +import stats from '../util/stats'; +import tags from '../v0/util/tags'; // eslint-disable-next-line @typescript-eslint/no-unused-vars import { CatchErr } from '../util/types'; export class RegulationController { public static async deleteUsers(ctx: Context) { - logger.debug( - 'Native(Process-Transform):: Requst to transformer::', - JSON.stringify(ctx.request.body), - ); + logger.debug('Native(Process-Transform):: Requst to transformer::', ctx.request.body); const startTime = new Date(); let rudderDestInfo: any; try { diff --git a/src/controllers/source.ts b/src/controllers/source.ts index ef5483a756..e1a4931371 100644 --- a/src/controllers/source.ts +++ b/src/controllers/source.ts @@ -1,20 +1,18 @@ +import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import { Context } from 'koa'; -import { MiscService } from '../services/misc'; import { ServiceSelector } from '../helpers/serviceSelector'; -import { ControllerUtility } from './util'; -import logger from '../logger'; +import { MiscService } from '../services/misc'; import { SourcePostTransformationService } from '../services/source/postTransformation'; +import { ControllerUtility } from './util'; export class SourceController { public static async sourceTransform(ctx: Context) { - logger.debug( - 'Native(Source-Transform):: Request to transformer::', - JSON.stringify(ctx.request.body), - ); + logger.debug('Native(Source-Transform):: Request to transformer::', ctx.request.body); const requestMetadata = MiscService.getRequestMetadata(ctx); const events = ctx.request.body as object[]; const { version, source }: { version: string; source: string } = ctx.params; const integrationService = ServiceSelector.getNativeSourceService(); + const loggerWithCtx = logger.child({ version, source }); try { const { implementationVersion, input } = ControllerUtility.adaptInputToVersion( source, @@ -26,6 +24,7 @@ export class SourceController { source, implementationVersion, requestMetadata, + loggerWithCtx, ); ctx.body = resplist; } catch (err: any) { @@ -34,10 +33,7 @@ export class SourceController { ctx.body = [resp]; } ControllerUtility.postProcess(ctx); - logger.debug( - 'Native(Source-Transform):: Response from transformer::', - JSON.stringify(ctx.body), - ); + loggerWithCtx.debug('Native(Source-Transform):: Response from transformer::', ctx.body); return ctx; } } diff --git a/src/controllers/userTransform.ts b/src/controllers/userTransform.ts index 3e01686a52..0e288c6f04 100644 --- a/src/controllers/userTransform.ts +++ b/src/controllers/userTransform.ts @@ -1,10 +1,10 @@ +import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import { Context } from 'koa'; -import { ProcessorTransformationRequest, UserTransformationServiceResponse } from '../types/index'; import { UserTransformService } from '../services/userTransform'; -import logger from '../logger'; +import { ProcessorTransformationRequest, UserTransformationServiceResponse } from '../types/index'; import { - setupUserTransformHandler, extractLibraries, + setupUserTransformHandler, validateCode, } from '../util/customTransformer'; import { ControllerUtility } from './util'; @@ -13,7 +13,7 @@ export class UserTransformController { public static async transform(ctx: Context) { logger.debug( '(User transform - router:/customTransform ):: Request to transformer', - JSON.stringify(ctx.request.body), + ctx.request.body, ); const requestSize = Number(ctx.request.get('content-length')); const events = ctx.request.body as ProcessorTransformationRequest[]; @@ -23,7 +23,7 @@ export class UserTransformController { ControllerUtility.postProcess(ctx, processedRespone.retryStatus); logger.debug( '(User transform - router:/customTransform ):: Response from transformer', - JSON.stringify(ctx.response.body), + ctx.response.body, ); return ctx; } @@ -31,7 +31,7 @@ export class UserTransformController { public static async testTransform(ctx: Context) { logger.debug( '(User transform - router:/transformation/test ):: Request to transformer', - JSON.stringify(ctx.request.body), + ctx.request.body, ); const { events, trRevCode, libraryVersionIDs = [] } = ctx.request.body as any; const response = await UserTransformService.testTransformRoutine( @@ -43,7 +43,7 @@ export class UserTransformController { ControllerUtility.postProcess(ctx, response.status); logger.debug( '(User transform - router:/transformation/test ):: Response from transformer', - JSON.stringify(ctx.response.body), + ctx.response.body, ); return ctx; } @@ -51,7 +51,7 @@ export class UserTransformController { public static async testTransformLibrary(ctx: Context) { logger.debug( '(User transform - router:/transformationLibrary/test ):: Request to transformer', - JSON.stringify(ctx.request.body), + ctx.request.body, ); try { const { code, language = 'javascript' } = ctx.request.body as any; @@ -66,7 +66,7 @@ export class UserTransformController { } logger.debug( '(User transform - router:/transformationLibrary/test ):: Response from transformer', - JSON.stringify(ctx.response.body), + ctx.response.body, ); return ctx; } @@ -74,7 +74,7 @@ export class UserTransformController { public static async testTransformSethandle(ctx: Context) { logger.debug( '(User transform - router:/transformation/sethandle ):: Request to transformer', - JSON.stringify(ctx.request.body), + ctx.request.body, ); try { const { trRevCode, libraryVersionIDs = [] } = ctx.request.body as any; @@ -96,7 +96,7 @@ export class UserTransformController { } logger.debug( '(User transform - router:/transformation/sethandle ):: Response from transformer', - JSON.stringify(ctx.request.body), + ctx.request.body, ); return ctx; } @@ -104,7 +104,7 @@ export class UserTransformController { public static async extractLibhandle(ctx: Context) { logger.debug( '(User transform - router:/extractLibs ):: Request to transformer', - JSON.stringify(ctx.request.body), + ctx.request.body, ); try { const { @@ -134,7 +134,7 @@ export class UserTransformController { } logger.debug( '(User transform - router:/extractLibs ):: Response from transformer', - JSON.stringify(ctx.request.body), + ctx.request.body, ); return ctx; } diff --git a/src/index.ts b/src/index.ts index 36f32f1aed..5557994b2e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,14 @@ +import { structuredLogger as logger } from '@rudderstack/integrations-lib'; +import dotenv from 'dotenv'; +import gracefulShutdown from 'http-graceful-shutdown'; import Koa from 'koa'; import bodyParser from 'koa-bodyparser'; -import gracefulShutdown from 'http-graceful-shutdown'; -import dotenv from 'dotenv'; -import logger from './logger'; -import cluster from './util/cluster'; +import { addRequestSizeMiddleware, addStatMiddleware, initPyroscope } from './middleware'; +import { addSwaggerRoutes, applicationRoutes } from './routes'; import { metricsRouter } from './routes/metricsRouter'; -import { addStatMiddleware, addRequestSizeMiddleware, initPyroscope } from './middleware'; -import { logProcessInfo } from './util/utils'; -import { applicationRoutes, addSwaggerRoutes } from './routes'; +import cluster from './util/cluster'; import { RedisDB } from './util/redis/redisConnector'; +import { logProcessInfo } from './util/utils'; dotenv.config(); const clusterEnabled = process.env.CLUSTER_ENABLED !== 'false'; diff --git a/src/interfaces/DestinationService.ts b/src/interfaces/DestinationService.ts index 4947089b5d..5d7596dac5 100644 --- a/src/interfaces/DestinationService.ts +++ b/src/interfaces/DestinationService.ts @@ -1,14 +1,14 @@ import { DeliveryV0Response, + DeliveryV1Response, MetaTransferObject, ProcessorTransformationRequest, ProcessorTransformationResponse, + ProxyRequest, RouterTransformationRequestData, RouterTransformationResponse, UserDeletionRequest, UserDeletionResponse, - ProxyRequest, - DeliveryV1Response, } from '../types/index'; export interface DestinationService { @@ -28,6 +28,7 @@ export interface DestinationService { destinationType: string, version: string, requestMetadata: NonNullable, + logger: NonNullable, ): Promise; doRouterTransformation( @@ -35,6 +36,7 @@ export interface DestinationService { destinationType: string, version: string, requestMetadata: NonNullable, + logger: NonNullable, ): Promise; doBatchTransformation( @@ -42,6 +44,7 @@ export interface DestinationService { destinationType: string, version: string, requestMetadata: NonNullable, + logger: NonNullable, ): RouterTransformationResponse[]; deliver( diff --git a/src/interfaces/SourceService.ts b/src/interfaces/SourceService.ts index c7de8cfe8b..fab6490264 100644 --- a/src/interfaces/SourceService.ts +++ b/src/interfaces/SourceService.ts @@ -1,4 +1,5 @@ import { MetaTransferObject, SourceTransformationResponse } from '../types/index'; +import { FixMe } from '../util/types'; export interface SourceService { getTags(): MetaTransferObject; @@ -8,5 +9,6 @@ export interface SourceService { sourceType: string, version: string, requestMetadata: NonNullable, + logger: FixMe, ): Promise; } diff --git a/src/services/comparator.ts b/src/services/comparator.ts index 36cb0ebd5a..511436dfd1 100644 --- a/src/services/comparator.ts +++ b/src/services/comparator.ts @@ -1,4 +1,5 @@ /* eslint-disable class-methods-use-this */ +import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import { DestinationService } from '../interfaces/DestinationService'; import { DeliveryV0Response, @@ -14,10 +15,9 @@ import { UserDeletionRequest, UserDeletionResponse, } from '../types'; -import tags from '../v0/util/tags'; -import stats from '../util/stats'; -import logger from '../logger'; import { CommonUtils } from '../util/common'; +import stats from '../util/stats'; +import tags from '../v0/util/tags'; const NS_PER_SEC = 1e9; @@ -204,6 +204,7 @@ export class ComparatorService implements DestinationService { destinationType, version, requestMetadata, + logger, ); const primaryTimeDiff = process.hrtime(primaryStartTime); const primaryTime = primaryTimeDiff[0] * NS_PER_SEC + primaryTimeDiff[1]; @@ -262,6 +263,7 @@ export class ComparatorService implements DestinationService { destinationType, version, requestMetadata, + logger, ); const primaryTimeDiff = process.hrtime(primaryStartTime); const primaryTime = primaryTimeDiff[0] * NS_PER_SEC + primaryTimeDiff[1]; @@ -320,6 +322,7 @@ export class ComparatorService implements DestinationService { destinationType, version, requestMetadata, + {}, ); const primaryTimeDiff = process.hrtime(primaryStartTime); const primaryTime = primaryTimeDiff[0] * NS_PER_SEC + primaryTimeDiff[1]; diff --git a/src/services/destination/__tests__/nativeIntegration.test.ts b/src/services/destination/__tests__/nativeIntegration.test.ts index 59c8b41881..85d099d292 100644 --- a/src/services/destination/__tests__/nativeIntegration.test.ts +++ b/src/services/destination/__tests__/nativeIntegration.test.ts @@ -1,11 +1,12 @@ -import { NativeIntegrationDestinationService } from '../nativeIntegration'; -import { DestinationPostTransformationService } from '../postTransformation'; +import { structuredLogger as logger } from '@rudderstack/integrations-lib'; +import { FetchHandler } from '../../../helpers/fetchHandlers'; import { - ProcessorTransformationRequest, ProcessorTransformationOutput, + ProcessorTransformationRequest, ProcessorTransformationResponse, } from '../../../types/index'; -import { FetchHandler } from '../../../helpers/fetchHandlers'; +import { NativeIntegrationDestinationService } from '../nativeIntegration'; +import { DestinationPostTransformationService } from '../postTransformation'; afterEach(() => { jest.clearAllMocks(); @@ -47,6 +48,7 @@ describe('NativeIntegration Service', () => { destType, version, requestMetadata, + logger, ); expect(resp).toEqual(tresponse); @@ -77,6 +79,7 @@ describe('NativeIntegration Service', () => { destType, version, requestMetadata, + logger, ); const expected = [ diff --git a/src/services/destination/cdkV2Integration.ts b/src/services/destination/cdkV2Integration.ts index c18a5cd936..a649da9154 100644 --- a/src/services/destination/cdkV2Integration.ts +++ b/src/services/destination/cdkV2Integration.ts @@ -1,27 +1,28 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable class-methods-use-this */ -import groupBy from 'lodash/groupBy'; import { TransformationError } from '@rudderstack/integrations-lib'; +import groupBy from 'lodash/groupBy'; import { processCdkV2Workflow } from '../../cdk/v2/handler'; import { DestinationService } from '../../interfaces/DestinationService'; import { DeliveryV0Response, + DeliveryV1Response, ErrorDetailer, MetaTransferObject, + ProcessorTransformationOutput, ProcessorTransformationRequest, ProcessorTransformationResponse, + ProxyRequest, RouterTransformationRequestData, RouterTransformationResponse, - ProcessorTransformationOutput, UserDeletionRequest, UserDeletionResponse, - ProxyRequest, - DeliveryV1Response, } from '../../types/index'; +import stats from '../../util/stats'; +import { CatchErr, FixMe } from '../../util/types'; import tags from '../../v0/util/tags'; +import { MiscService } from '../misc'; import { DestinationPostTransformationService } from './postTransformation'; -import stats from '../../util/stats'; -import { CatchErr } from '../../util/types'; export class CDKV2DestinationService implements DestinationService { public init() {} @@ -55,10 +56,19 @@ export class CDKV2DestinationService implements DestinationService { destinationType: string, _version: string, requestMetadata: NonNullable, + logger: any, ): Promise { // TODO: Change the promise type const respList: ProcessorTransformationResponse[][] = await Promise.all( events.map(async (event) => { + const metaTo = this.getTags( + destinationType, + event.metadata.destinationId, + event.metadata.workspaceId, + tags.FEATURES.PROCESSOR, + ); + metaTo.metadata = event.metadata; + const loggerWithCtx = logger.child({ ...MiscService.getLoggableData(metaTo.errorDetails) }); try { const transformedPayloads: | ProcessorTransformationOutput @@ -66,9 +76,9 @@ export class CDKV2DestinationService implements DestinationService { destinationType, event, tags.FEATURES.PROCESSOR, + loggerWithCtx, requestMetadata, ); - stats.increment('event_transform_success', { destType: destinationType, module: tags.MODULES.DESTINATION, @@ -85,13 +95,6 @@ export class CDKV2DestinationService implements DestinationService { undefined, ); } catch (error: CatchErr) { - const metaTo = this.getTags( - destinationType, - event.metadata.destinationId, - event.metadata.workspaceId, - tags.FEATURES.PROCESSOR, - ); - metaTo.metadata = event.metadata; const erroredResp = DestinationPostTransformationService.handleProcessorTransformFailureEvents( error, @@ -112,6 +115,7 @@ export class CDKV2DestinationService implements DestinationService { destinationType: string, _version: string, requestMetadata: NonNullable, + logger: FixMe, ): Promise { const allDestEvents: object = groupBy( events, @@ -127,12 +131,16 @@ export class CDKV2DestinationService implements DestinationService { tags.FEATURES.ROUTER, ); metaTo.metadata = destInputArray[0].metadata; + const loggerWithCtx = logger.child({ + ...MiscService.getLoggableData(metaTo.errorDetails), + }); try { const doRouterTransformationResponse: RouterTransformationResponse[] = await processCdkV2Workflow( destinationType, destInputArray, tags.FEATURES.ROUTER, + loggerWithCtx, requestMetadata, ); return DestinationPostTransformationService.handleRouterTransformSuccessEvents( diff --git a/src/services/destination/nativeIntegration.ts b/src/services/destination/nativeIntegration.ts index 2bb82fc602..0bc9308fcd 100644 --- a/src/services/destination/nativeIntegration.ts +++ b/src/services/destination/nativeIntegration.ts @@ -1,31 +1,32 @@ /* eslint-disable prefer-destructuring */ /* eslint-disable sonarjs/no-duplicate-string */ /* eslint-disable @typescript-eslint/no-unused-vars */ -import groupBy from 'lodash/groupBy'; import cloneDeep from 'lodash/cloneDeep'; +import groupBy from 'lodash/groupBy'; +import networkHandlerFactory from '../../adapters/networkHandlerFactory'; +import { FetchHandler } from '../../helpers/fetchHandlers'; import { DestinationService } from '../../interfaces/DestinationService'; import { + DeliveryJobState, DeliveryV0Response, + DeliveryV1Response, ErrorDetailer, MetaTransferObject, + ProcessorTransformationOutput, ProcessorTransformationRequest, ProcessorTransformationResponse, + ProxyRequest, + ProxyV0Request, + ProxyV1Request, RouterTransformationRequestData, RouterTransformationResponse, - ProcessorTransformationOutput, UserDeletionRequest, UserDeletionResponse, - ProxyRequest, - ProxyV0Request, - ProxyV1Request, - DeliveryV1Response, - DeliveryJobState, } from '../../types/index'; -import { DestinationPostTransformationService } from './postTransformation'; -import networkHandlerFactory from '../../adapters/networkHandlerFactory'; -import { FetchHandler } from '../../helpers/fetchHandlers'; -import tags from '../../v0/util/tags'; import stats from '../../util/stats'; +import tags from '../../v0/util/tags'; +import { MiscService } from '../misc'; +import { DestinationPostTransformationService } from './postTransformation'; export class NativeIntegrationDestinationService implements DestinationService { public init() {} @@ -59,27 +60,33 @@ export class NativeIntegrationDestinationService implements DestinationService { destinationType: string, version: string, requestMetadata: NonNullable, + logger: any, ): Promise { const destHandler = FetchHandler.getDestHandler(destinationType, version); const respList: ProcessorTransformationResponse[][] = await Promise.all( events.map(async (event) => { + const metaTO = this.getTags( + destinationType, + event.metadata?.destinationId, + event.metadata?.workspaceId, + tags.FEATURES.PROCESSOR, + ); + metaTO.metadata = event.metadata; + const loggerWithCtx = logger.child({ ...MiscService.getLoggableData(metaTO.errorDetails) }); try { const transformedPayloads: | ProcessorTransformationOutput - | ProcessorTransformationOutput[] = await destHandler.process(event, requestMetadata); + | ProcessorTransformationOutput[] = await destHandler.process( + event, + requestMetadata, + loggerWithCtx, + ); return DestinationPostTransformationService.handleProcessorTransformSucessEvents( event, transformedPayloads, destHandler, ); } catch (error: any) { - const metaTO = this.getTags( - destinationType, - event.metadata?.destinationId, - event.metadata?.workspaceId, - tags.FEATURES.PROCESSOR, - ); - metaTO.metadata = event.metadata; const erroredResp = DestinationPostTransformationService.handleProcessorTransformFailureEvents( error, @@ -97,6 +104,7 @@ export class NativeIntegrationDestinationService implements DestinationService { destinationType: string, version: string, requestMetadata: NonNullable, + logger: any, ): Promise { const destHandler = FetchHandler.getDestHandler(destinationType, version); const allDestEvents: NonNullable = groupBy( @@ -112,9 +120,16 @@ export class NativeIntegrationDestinationService implements DestinationService { destInputArray[0].metadata?.workspaceId, tags.FEATURES.ROUTER, ); + const loggerWithCtx = logger.child({ + ...MiscService.getLoggableData(metaTO.errorDetails), + }); try { const doRouterTransformationResponse: RouterTransformationResponse[] = - await destHandler.processRouterDest(cloneDeep(destInputArray), requestMetadata); + await destHandler.processRouterDest( + cloneDeep(destInputArray), + requestMetadata, + loggerWithCtx, + ); metaTO.metadata = destInputArray[0].metadata; return DestinationPostTransformationService.handleRouterTransformSuccessEvents( doRouterTransformationResponse, @@ -141,6 +156,7 @@ export class NativeIntegrationDestinationService implements DestinationService { destinationType: string, version: any, requestMetadata: NonNullable, + logger: any, ): RouterTransformationResponse[] { const destHandler = FetchHandler.getDestHandler(destinationType, version); if (!destHandler.batch) { @@ -152,20 +168,24 @@ export class NativeIntegrationDestinationService implements DestinationService { ); const groupedEvents: RouterTransformationRequestData[][] = Object.values(allDestEvents); const response = groupedEvents.map((destEvents) => { + const metaTO = this.getTags( + destinationType, + destEvents[0].metadata.destinationId, + destEvents[0].metadata.workspaceId, + tags.FEATURES.BATCH, + ); + metaTO.metadatas = events.map((event) => event.metadata); + const loggerWithCtx = logger.child({ + ...MiscService.getLoggableData(metaTO.errorDetails), + }); try { const destBatchedRequests: RouterTransformationResponse[] = destHandler.batch( destEvents, requestMetadata, + loggerWithCtx, ); return destBatchedRequests; } catch (error: any) { - const metaTO = this.getTags( - destinationType, - destEvents[0].metadata.destinationId, - destEvents[0].metadata.workspaceId, - tags.FEATURES.BATCH, - ); - metaTO.metadatas = events.map((event) => event.metadata); const errResp = DestinationPostTransformationService.handleBatchTransformFailureEvents( error, metaTO, @@ -264,6 +284,7 @@ export class NativeIntegrationDestinationService implements DestinationService { error: `${destType}: Doesn't support deletion of users`, } as UserDeletionResponse; } + const metaTO = this.getTags(destType, 'unknown', 'unknown', tags.FEATURES.USER_DELETION); try { const result: UserDeletionResponse = await destUserDeletionHandler.processDeleteUsers({ ...request, @@ -276,7 +297,6 @@ export class NativeIntegrationDestinationService implements DestinationService { }); return result; } catch (error: any) { - const metaTO = this.getTags(destType, 'unknown', 'unknown', tags.FEATURES.USER_DELETION); return DestinationPostTransformationService.handleUserDeletionFailureEvents( error, metaTO, diff --git a/src/services/destination/postTransformation.ts b/src/services/destination/postTransformation.ts index 161547683b..40cee61e66 100644 --- a/src/services/destination/postTransformation.ts +++ b/src/services/destination/postTransformation.ts @@ -1,24 +1,30 @@ /* eslint-disable no-param-reassign */ +import { PlatformError } from '@rudderstack/integrations-lib'; import cloneDeep from 'lodash/cloneDeep'; -import isObject from 'lodash/isObject'; import isEmpty from 'lodash/isEmpty'; -import { PlatformError } from '@rudderstack/integrations-lib'; +import isObject from 'lodash/isObject'; import { + DeliveryJobState, + DeliveryV0Response, + DeliveryV1Response, + MetaTransferObject, + ProcessorTransformationOutput, ProcessorTransformationRequest, ProcessorTransformationResponse, RouterTransformationResponse, - ProcessorTransformationOutput, - DeliveryV0Response, - MetaTransferObject, UserDeletionResponse, - DeliveryV1Response, - DeliveryJobState, } from '../../types/index'; -import { generateErrorObject } from '../../v0/util'; -import { ErrorReportingService } from '../errorReporting'; -import tags from '../../v0/util/tags'; import stats from '../../util/stats'; import { FixMe } from '../../util/types'; +import { generateErrorObject } from '../../v0/util'; +import tags from '../../v0/util/tags'; +import { ErrorReportingService } from '../errorReporting'; +import { MiscService } from '../misc'; + +const defaultErrorMessages = { + router: '[Router Transform] Error occurred while processing the payload.', + delivery: '[Delivery] Error occured while processing payload', +} as const; export class DestinationPostTransformationService { public static handleProcessorTransformSucessEvents( @@ -62,6 +68,10 @@ export class DestinationPostTransformationService { error: errObj.message || '[Processor Transform] Error occurred while processing the payload.', statTags: errObj.statTags, } as ProcessorTransformationResponse; + MiscService.logError( + errObj.message || '[Processor Transform] Error occurred while processing the payload.', + metaTo.errorDetails, + ); ErrorReportingService.reportError(error, metaTo.errorContext, resp); return resp; } @@ -99,6 +109,7 @@ export class DestinationPostTransformationService { ...resp.statTags, ...metaTo.errorDetails, }; + MiscService.logError(resp.error || defaultErrorMessages.router, metaTo.errorDetails); stats.increment('event_transform_failure', metaTo.errorDetails); } else { stats.increment('event_transform_success', { @@ -124,9 +135,10 @@ export class DestinationPostTransformationService { metadata: metaTo.metadatas, batched: false, statusCode: errObj.status, - error: errObj.message || '[Router Transform] Error occurred while processing the payload.', + error: errObj.message || defaultErrorMessages.router, statTags: errObj.statTags, } as RouterTransformationResponse; + MiscService.logError(errObj.message || defaultErrorMessages.router, metaTo.errorDetails); ErrorReportingService.reportError(error, metaTo.errorContext, resp); stats.increment('event_transform_failure', metaTo.errorDetails); return resp; @@ -141,9 +153,10 @@ export class DestinationPostTransformationService { metadata: metaTo.metadatas, batched: false, statusCode: 500, // for batch we should consider code error hence keeping retryable - error: errObj.message || '[Batch Transform] Error occurred while processing payload.', + error: errObj.message || defaultErrorMessages.delivery, statTags: errObj.statTags, } as RouterTransformationResponse; + MiscService.logError(error as string, metaTo.errorDetails); ErrorReportingService.reportError(error, metaTo.errorContext, resp); return resp; } @@ -174,6 +187,10 @@ export class DestinationPostTransformationService { const errObj = generateErrorObject(error, metaTo.errorDetails, false); const metadataArray = metaTo.metadatas; if (!Array.isArray(metadataArray)) { + MiscService.logError( + 'Proxy v1 endpoint error : metadataArray is not an array', + metaTo.errorDetails, + ); // Panic throw new PlatformError('Proxy v1 endpoint error : metadataArray is not an array'); } @@ -182,7 +199,7 @@ export class DestinationPostTransformationService { error: JSON.stringify(error.destinationResponse?.response) || errObj.message || - '[Delivery] Error occured while processing payload', + defaultErrorMessages.delivery, statusCode: errObj.status, metadata, } as DeliveryJobState; @@ -198,7 +215,7 @@ export class DestinationPostTransformationService { authErrorCategory: errObj.authErrorCategory, }), } as DeliveryV1Response; - + MiscService.logError(errObj.message, metaTo.errorDetails); ErrorReportingService.reportError(error, metaTo.errorContext, resp); return resp; } @@ -216,6 +233,7 @@ export class DestinationPostTransformationService { authErrorCategory: errObj.authErrorCategory, }), } as UserDeletionResponse; + MiscService.logError(errObj.message, metaTo.errorDetails); ErrorReportingService.reportError(error, metaTo.errorContext, resp); return resp; } diff --git a/src/services/misc.ts b/src/services/misc.ts index e0953d08bf..3df1196c1d 100644 --- a/src/services/misc.ts +++ b/src/services/misc.ts @@ -1,10 +1,11 @@ /* eslint-disable global-require, import/no-dynamic-require */ +import { LoggableExtraData, structuredLogger as logger } from '@rudderstack/integrations-lib'; import fs from 'fs'; -import path from 'path'; import { Context } from 'koa'; +import path from 'path'; import { DestHandlerMap } from '../constants/destinationCanonicalNames'; -import { Metadata } from '../types'; import { getCPUProfile, getHeapProfile } from '../middleware'; +import { ErrorDetailer, Metadata } from '../types'; export class MiscService { public static getDestHandler(dest: string, version: string) { @@ -74,4 +75,21 @@ export class MiscService { public static async getHeapProfile() { return getHeapProfile(); } + + public static getLoggableData(errorDetailer: ErrorDetailer): Partial { + return { + ...(errorDetailer?.destinationId && { destinationId: errorDetailer.destinationId }), + ...(errorDetailer?.sourceId && { sourceId: errorDetailer.sourceId }), + ...(errorDetailer?.workspaceId && { workspaceId: errorDetailer.workspaceId }), + ...(errorDetailer?.destType && { destType: errorDetailer.destType }), + ...(errorDetailer?.module && { module: errorDetailer.module }), + ...(errorDetailer?.implementation && { implementation: errorDetailer.implementation }), + ...(errorDetailer?.feature && { feature: errorDetailer.feature }), + }; + } + + public static logError(message: string, errorDetailer: ErrorDetailer) { + const loggableExtraData: Partial = this.getLoggableData(errorDetailer); + logger.errorw(message || '', loggableExtraData); + } } diff --git a/src/services/source/__tests__/nativeIntegration.test.ts b/src/services/source/__tests__/nativeIntegration.test.ts index bb40438811..77e355fd1a 100644 --- a/src/services/source/__tests__/nativeIntegration.test.ts +++ b/src/services/source/__tests__/nativeIntegration.test.ts @@ -1,8 +1,9 @@ +import { structuredLogger as logger } from '@rudderstack/integrations-lib'; +import { FetchHandler } from '../../../helpers/fetchHandlers'; +import { RudderMessage, SourceTransformationResponse } from '../../../types/index'; +import stats from '../../../util/stats'; import { NativeIntegrationSourceService } from '../nativeIntegration'; import { SourcePostTransformationService } from '../postTransformation'; -import { SourceTransformationResponse, RudderMessage } from '../../../types/index'; -import stats from '../../../util/stats'; -import { FetchHandler } from '../../../helpers/fetchHandlers'; afterEach(() => { jest.clearAllMocks(); @@ -43,7 +44,13 @@ describe('NativeIntegration Source Service', () => { }); const service = new NativeIntegrationSourceService(); - const resp = await service.sourceTransformRoutine(events, sourceType, version, requestMetadata); + const resp = await service.sourceTransformRoutine( + events, + sourceType, + version, + requestMetadata, + logger, + ); expect(resp).toEqual(tresponse); @@ -80,7 +87,13 @@ describe('NativeIntegration Source Service', () => { jest.spyOn(stats, 'increment').mockImplementation(() => {}); const service = new NativeIntegrationSourceService(); - const resp = await service.sourceTransformRoutine(events, sourceType, version, requestMetadata); + const resp = await service.sourceTransformRoutine( + events, + sourceType, + version, + requestMetadata, + logger, + ); expect(resp).toEqual(tresponse); diff --git a/src/services/source/nativeIntegration.ts b/src/services/source/nativeIntegration.ts index 6eaef2f835..2ecfc30066 100644 --- a/src/services/source/nativeIntegration.ts +++ b/src/services/source/nativeIntegration.ts @@ -1,3 +1,4 @@ +import { FetchHandler } from '../../helpers/fetchHandlers'; import { SourceService } from '../../interfaces/SourceService'; import { ErrorDetailer, @@ -5,11 +6,11 @@ import { RudderMessage, SourceTransformationResponse, } from '../../types/index'; +import stats from '../../util/stats'; import { FixMe } from '../../util/types'; -import { SourcePostTransformationService } from './postTransformation'; -import { FetchHandler } from '../../helpers/fetchHandlers'; import tags from '../../v0/util/tags'; -import stats from '../../util/stats'; +import { MiscService } from '../misc'; +import { SourcePostTransformationService } from './postTransformation'; export class NativeIntegrationSourceService implements SourceService { public getTags(): MetaTransferObject { @@ -31,20 +32,23 @@ export class NativeIntegrationSourceService implements SourceService { version: string, // eslint-disable-next-line @typescript-eslint/no-unused-vars _requestMetadata: NonNullable, + logger: FixMe, ): Promise { const sourceHandler = FetchHandler.getSourceHandler(sourceType, version); + const metaTO = this.getTags(); + const loggerWithCtx = logger.child({ ...MiscService.getLoggableData(metaTO.errorDetails) }); const respList: SourceTransformationResponse[] = await Promise.all( sourceEvents.map(async (sourceEvent) => { try { const respEvents: RudderMessage | RudderMessage[] | SourceTransformationResponse = - await sourceHandler.process(sourceEvent); + await sourceHandler.process(sourceEvent, loggerWithCtx); return SourcePostTransformationService.handleSuccessEventsSource(respEvents); } catch (error: FixMe) { - const metaTO = this.getTags(); stats.increment('source_transform_errors', { source: sourceType, version, }); + logger.debug('Error during source Transform', error); return SourcePostTransformationService.handleFailureEventsSource(error, metaTO); } }), diff --git a/src/util/redis/redisConnector.test.js b/src/util/redis/redisConnector.test.js index e0491132ff..7cf2ccbbcf 100644 --- a/src/util/redis/redisConnector.test.js +++ b/src/util/redis/redisConnector.test.js @@ -2,6 +2,7 @@ const fs = require('fs'); const path = require('path'); const version = 'v0'; const { RedisDB } = require('./redisConnector'); +const { structuredLogger: logger } = require('@rudderstack/integrations-lib'); jest.mock('ioredis', () => require('../../../test/__mocks__/redis')); const sourcesList = ['shopify']; process.env.USE_REDIS_DB = 'true'; @@ -54,7 +55,7 @@ describe(`Source Tests`, () => { data.forEach((dataPoint, index) => { it(`${index}. ${source} - ${dataPoint.description}`, async () => { try { - const output = await transformer.process(dataPoint.input); + const output = await transformer.process(dataPoint.input, logger); expect(output).toEqual(dataPoint.output); } catch (error) { expect(error.message).toEqual(dataPoint.output.error); diff --git a/src/v0/destinations/campaign_manager/transform.js b/src/v0/destinations/campaign_manager/transform.js index 14bc6d2c19..403a79a971 100644 --- a/src/v0/destinations/campaign_manager/transform.js +++ b/src/v0/destinations/campaign_manager/transform.js @@ -243,7 +243,8 @@ const batchEvents = (eventChunksArray) => { return batchedResponseList; }; -const processRouterDest = async (inputs, reqMetadata) => { +const processRouterDest = async (inputs, reqMetadata, logger) => { + logger.debug(`Transformation router request received with size ${inputs.length}`); const batchErrorRespList = []; const eventChunksArray = []; const { destination } = inputs[0]; diff --git a/src/v0/destinations/mailchimp/utils.js b/src/v0/destinations/mailchimp/utils.js index 1f4fc03ee5..a726f23a39 100644 --- a/src/v0/destinations/mailchimp/utils.js +++ b/src/v0/destinations/mailchimp/utils.js @@ -1,9 +1,12 @@ const get = require('get-value'); const md5 = require('md5'); -const { InstrumentationError, NetworkError } = require('@rudderstack/integrations-lib'); +const { + InstrumentationError, + NetworkError, + structuredLogger: logger, +} = require('@rudderstack/integrations-lib'); const myAxios = require('../../../util/myAxios'); const { MappedToDestinationKey } = require('../../../constants'); -const logger = require('../../../logger'); const { isDefinedAndNotNull, isDefined, diff --git a/src/v0/destinations/twitter_ads/transform.js b/src/v0/destinations/twitter_ads/transform.js index 268dca3636..365663925e 100644 --- a/src/v0/destinations/twitter_ads/transform.js +++ b/src/v0/destinations/twitter_ads/transform.js @@ -156,7 +156,8 @@ function validateRequest(message) { } } -function process(event) { +function process(event, requestMetadata, logger) { + logger.info(`[TWITTER ADS]: Transforming request received with info`); const { message, metadata, destination } = event; validateRequest(message); diff --git a/src/v0/sources/canny/transform.js b/src/v0/sources/canny/transform.js index 9188f5ac34..38ed5e137e 100644 --- a/src/v0/sources/canny/transform.js +++ b/src/v0/sources/canny/transform.js @@ -2,7 +2,6 @@ const sha256 = require('sha256'); const { TransformationError } = require('@rudderstack/integrations-lib'); const Message = require('../message'); const { voterMapping, authorMapping, checkForRequiredFields } = require('./util'); -const { logger } = require('../../../logger'); const CannyOperation = { VOTE_CREATED: 'vote.created', @@ -15,7 +14,7 @@ const CannyOperation = { * @param {*} event * @param {*} typeOfUser */ -function settingIds(message, event, typeOfUser) { +function settingIds(message, event, typeOfUser, logger) { const clonedMessage = { ...message }; try { // setting up userId @@ -48,7 +47,7 @@ function settingIds(message, event, typeOfUser) { * @param {*} typeOfUser * @returns message */ -function createMessage(event, typeOfUser) { +function createMessage(event, typeOfUser, logger) { const message = new Message(`Canny`); message.setEventType('track'); @@ -61,7 +60,7 @@ function createMessage(event, typeOfUser) { message.context.integration.version = '1.0.0'; - const finalMessage = settingIds(message, event, typeOfUser); + const finalMessage = settingIds(message, event, typeOfUser, logger); checkForRequiredFields(finalMessage); @@ -73,7 +72,7 @@ function createMessage(event, typeOfUser) { return finalMessage; } -function process(event) { +function process(event, logger) { let typeOfUser; switch (event.type) { @@ -86,6 +85,6 @@ function process(event) { typeOfUser = 'author'; } - return createMessage(event, typeOfUser); + return createMessage(event, typeOfUser, logger); } module.exports = { process }; diff --git a/src/v0/sources/shopify/transform.js b/src/v0/sources/shopify/transform.js index b6d2a4c6cf..4886fb3df1 100644 --- a/src/v0/sources/shopify/transform.js +++ b/src/v0/sources/shopify/transform.js @@ -16,7 +16,6 @@ const { const { RedisDB } = require('../../../util/redis/redisConnector'); const { removeUndefinedAndNullValues, isDefinedAndNotNull } = require('../../util'); const Message = require('../message'); -const logger = require('../../../logger'); const { EventType } = require('../../../constants'); const { INTEGERATION, @@ -206,7 +205,7 @@ const processEvent = async (inputEvent, metricMetadata) => { }; const isIdentifierEvent = (event) => ['rudderIdentifier', 'rudderSessionIdentifier'].includes(event?.event); -const processIdentifierEvent = async (event, metricMetadata) => { +const processIdentifierEvent = async (event, metricMetadata, logger) => { if (useRedisDatabase) { let value; let field; @@ -256,13 +255,13 @@ const processIdentifierEvent = async (event, metricMetadata) => { } return NO_OPERATION_SUCCESS; }; -const process = async (event) => { +const process = async (event, logger) => { const metricMetadata = { writeKey: event.query_parameters?.writeKey?.[0], source: 'SHOPIFY', }; if (isIdentifierEvent(event)) { - return processIdentifierEvent(event, metricMetadata); + return processIdentifierEvent(event, metricMetadata, logger); } const response = await processEvent(event, metricMetadata); return response; diff --git a/src/v0/sources/shopify/util.js b/src/v0/sources/shopify/util.js index c4bbb61b9c..3dc54cc434 100644 --- a/src/v0/sources/shopify/util.js +++ b/src/v0/sources/shopify/util.js @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ const { v5 } = require('uuid'); const sha256 = require('sha256'); -const { TransformationError } = require('@rudderstack/integrations-lib'); +const { TransformationError, structuredLogger: logger } = require('@rudderstack/integrations-lib'); const stats = require('../../../util/stats'); const { constructPayload, @@ -12,7 +12,6 @@ const { isDefinedAndNotNull, } = require('../../util'); const { RedisDB } = require('../../../util/redis/redisConnector'); -const logger = require('../../../logger'); const { lineItemsMappingJSON, productMappingJSON, diff --git a/test/__tests__/pinterestConversion-cdk.test.ts b/test/__tests__/pinterestConversion-cdk.test.ts index f4da92eea9..6aaa710ed7 100644 --- a/test/__tests__/pinterestConversion-cdk.test.ts +++ b/test/__tests__/pinterestConversion-cdk.test.ts @@ -1,6 +1,7 @@ +import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import fs from 'fs'; import path from 'path'; -import { processCdkV2Workflow, getWorkflowEngine, executeWorkflow } from '../../src/cdk/v2/handler'; +import { executeWorkflow, getWorkflowEngine, processCdkV2Workflow } from '../../src/cdk/v2/handler'; import tags from '../../src/v0/util/tags'; const integration = 'pinterest_tag'; @@ -22,7 +23,12 @@ describe(`${name} Tests`, () => { it(`${name} - payload: ${index}`, async () => { const expected = expectedData[index]; try { - const output = await processCdkV2Workflow(integration, input, tags.FEATURES.PROCESSOR); + const output = await processCdkV2Workflow( + integration, + input, + tags.FEATURES.PROCESSOR, + logger, + ); expect(output).toEqual(expected); } catch (error: any) { expect(error.message).toEqual(expected.error); @@ -46,7 +52,12 @@ describe(`${name} Tests`, () => { it(`${name} - payload: ${index}`, async () => { const expected = expectedData[index]; try { - const output = await processCdkV2Workflow(integration, input, tags.FEATURES.PROCESSOR); + const output = await processCdkV2Workflow( + integration, + input, + tags.FEATURES.PROCESSOR, + logger, + ); expect(output).toEqual(expected); } catch (error: any) { expect(error.message).toEqual(expected.error); @@ -91,6 +102,7 @@ describe(`${name} Tests`, () => { integration, inputRouterErrorData, tags.FEATURES.ROUTER, + logger, ); expect(output).toEqual(expectedRouterErrorData); }); @@ -98,7 +110,12 @@ describe(`${name} Tests`, () => { describe('Default Batch size', () => { inputRouterData.forEach((input, index) => { it(`Payload: ${index}`, async () => { - const output = await processCdkV2Workflow(integration, input, tags.FEATURES.ROUTER); + const output = await processCdkV2Workflow( + integration, + input, + tags.FEATURES.ROUTER, + logger, + ); expect(output).toEqual(expectedRouterData[index]); }); }); diff --git a/test/apitests/service.api.test.ts b/test/apitests/service.api.test.ts index 266619b6ac..e46357f824 100644 --- a/test/apitests/service.api.test.ts +++ b/test/apitests/service.api.test.ts @@ -1,13 +1,13 @@ import fs from 'fs'; -import path from 'path'; -import request from 'supertest'; import { createHttpTerminator } from 'http-terminator'; import Koa from 'koa'; import bodyParser from 'koa-bodyparser'; +import path from 'path'; import setValue from 'set-value'; -import { applicationRoutes } from '../../src/routes'; -import { FetchHandler } from '../../src/helpers/fetchHandlers'; +import request from 'supertest'; import networkHandlerFactory from '../../src/adapters/networkHandlerFactory'; +import { FetchHandler } from '../../src/helpers/fetchHandlers'; +import { applicationRoutes } from '../../src/routes'; let server: any; const OLD_ENV = process.env; From 007009f7a6da2576f340ce965a4d6f190def7760 Mon Sep 17 00:00:00 2001 From: Siva Shanmukh Vetcha Date: Tue, 9 Apr 2024 10:20:22 -0400 Subject: [PATCH 096/240] chore: add logs on dns errors (#3257) --- src/util/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/utils.js b/src/util/utils.js index 0ba6008368..d74603dd7a 100644 --- a/src/util/utils.js +++ b/src/util/utils.js @@ -22,6 +22,7 @@ const staticLookup = (transformerVersionId) => async (hostname, _, cb) => { try { ips = await resolver.resolve4(hostname); } catch (error) { + logger.error(`DNS Error Code: ${error.code} | Message : ${error.message}`); stats.timing('fetch_dns_resolve_time', resolveStartTime, { transformerVersionId, error: 'true', From 3399c47fdce1b3d19e29306ca3c5692a2fbc30fb Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Fri, 12 Apr 2024 12:58:54 +0530 Subject: [PATCH 097/240] fix: adding check for reserved key words in extract custom fields (#3264) * fix: adding check for reserved key words * fix: review comments addressed --- src/v0/util/index.js | 13 ++- src/v0/util/index.test.js | 184 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+), 2 deletions(-) diff --git a/src/v0/util/index.js b/src/v0/util/index.js index 32872cc5d9..ac1bacf404 100644 --- a/src/v0/util/index.js +++ b/src/v0/util/index.js @@ -1328,12 +1328,19 @@ const generateExclusionList = (mappingConfig) => */ function extractCustomFields(message, payload, keys, exclusionFields) { const mappingKeys = []; + // Define reserved words + const reservedWords = ['__proto__', 'constructor', 'prototype']; + + const isReservedWord = (key) => reservedWords.includes(key); + if (Array.isArray(keys)) { keys.forEach((key) => { const messageContext = get(message, key); if (messageContext) { Object.keys(messageContext).forEach((k) => { - if (!exclusionFields.includes(k)) mappingKeys.push(k); + if (!exclusionFields.includes(k) && !isReservedWord(k)) { + mappingKeys.push(k); + } }); mappingKeys.forEach((mappingKey) => { if (!(typeof messageContext[mappingKey] === 'undefined')) { @@ -1344,7 +1351,9 @@ function extractCustomFields(message, payload, keys, exclusionFields) { }); } else if (keys === 'root') { Object.keys(message).forEach((k) => { - if (!exclusionFields.includes(k)) mappingKeys.push(k); + if (!exclusionFields.includes(k) && !isReservedWord(k)) { + mappingKeys.push(k); + } }); mappingKeys.forEach((mappingKey) => { if (!(typeof message[mappingKey] === 'undefined')) { diff --git a/src/v0/util/index.test.js b/src/v0/util/index.test.js index 810eb5a9d4..c34d513325 100644 --- a/src/v0/util/index.test.js +++ b/src/v0/util/index.test.js @@ -506,3 +506,187 @@ describe('validateEventAndLowerCaseConversion Tests', () => { }).toThrow(InstrumentationError); }); }); + +describe('extractCustomFields', () => { + // Handle reserved words in message keys + it('should handle reserved word "prototype" in message keys when keys are provided', () => { + const message = { + traits: { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + prototype: 'reserved', + }, + context: { + traits: { + phone: '1234567890', + city: 'New York', + country: 'USA', + prototype: 'reserved', + }, + }, + properties: { + title: 'Developer', + organization: 'ABC Company', + zip: '12345', + prototype: 'reserved', + }, + }; + + const payload = {}; + + const keys = ['properties', 'context.traits', 'traits']; + + const exclusionFields = [ + 'firstName', + 'lastName', + 'phone', + 'title', + 'organization', + 'city', + 'region', + 'country', + 'zip', + 'image', + 'timezone', + ]; + + const result = utilities.extractCustomFields(message, payload, keys, exclusionFields); + + expect(result).toEqual({ + email: 'john.doe@example.com', + }); + }); + + it('should handle reserved word "__proto__" in message keys when keys are provided', () => { + const message = { + traits: { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + __proto__: 'reserved', + }, + context: { + traits: { + phone: '1234567890', + city: 'New York', + country: 'USA', + __proto__: 'reserved', + }, + }, + properties: { + title: 'Developer', + organization: 'ABC Company', + zip: '12345', + __proto__: 'reserved', + }, + }; + + const payload = {}; + + const keys = ['properties', 'context.traits', 'traits']; + + const exclusionFields = [ + 'firstName', + 'lastName', + 'phone', + 'title', + 'organization', + 'city', + 'region', + 'country', + 'zip', + 'image', + 'timezone', + ]; + const result = utilities.extractCustomFields(message, payload, keys, exclusionFields); + expect(result).toEqual({ + email: 'john.doe@example.com', + }); + }); + + it('should handle reserved word "constructor" in message keys when keys are provided', () => { + const message = { + traits: { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + constructor: 'reserved', + }, + context: { + traits: { + phone: '1234567890', + city: 'New York', + country: 'USA', + constructor: 'reserved', + }, + }, + properties: { + title: 'Developer', + organization: 'ABC Company', + zip: '12345', + constructor: 'reserved', + }, + }; + + const payload = {}; + + const keys = ['properties', 'context.traits', 'traits']; + + const exclusionFields = [ + 'firstName', + 'lastName', + 'phone', + 'title', + 'organization', + 'city', + 'region', + 'country', + 'zip', + 'image', + 'timezone', + ]; + const result = utilities.extractCustomFields(message, payload, keys, exclusionFields); + expect(result).toEqual({ + email: 'john.doe@example.com', + }); + }); + + it('should handle reserved words in message keys when key is root', () => { + const message = { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + prototype: 'reserved', + phone: '1234567890', + city: 'New York', + country: 'USA', + __proto__: 'reserved', + constructor: 'reserved', + }; + + const payload = {}; + + const keys = 'root'; + + const exclusionFields = [ + 'firstName', + 'lastName', + 'phone', + 'title', + 'organization', + 'city', + 'region', + 'country', + 'zip', + 'image', + 'timezone', + ]; + + const result = utilities.extractCustomFields(message, payload, keys, exclusionFields); + + expect(result).toEqual({ + email: 'john.doe@example.com', + }); + }); +}); From 3721a4475a7caa89635839b2234e0574ffeea69b Mon Sep 17 00:00:00 2001 From: Abhimanyu Babbar Date: Sat, 13 Apr 2024 00:59:38 +0530 Subject: [PATCH 098/240] chore: setup transformer code with openfaas pro deployment changes (#3180) --- src/util/openfaas/faasApi.js | 36 +++++++++++++++------- src/util/openfaas/index.js | 12 +++++++- test/__tests__/user_transformation.test.js | 22 +++++++++---- 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/src/util/openfaas/faasApi.js b/src/util/openfaas/faasApi.js index 4db5e2a81c..f8f830f6e4 100644 --- a/src/util/openfaas/faasApi.js +++ b/src/util/openfaas/faasApi.js @@ -2,6 +2,13 @@ const axios = require('axios'); const { RespStatusError, RetryRequestError } = require('../utils'); const OPENFAAS_GATEWAY_URL = process.env.OPENFAAS_GATEWAY_URL || 'http://localhost:8080'; +const OPENFAAS_GATEWAY_USERNAME = process.env.OPENFAAS_GATEWAY_USERNAME || ''; +const OPENFAAS_GATEWAY_PASSWORD = process.env.OPENFAAS_GATEWAY_PASSWORD || ''; + +const basicAuth = { + username: OPENFAAS_GATEWAY_USERNAME, + password: OPENFAAS_GATEWAY_PASSWORD, +}; const parseAxiosError = (error) => { if (error.response) { @@ -21,7 +28,7 @@ const deleteFunction = async (functionName) => new Promise((resolve, reject) => { const url = `${OPENFAAS_GATEWAY_URL}/system/functions`; axios - .delete(url, { data: { functionName } }) + .delete(url, { data: { functionName }, auth: basicAuth }) .then(() => resolve()) .catch((err) => reject(parseAxiosError(err))); }); @@ -30,7 +37,7 @@ const getFunction = async (functionName) => new Promise((resolve, reject) => { const url = `${OPENFAAS_GATEWAY_URL}/system/function/${functionName}`; axios - .get(url) + .get(url, { auth: basicAuth }) .then((resp) => resolve(resp.data)) .catch((err) => reject(parseAxiosError(err))); }); @@ -39,7 +46,7 @@ const getFunctionList = async () => new Promise((resolve, reject) => { const url = `${OPENFAAS_GATEWAY_URL}/system/functions`; axios - .get(url) + .get(url, { auth: basicAuth }) .then((resp) => resolve(resp.data)) .catch((err) => reject(parseAxiosError(err))); }); @@ -48,29 +55,36 @@ const invokeFunction = async (functionName, payload) => new Promise((resolve, reject) => { const url = `${OPENFAAS_GATEWAY_URL}/function/${functionName}`; axios - .post(url, payload) + .post(url, payload, { auth: basicAuth }) .then((resp) => resolve(resp.data)) .catch((err) => reject(parseAxiosError(err))); }); -const checkFunctionHealth = async (functionName) => - new Promise((resolve, reject) => { +const checkFunctionHealth = async (functionName) => { + return new Promise((resolve, reject) => { const url = `${OPENFAAS_GATEWAY_URL}/function/${functionName}`; axios - .get(url, { - headers: { 'X-REQUEST-TYPE': 'HEALTH-CHECK' }, - }) + .get( + url, + { + headers: { 'X-REQUEST-TYPE': 'HEALTH-CHECK' }, + }, + { auth: basicAuth }, + ) .then((resp) => resolve(resp)) .catch((err) => reject(parseAxiosError(err))); }); +}; const deployFunction = async (payload) => new Promise((resolve, reject) => { const url = `${OPENFAAS_GATEWAY_URL}/system/functions`; axios - .post(url, payload) + .post(url, payload, { auth: basicAuth }) .then((resp) => resolve(resp.data)) - .catch((err) => reject(parseAxiosError(err))); + .catch((err) => { + reject(parseAxiosError(err)); + }); }); module.exports = { diff --git a/src/util/openfaas/index.js b/src/util/openfaas/index.js index 00b3720e13..7a1fce3cfa 100644 --- a/src/util/openfaas/index.js +++ b/src/util/openfaas/index.js @@ -11,6 +11,11 @@ const stats = require('../stats'); const { getMetadata, getTransformationMetadata } = require('../../v0/util'); const { HTTP_STATUS_CODES } = require('../../v0/util/constant'); +const FAAS_SCALE_TYPE = process.env.FAAS_SCALE_TYPE || 'capacity'; +const FAAS_SCALE_TARGET = process.env.FAAS_SCALE_TARGET || '4'; +const FAAS_SCALE_TARGET_PROPORTION = process.env.FAAS_SCALE_TARGET_PROPORTION || '0.70'; +const FAAS_SCALE_ZERO = process.env.FAAS_SCALE_ZERO || 'false'; +const FAAS_SCALE_ZERO_DURATION = process.env.FAAS_SCALE_ZERO_DURATION || '15m'; const FAAS_BASE_IMG = process.env.FAAS_BASE_IMG || 'rudderlabs/openfaas-flask:main'; const FAAS_MAX_PODS_IN_TEXT = process.env.FAAS_MAX_PODS_IN_TEXT || '40'; const FAAS_MIN_PODS_IN_TEXT = process.env.FAAS_MIN_PODS_IN_TEXT || '1'; @@ -125,7 +130,7 @@ const deployFaasFunction = async ( trMetadata = {}, ) => { try { - logger.debug('[Faas] Deploying a faas function'); + logger.debug(`[Faas] Deploying a faas function: ${functionName}`); let envProcess = 'python index.py'; const lvidsString = libraryVersionIDs.join(','); @@ -150,6 +155,11 @@ const deployFaasFunction = async ( 'parent-component': 'openfaas', 'com.openfaas.scale.max': FAAS_MAX_PODS_IN_TEXT, 'com.openfaas.scale.min': FAAS_MIN_PODS_IN_TEXT, + 'com.openfaas.scale.zero': FAAS_SCALE_ZERO, + 'com.openfaas.scale.zero-duration': FAAS_SCALE_ZERO_DURATION, + 'com.openfaas.scale.target': FAAS_SCALE_TARGET, + 'com.openfaas.scale.target-proportion': FAAS_SCALE_TARGET_PROPORTION, + 'com.openfaas.scale.type': FAAS_SCALE_TYPE, transformationId: trMetadata.transformationId, workspaceId: trMetadata.workspaceId, team: 'data-management', diff --git a/test/__tests__/user_transformation.test.js b/test/__tests__/user_transformation.test.js index 8b781cda9a..924bf4f791 100644 --- a/test/__tests__/user_transformation.test.js +++ b/test/__tests__/user_transformation.test.js @@ -37,6 +37,10 @@ const { parserForImport } = require("../../src/util/parser"); const { RetryRequestError, RespStatusError } = require("../../src/util/utils"); const OPENFAAS_GATEWAY_URL = "http://localhost:8080"; +const defaultBasicAuth = { + "username": "", + "password": "" +}; const randomID = () => Math.random() @@ -1400,12 +1404,14 @@ describe("Python transformations", () => { expect(axios.post).toHaveBeenCalledTimes(1); expect(axios.post).toHaveBeenCalledWith( `${OPENFAAS_GATEWAY_URL}/system/functions`, - expect.objectContaining({ name: funcName, service: funcName }) + expect.objectContaining({ name: funcName, service: funcName }), + { auth: defaultBasicAuth }, ); expect(axios.get).toHaveBeenCalledTimes(1); expect(axios.get).toHaveBeenCalledWith( `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, - {"headers": {"X-REQUEST-TYPE": "HEALTH-CHECK"}} + {"headers": {"X-REQUEST-TYPE": "HEALTH-CHECK"}}, + { auth: defaultBasicAuth }, ); }); @@ -1622,7 +1628,8 @@ describe("Python transformations", () => { expect(axios.post).toHaveBeenCalledTimes(1); expect(axios.post).toHaveBeenCalledWith( `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, - inputData + inputData, + { auth: defaultBasicAuth }, ); }); @@ -1655,17 +1662,20 @@ describe("Python transformations", () => { expect(axios.post).toHaveBeenCalledTimes(2); expect(axios.post).toHaveBeenCalledWith( `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, - inputData + inputData, + { auth: defaultBasicAuth }, ); expect(axios.post).toHaveBeenCalledWith( `${OPENFAAS_GATEWAY_URL}/system/functions`, - expect.objectContaining({ name: funcName, service: funcName }) + expect.objectContaining({ name: funcName, service: funcName }), + { auth: defaultBasicAuth }, ); expect(axios.get).toHaveBeenCalledTimes(1); expect(axios.get).toHaveBeenCalledWith( `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, - {"headers": {"X-REQUEST-TYPE": "HEALTH-CHECK"}} + {"headers": {"X-REQUEST-TYPE": "HEALTH-CHECK"}}, + { auth: defaultBasicAuth }, ); }); From cb8ff2fb943c49df4ac083bd179d9674b40eb602 Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:11:44 +0530 Subject: [PATCH 099/240] fix: impact: support custom product mapping (#3249) * fix: impact: support custom product mapping * chore: add util test file * chore: address comments --- src/v0/destinations/impact/util.js | 9 ++ src/v0/destinations/impact/utils.test.js | 134 ++++++++++++++++++ .../destinations/impact/processor/data.ts | 6 + 3 files changed, 149 insertions(+) create mode 100644 src/v0/destinations/impact/utils.test.js diff --git a/src/v0/destinations/impact/util.js b/src/v0/destinations/impact/util.js index b8c6e92e76..8fb43a8df6 100644 --- a/src/v0/destinations/impact/util.js +++ b/src/v0/destinations/impact/util.js @@ -78,6 +78,7 @@ const populateProductProperties = (productsMapping, properties) => { const productProperties = {}; if (products && Array.isArray(products)) { products.forEach((item, index) => { + // Following product properties have a default mapping as well in config.js as itemMapping productProperties[getPropertyName('ItemBrand', index + 1)] = item[getProductsMapping(productsMapping, 'ItemBrand')]; productProperties[getPropertyName('ItemCategory', index + 1)] = @@ -92,8 +93,16 @@ const populateProductProperties = (productsMapping, properties) => { item[getProductsMapping(productsMapping, 'ItemQuantity')]; productProperties[getPropertyName('ItemSku', index + 1)] = item[getProductsMapping(productsMapping, 'ItemSku')]; + + // Following product properties are build from configuration in RudderStack dashboard + if (productsMapping && Array.isArray(productsMapping)) { + productsMapping.forEach((mapping) => { + productProperties[getPropertyName(mapping.to, index + 1)] = item[mapping.from]; + }); + } }); } else { + // Not providing product level mapping here as following are fetched from properties level const index = 1; productProperties[getPropertyName('ItemBrand', index)] = brand; productProperties[getPropertyName('ItemCategory', index)] = category; diff --git a/src/v0/destinations/impact/utils.test.js b/src/v0/destinations/impact/utils.test.js new file mode 100644 index 0000000000..17f90bbc5d --- /dev/null +++ b/src/v0/destinations/impact/utils.test.js @@ -0,0 +1,134 @@ +const { populateProductProperties } = require('./util'); +// Generated by CodiumAI + +describe('populateProductProperties', () => { + // Given a valid productsMapping and properties, it should return product properties with default mappings and configured mappings from RudderStack dashboard + it('should return product properties with default and configured mappings', () => { + // Arrange + const productsMapping = [ + { to: 'ItemBrand', from: 'Brand' }, + { to: 'ItemCategory', from: 'Category' }, + { to: 'ItemName', from: 'Name' }, + { to: 'ItemPrice', from: 'Price' }, + { to: 'ItemPromoCode', from: 'PromoCode' }, + { to: 'ItemQuantity', from: 'Quantity' }, + { to: 'ItemSku', from: 'Sku' }, + { to: 'dummyRHS', from: 'dummyLHS' }, + ]; + const properties = { + products: [ + { + Brand: 'Brand1', + Category: 'Category1', + Name: 'Name1', + Price: 10, + PromoCode: 'PromoCode1', + Quantity: 1, + Sku: 'Sku1', + dummyLHS: 'DummyValue', + }, + { + Brand: 'Brand2', + Category: 'Category2', + Name: 'Name2', + Price: 20, + PromoCode: 'PromoCode2', + Quantity: 2, + }, + ], + brand: 'Brand3', + category: 'Category3', + name: 'Name3', + price: 30, + coupon: 'PromoCode3', + quantity: 3, + sku: 'Sku3', + }; + + // Act + const result = populateProductProperties(productsMapping, properties); + + // Assert + expect(result).toEqual({ + ItemBrand1: 'Brand1', + ItemCategory1: 'Category1', + ItemName1: 'Name1', + ItemPrice1: 10, + ItemPromoCode1: 'PromoCode1', + ItemQuantity1: 1, + ItemSku1: 'Sku1', + ItemBrand2: 'Brand2', + ItemCategory2: 'Category2', + ItemName2: 'Name2', + ItemPrice2: 20, + ItemPromoCode2: 'PromoCode2', + ItemQuantity2: 2, + dummyRHS1: 'DummyValue', + }); + }); + + // Given an empty productsMapping and valid properties, it should return product properties with default mappings from properties + it('should return product properties with default mappings from properties when products array is not available', () => { + // Arrange + const productsMapping = []; + const properties = { + brand: 'Brand3', + category: 'Category3', + name: 'Name3', + price: 30, + coupon: 'PromoCode3', + quantity: 3, + sku: 'Sku3', + }; + + // Act + const result = populateProductProperties(productsMapping, properties); + + // Assert + expect(result).toEqual({ + ItemBrand1: 'Brand3', + ItemCategory1: 'Category3', + ItemName1: 'Name3', + ItemPrice1: 30, + ItemPromoCode1: 'PromoCode3', + ItemQuantity1: 3, + ItemSku1: 'Sku3', + }); + }); + it('should return product properties with custom mappings', () => { + const productsMapping = [ + { + from: 'dummy_LHS', + to: 'dummy_RHS', + }, + ]; + const properties = { + products: [ + { + brand: 'Brand3', + category: 'Category3', + name: 'Name3', + price: 30, + coupon: 'PromoCode3', + quantity: 3, + sku: 'Sku3', + dummy_LHS: 'DummyValue', + }, + ], + }; + + const result = populateProductProperties(productsMapping, properties); + + // Assert + expect(result).toEqual({ + ItemBrand1: 'Brand3', + ItemCategory1: 'Category3', + ItemName1: 'Name3', + ItemPrice1: 30, + ItemPromoCode1: 'PromoCode3', + ItemQuantity1: 3, + ItemSku1: 'Sku3', + dummy_RHS1: 'DummyValue', + }); + }); +}); diff --git a/test/integrations/destinations/impact/processor/data.ts b/test/integrations/destinations/impact/processor/data.ts index e467956d62..1e4e91e7ad 100644 --- a/test/integrations/destinations/impact/processor/data.ts +++ b/test/integrations/destinations/impact/processor/data.ts @@ -891,6 +891,7 @@ export const data = [ price: 332, quantity: 1, sku: 'G-32', + customRSProductField: 'customRSVal', }, ], }, @@ -941,6 +942,10 @@ export const data = [ from: 'variant', to: 'ItemCategory', }, + { + from: 'customRSProductField', + to: 'customImpactProductField', + }, ], enableIdentifyEvents: false, enablePageEvents: false, @@ -990,6 +995,7 @@ export const data = [ DeviceLocale: 'en-US', EventTypeCode: 'Order Completed', ItemQuantity1: 1, + customImpactProductField1: 'customRSVal', OrderPromoCode: '10OFF-ROCKET', CustomProfileId: '97c46c81-3140-456d-b2a9-690d70aaca35', }, From 1bfcd27a21ad224880f59001efbac728e356afe1 Mon Sep 17 00:00:00 2001 From: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Date: Mon, 15 Apr 2024 12:02:07 +0530 Subject: [PATCH 100/240] refactor: deprecate mixpanel /track endpoint (#2833) * refactor: deprecate mixpanel /track endpoint * fix: used token in place of apiSecret for merge call * fix: no-case-declarations --- src/util/prometheus.js | 6 - src/v0/destinations/mp/config.js | 2 - src/v0/destinations/mp/transform.js | 69 ++-- src/v0/destinations/mp/util.js | 8 +- src/v0/destinations/mp/util.test.js | 14 - test/integrations/destinations/mp/common.ts | 2 +- .../destinations/mp/processor/data.ts | 360 ++++++++++-------- .../destinations/mp/router/data.ts | 12 +- 8 files changed, 243 insertions(+), 230 deletions(-) diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 882dff9e75..a46eae12c9 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -587,12 +587,6 @@ class Prometheus { type: 'gauge', labelNames: ['destination_id'], }, - { - name: 'mixpanel_batch_track_pack_size', - help: 'mixpanel_batch_track_pack_size', - type: 'gauge', - labelNames: ['destination_id'], - }, { name: 'mixpanel_batch_import_pack_size', help: 'mixpanel_batch_import_pack_size', diff --git a/src/v0/destinations/mp/config.js b/src/v0/destinations/mp/config.js index 35b40294f5..3abdf2eebb 100644 --- a/src/v0/destinations/mp/config.js +++ b/src/v0/destinations/mp/config.js @@ -49,7 +49,6 @@ const MP_IDENTIFY_EXCLUSION_LIST = [ ]; const GEO_SOURCE_ALLOWED_VALUES = [null, 'reverse_geocoding']; -const TRACK_MAX_BATCH_SIZE = 50; const IMPORT_MAX_BATCH_SIZE = 2000; const ENGAGE_MAX_BATCH_SIZE = 2000; const GROUPS_MAX_BATCH_SIZE = 200; @@ -68,7 +67,6 @@ module.exports = { MP_IDENTIFY_EXCLUSION_LIST, getCreateDeletionTaskEndpoint, DISTINCT_ID_MAX_BATCH_SIZE, - TRACK_MAX_BATCH_SIZE, IMPORT_MAX_BATCH_SIZE, ENGAGE_MAX_BATCH_SIZE, GROUPS_MAX_BATCH_SIZE, diff --git a/src/v0/destinations/mp/transform.js b/src/v0/destinations/mp/transform.js index a2c40a5672..195c42fbee 100644 --- a/src/v0/destinations/mp/transform.js +++ b/src/v0/destinations/mp/transform.js @@ -25,7 +25,6 @@ const { BASE_ENDPOINT, BASE_ENDPOINT_EU, IMPORT_MAX_BATCH_SIZE, - TRACK_MAX_BATCH_SIZE, ENGAGE_MAX_BATCH_SIZE, GROUPS_MAX_BATCH_SIZE, } = require('./config'); @@ -47,21 +46,19 @@ const mPEventPropertiesConfigJson = mappingConfig[ConfigCategory.EVENT_PROPERTIE const setImportCredentials = (destConfig) => { const endpoint = destConfig.dataResidency === 'eu' ? `${BASE_ENDPOINT_EU}/import/` : `${BASE_ENDPOINT}/import/`; - const headers = { 'Content-Type': 'application/json' }; const params = { strict: destConfig.strictMode ? 1 : 0 }; - const { apiSecret, serviceAccountUserName, serviceAccountSecret, projectId } = destConfig; - if (apiSecret) { - headers.Authorization = `Basic ${base64Convertor(`${apiSecret}:`)}`; + const { serviceAccountUserName, serviceAccountSecret, projectId, token } = destConfig; + let credentials; + if (token) { + credentials = `${token}:`; } else if (serviceAccountUserName && serviceAccountSecret && projectId) { - headers.Authorization = `Basic ${base64Convertor( - `${serviceAccountUserName}:${serviceAccountSecret}`, - )}`; + credentials = `${serviceAccountUserName}:${serviceAccountSecret}`; params.projectId = projectId; - } else { - throw new InstrumentationError( - 'Event timestamp is older than 5 days and no API secret or service account credentials (i.e. username, secret and projectId) are provided in destination configuration', - ); } + const headers = { + 'Content-Type': 'application/json', + Authorization: `Basic ${base64Convertor(credentials)}`, + }; return { endpoint, headers, params }; }; @@ -70,37 +67,26 @@ const responseBuilderSimple = (payload, message, eventType, destConfig) => { response.method = defaultPostRequestConfig.requestMethod; response.userId = message.userId || message.anonymousId; response.body.JSON_ARRAY = { batch: JSON.stringify([removeUndefinedValues(payload)]) }; - const { apiSecret, serviceAccountUserName, serviceAccountSecret, projectId, dataResidency } = - destConfig; + const { dataResidency } = destConfig; const duration = getTimeDifference(message.timestamp); switch (eventType) { case EventType.ALIAS: case EventType.TRACK: case EventType.SCREEN: - case EventType.PAGE: - if ( - !apiSecret && - !(serviceAccountUserName && serviceAccountSecret && projectId) && - duration.days <= 5 - ) { - response.endpoint = - dataResidency === 'eu' ? `${BASE_ENDPOINT_EU}/track/` : `${BASE_ENDPOINT}/track/`; - response.headers = {}; - } else if (duration.years > 5) { + case EventType.PAGE: { + if (duration.years > 5) { throw new InstrumentationError('Event timestamp should be within last 5 years'); - } else { - const credentials = setImportCredentials(destConfig); - response.endpoint = credentials.endpoint; - response.headers = credentials.headers; - response.params = { - project_id: credentials.params?.projectId, - strict: credentials.params.strict, - }; - break; } + const credentials = setImportCredentials(destConfig); + response.endpoint = credentials.endpoint; + response.headers = credentials.headers; + response.params = { + project_id: credentials.params?.projectId, + strict: credentials.params.strict, + }; break; - case 'merge': - // eslint-disable-next-line no-case-declarations + } + case 'merge': { const credentials = setImportCredentials(destConfig); response.endpoint = credentials.endpoint; response.headers = credentials.headers; @@ -109,6 +95,7 @@ const responseBuilderSimple = (payload, message, eventType, destConfig) => { strict: credentials.params.strict, }; break; + } default: response.endpoint = dataResidency === 'eu' ? `${BASE_ENDPOINT_EU}/engage/` : `${BASE_ENDPOINT}/engage/`; @@ -483,7 +470,6 @@ const processRouterDest = async (inputs, reqMetadata) => { const batchSize = { engage: 0, groups: 0, - track: 0, import: 0, }; @@ -515,23 +501,16 @@ const processRouterDest = async (inputs, reqMetadata) => { ); transformedPayloads = lodash.flatMap(transformedPayloads); - const { engageEvents, groupsEvents, trackEvents, importEvents, batchErrorRespList } = + const { engageEvents, groupsEvents, importEvents, batchErrorRespList } = groupEventsByEndpoint(transformedPayloads); const engageRespList = batchEvents(engageEvents, ENGAGE_MAX_BATCH_SIZE, reqMetadata); const groupsRespList = batchEvents(groupsEvents, GROUPS_MAX_BATCH_SIZE, reqMetadata); - const trackRespList = batchEvents(trackEvents, TRACK_MAX_BATCH_SIZE, reqMetadata); const importRespList = batchEvents(importEvents, IMPORT_MAX_BATCH_SIZE, reqMetadata); - const batchSuccessRespList = [ - ...engageRespList, - ...groupsRespList, - ...trackRespList, - ...importRespList, - ]; + const batchSuccessRespList = [...engageRespList, ...groupsRespList, ...importRespList]; batchSize.engage += engageRespList.length; batchSize.groups += groupsRespList.length; - batchSize.track += trackRespList.length; batchSize.import += importRespList.length; return [...batchSuccessRespList, ...batchErrorRespList]; diff --git a/src/v0/destinations/mp/util.js b/src/v0/destinations/mp/util.js index d564e805ad..b2807d6e11 100644 --- a/src/v0/destinations/mp/util.js +++ b/src/v0/destinations/mp/util.js @@ -136,7 +136,7 @@ const createIdentifyResponse = (message, type, destination, responseBuilderSimpl * @returns */ const isImportAuthCredentialsAvailable = (destination) => - destination.Config.apiSecret || + destination.Config.token || (destination.Config.serviceAccountSecret && destination.Config.serviceAccountUserName && destination.Config.projectId); @@ -179,7 +179,6 @@ const groupEventsByEndpoint = (events) => { const eventMap = { engage: [], groups: [], - track: [], import: [], }; const batchErrorRespList = []; @@ -204,7 +203,6 @@ const groupEventsByEndpoint = (events) => { return { engageEvents: eventMap.engage, groupsEvents: eventMap.groups, - trackEvents: eventMap.track, importEvents: eventMap.import, batchErrorRespList, }; @@ -349,7 +347,6 @@ const generatePageOrScreenCustomEventName = (message, userDefinedEventTemplate) * @param {Object} batchSize - The object containing the batch size for different endpoints. * @param {number} batchSize.engage - The batch size for engage endpoint. * @param {number} batchSize.groups - The batch size for group endpoint. - * @param {number} batchSize.track - The batch size for track endpoint. * @param {number} batchSize.import - The batch size for import endpoint. * @param {string} destinationId - The ID of the destination. * @returns {void} @@ -361,9 +358,6 @@ const recordBatchSizeMetrics = (batchSize, destinationId) => { stats.gauge('mixpanel_batch_group_pack_size', batchSize.groups, { destination_id: destinationId, }); - stats.gauge('mixpanel_batch_track_pack_size', batchSize.track, { - destination_id: destinationId, - }); stats.gauge('mixpanel_batch_import_pack_size', batchSize.import, { destination_id: destinationId, }); diff --git a/src/v0/destinations/mp/util.test.js b/src/v0/destinations/mp/util.test.js index 40cdb34649..3666081f59 100644 --- a/src/v0/destinations/mp/util.test.js +++ b/src/v0/destinations/mp/util.test.js @@ -18,7 +18,6 @@ describe('Unit test cases for groupEventsByEndpoint', () => { expect(result).toEqual({ engageEvents: [], groupsEvents: [], - trackEvents: [], importEvents: [], batchErrorRespList: [], }); @@ -122,19 +121,6 @@ describe('Unit test cases for groupEventsByEndpoint', () => { }, }, ], - trackEvents: [ - { - message: { - endpoint: '/track', - body: { - JSON_ARRAY: { - batch: '[{prop:4}]', - }, - }, - userId: 'user1', - }, - }, - ], importEvents: [ { message: { diff --git a/test/integrations/destinations/mp/common.ts b/test/integrations/destinations/mp/common.ts index 82f0e3202b..d40afa0c02 100644 --- a/test/integrations/destinations/mp/common.ts +++ b/test/integrations/destinations/mp/common.ts @@ -7,7 +7,7 @@ const defaultMockFns = () => { const sampleDestination: Destination = { Config: { apiKey: 'dummyApiKey', - token: 'dummyApiKey', + token: 'test_api_token', prefixProperties: true, useNativeSDK: false, }, diff --git a/test/integrations/destinations/mp/processor/data.ts b/test/integrations/destinations/mp/processor/data.ts index 5b2d0fbfff..db5bc840c2 100644 --- a/test/integrations/destinations/mp/processor/data.ts +++ b/test/integrations/destinations/mp/processor/data.ts @@ -12,7 +12,7 @@ export const data = [ request: { body: [ { - destination: overrideDestination(sampleDestination, { token: 'dummyApiKey' }), + destination: overrideDestination(sampleDestination, { token: 'test_api_token' }), message: { anonymousId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', channel: 'web', @@ -87,14 +87,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","campaign_id":"test_name","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","campaign_id":"test_name","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -191,14 +194,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Viewed a Contact Us page","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us","category":"Contact","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Viewed a Contact Us page","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us","category":"Contact","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -272,14 +278,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Screen","properties":{"category":"communication","ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', + '[{"event":"Loaded a Screen","properties":{"category":"communication","ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', }, XML: {}, FORM: {}, @@ -360,14 +369,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Screen","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","ip":"0.0.0.0","$user_id":"hjiklmk","$screen_dpi":2,"mp_lib":"RudderLabs Android SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjiklmk","time":1579847342402,"name":"Contact Us","category":"Contact"}}]', + '[{"event":"Loaded a Screen","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","ip":"0.0.0.0","$user_id":"hjiklmk","$screen_dpi":2,"mp_lib":"RudderLabs Android SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjiklmk","time":1579847342402,"name":"Contact Us","category":"Contact"}}]', }, XML: {}, FORM: {}, @@ -440,14 +452,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Screen","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', + '[{"event":"Loaded a Screen","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', }, XML: {}, FORM: {}, @@ -550,7 +565,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -664,7 +679,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":45.89}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":45.89}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -686,7 +701,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$add":{"counter":1,"item_purchased":"2"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$add":{"counter":1,"item_purchased":"2"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -701,14 +716,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":45.89,"counter":1,"item_purchased":"2","number_of_logins":"","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","campaign_id":"test_name","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342403,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":45.89,"counter":1,"item_purchased":"2","number_of_logins":"","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","campaign_id":"test_name","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342403,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -796,14 +814,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"dummyApiKey"}}]', + '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"test_api_token"}}]', }, XML: {}, FORM: {}, @@ -933,7 +954,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -948,14 +969,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1089,7 +1113,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":34}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":34}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -1104,14 +1128,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","revenue":34,"key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","first_name":"Mickey","lastName":"Mouse","name":"Mickey Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","revenue":34,"key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","first_name":"Mickey","lastName":"Mouse","name":"Mickey Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1235,14 +1262,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":" new Order Completed totally","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":" new Order Completed totally","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1366,14 +1396,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":" Order Completed ","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"Billing Amount":"77","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":" Order Completed ","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"Billing Amount":"77","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1541,7 +1574,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -1628,7 +1661,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', + '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -1650,7 +1683,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', + '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', }, XML: {}, FORM: {}, @@ -1737,7 +1770,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"company":["testComp","testComp1"]},"$ip":"0.0.0.0"}]', + '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"company":["testComp","testComp1"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -1759,7 +1792,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp","$set":{"company":["testComp","testComp1"]}}]', + '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":["testComp","testComp1"]}}]', }, XML: {}, FORM: {}, @@ -1781,7 +1814,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp1","$set":{"company":["testComp","testComp1"]}}]', + '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp1","$set":{"company":["testComp","testComp1"]}}]', }, XML: {}, FORM: {}, @@ -1869,7 +1902,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', + '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -1891,7 +1924,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', + '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', }, XML: {}, FORM: {}, @@ -2019,7 +2052,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -2034,14 +2067,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api-eu.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api-eu.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstname":"Mickey","lastname":"Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstname":"Mickey","lastname":"Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -2129,7 +2165,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","$android_devices":["test_device_token"],"$os":"Android","$android_model":"Android SDK built for x86","$android_os_version":"8.1.0","$android_manufacturer":"Google","$android_app_version":"1.0","$android_app_version_code":"1.0","$android_brand":"Google"},"$token":"dummyApiKey","$distinct_id":"5094f5704b9cf2b3","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","$android_devices":["test_device_token"],"$os":"Android","$android_model":"Android SDK built for x86","$android_os_version":"8.1.0","$android_manufacturer":"Google","$android_app_version":"1.0","$android_app_version_code":"1.0","$android_brand":"Google"},"$token":"test_api_token","$distinct_id":"5094f5704b9cf2b3","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -2216,7 +2252,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"dummyApiKey","$distinct_id":"test_user_id","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"test_api_token","$distinct_id":"test_user_id","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -2233,7 +2269,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -2241,7 +2277,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"dummyApiKey"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"test_api_token"}}]', }, XML: {}, FORM: {}, @@ -2328,14 +2364,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Page","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","category":"communication","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Loaded a Page","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","category":"communication","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -2423,14 +2462,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"dummyApiKey"}}]', + '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"test_api_token"}}]', }, XML: {}, FORM: {}, @@ -2523,7 +2565,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","createdat":"2020-01-23T08:54:02.362Z","$ios_devices":["test_device_token"],"$ios_device_model":"Android SDK built for x86","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"dummyApiKey","$distinct_id":"test_user_id","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","createdat":"2020-01-23T08:54:02.362Z","$ios_devices":["test_device_token"],"$ios_device_model":"Android SDK built for x86","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"test_api_token","$distinct_id":"test_user_id","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -2540,7 +2582,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -2548,7 +2590,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"dummyApiKey"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"test_api_token"}}]', }, XML: {}, FORM: {}, @@ -2641,7 +2683,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -2733,7 +2775,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -2826,7 +2868,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -2924,7 +2966,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3020,7 +3062,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3115,7 +3157,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3209,7 +3251,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3304,7 +3346,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3405,7 +3447,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3502,17 +3544,29 @@ export const data = [ status: 200, body: [ { - error: - 'Event timestamp is older than 5 days and no API secret or service account credentials (i.e. username, secret and projectId) are provided in destination configuration', - statTags: { - destType: 'MP', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, + body: { + JSON: {}, + JSON_ARRAY: { + batch: + '[{"event":"FirstTrackCall12","properties":{"foo":"bar","$deviceId":"nkasdnkasd","anonymousId":"ea776ad0-3136-44fb-9216-5b1578609a2b","userId":"as09sufa09usaf09as0f9uasf","id":"as09sufa09usaf09as0f9uasf","firstName":"Bob","lastName":"Marley","name":"Bob Marley","age":43,"email":"bob@marleymail.com","phone":"+447748544123","birthday":"1987-01-01T20:08:59+0000","createdAt":"2022-01-21T14:10:12+0000","address":"51,B.L.T road, Kolkata-700060","description":"I am great","gender":"male","title":"Founder","username":"bobm","website":"https://bobm.com","randomProperty":"randomValue","$user_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$current_url":"http://127.0.0.1:7307/Testing/App_for_testingTool/","$referrer":"http://127.0.0.1:7307/Testing/","$screen_height":900,"$screen_width":1440,"$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.1.18","$insert_id":"0d5c1a4a-27e4-41da-a246-4d01f44e74bd","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1632986123523,"$browser":"Chrome","$browser_version":"93.0.4577.82"}}]', + }, + XML: {}, + FORM: {}, + }, + files: {}, + userId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', }, - statusCode: 400, + statusCode: 200, }, ], }, @@ -3686,7 +3740,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -3694,7 +3748,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"MainActivity","properties":{"name":"MainActivity","automatic":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$user_id":"test_user_id","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"dummyApiKey","distinct_id":"test_user_id","time":1520845503421}}]', + '[{"event":"MainActivity","properties":{"name":"MainActivity","automatic":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$user_id":"test_user_id","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"test_api_token","distinct_id":"test_user_id","time":1520845503421}}]', }, XML: {}, FORM: {}, @@ -3965,7 +4019,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402,"$ignore_time":true}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402,"$ignore_time":true}]', }, XML: {}, FORM: {}, @@ -4071,7 +4125,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4276,7 +4330,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"user1234","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"user1234","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4294,15 +4348,14 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: - 'Basic cnVkZGVyLmQyYTNmMS5tcC1zZXJ2aWNlLWFjY291bnQ6amF0cFF4Y2pNaDhlZXRrMXhySDNLalFJYnp5NGlYOGI=', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, - params: { project_id: '123456', strict: 0 }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["user1234","e6ab2c5e-2cda-44a9-a962-e2f67df78bca"],"token":"dummyApiKey"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["user1234","e6ab2c5e-2cda-44a9-a962-e2f67df78bca"],"token":"test_api_token"}}]', }, XML: {}, FORM: {}, @@ -4387,7 +4440,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic ZHVtbXlBcGlLZXk6', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -4395,7 +4448,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', + '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', }, XML: {}, FORM: {}, @@ -4477,7 +4530,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic ZHVtbXlBcGlLZXk6', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -4485,7 +4538,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Opened","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', + '[{"event":"Application Opened","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', }, XML: {}, FORM: {}, @@ -4573,7 +4626,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"groupId":["testGroupId"]},"$ip":"0.0.0.0"}]', + '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"groupId":["testGroupId"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -4595,7 +4648,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"groupId","$group_id":"testGroupId","$set":{"company":"testComp","groupId":"groupIdInTraits"}}]', + '[{"$token":"test_api_token","$group_key":"groupId","$group_id":"testGroupId","$set":{"company":"testComp","groupId":"groupIdInTraits"}}]', }, XML: {}, FORM: {}, @@ -4622,7 +4675,7 @@ export const data = [ description: 'Track: set device id and user id when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -4678,14 +4731,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Product Viewed","properties":{"name":"T-Shirt","$user_id":"userId01","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"apiToken123","distinct_id":"userId01","time":1579847342402,"$device_id":"anonId01"}}]', + '[{"event":"Product Viewed","properties":{"name":"T-Shirt","$user_id":"userId01","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"test_api_token","distinct_id":"userId01","time":1579847342402,"$device_id":"anonId01"}}]', }, XML: {}, FORM: {}, @@ -4714,7 +4770,7 @@ export const data = [ { description: 'Identify: skip merge event when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -4795,7 +4851,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"apiToken123","$distinct_id":"userId01","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"userId01","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4823,7 +4879,7 @@ export const data = [ 'Identify: append $device: to deviceId while creating the user when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -4903,7 +4959,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"apiToken123","$distinct_id":"$device:anonId01","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"$device:anonId01","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4930,7 +4986,7 @@ export const data = [ description: 'Unsupported alias call when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -5019,7 +5075,7 @@ export const data = [ 'Track revenue event: set device id and user id when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -5090,7 +5146,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":18.9}},"$token":"apiToken123","$distinct_id":"userId01"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":18.9}},"$token":"test_api_token","$distinct_id":"userId01"}]', }, XML: {}, FORM: {}, @@ -5105,14 +5161,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":18.9,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$user_id":"userId01","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"apiToken123","distinct_id":"userId01","time":1579847342403,"$device_id":"anonId01","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":18.9,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$user_id":"userId01","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"test_api_token","distinct_id":"userId01","time":1579847342403,"$device_id":"anonId01","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -5142,7 +5201,7 @@ export const data = [ description: 'Page with anonymous user when simplified api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -5209,14 +5268,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"apiToken123","distinct_id":"$device:anonId01","time":1579847342402,"$device_id":"anonId01","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"$device:anonId01","time":1579847342402,"$device_id":"anonId01","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -5246,7 +5308,7 @@ export const data = [ description: 'Group call with anonymous user when simplified api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', groupKeySettings: [{ groupKey: 'company' }], }), @@ -5309,7 +5371,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"apiToken123","$distinct_id":"$device:anonId01","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', + '[{"$token":"test_api_token","$distinct_id":"$device:anonId01","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -5331,7 +5393,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"apiToken123","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', + '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', }, XML: {}, FORM: {}, @@ -5357,7 +5419,7 @@ export const data = [ { destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', groupKeySettings: [{ groupKey: 'company' }], }), @@ -5476,7 +5538,7 @@ export const data = [ }, destination: overrideDestination(sampleDestination, { apiKey: 'dummyApiKey', - token: 'dummyApiKey', + token: 'test_api_token', apiSecret: 'dummyApiKey', useNewMapping: true, }), @@ -5502,7 +5564,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":12.13}},"$token":"dummyApiKey","$distinct_id":"39da706ec83d0e90"}]', + '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":12.13}},"$token":"test_api_token","$distinct_id":"39da706ec83d0e90"}]', }, XML: {}, FORM: {}, @@ -5519,7 +5581,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic ZHVtbXlBcGlLZXk6', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -5527,7 +5589,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":12.13,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', + '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":12.13,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', }, XML: {}, FORM: {}, @@ -5591,7 +5653,7 @@ export const data = [ }, destination: overrideDestination(sampleDestination, { apiKey: 'dummyApiKey', - token: 'dummyApiKey', + token: 'test_api_token', apiSecret: 'dummyApiKey', useNewMapping: true, }), @@ -5617,7 +5679,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":23.45}},"$token":"dummyApiKey","$distinct_id":"39da706ec83d0e90"}]', + '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":23.45}},"$token":"test_api_token","$distinct_id":"39da706ec83d0e90"}]', }, XML: {}, FORM: {}, @@ -5634,7 +5696,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic ZHVtbXlBcGlLZXk6', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -5642,7 +5704,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":23.45,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":null}}]', + '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":23.45,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":null}}]', }, XML: {}, FORM: {}, @@ -5669,7 +5731,7 @@ export const data = [ description: 'Track: with strict mode enabled', destination: overrideDestination(sampleDestination, { apiKey: 'dummyApiKey', - token: 'dummyApiKey', + token: 'test_api_token', apiSecret: 'some_api_secret', dataResidency: 'eu', strictMode: true, @@ -5733,7 +5795,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"dummyApiKey","$distinct_id":"test_user_id","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"test_api_token","$distinct_id":"test_user_id","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -5750,7 +5812,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 1 }, @@ -5758,7 +5820,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"dummyApiKey"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"test_api_token"}}]', }, XML: {}, FORM: {}, diff --git a/test/integrations/destinations/mp/router/data.ts b/test/integrations/destinations/mp/router/data.ts index 059e222e92..8716c9daa0 100644 --- a/test/integrations/destinations/mp/router/data.ts +++ b/test/integrations/destinations/mp/router/data.ts @@ -442,7 +442,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { @@ -509,7 +509,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { @@ -577,7 +577,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { @@ -1166,7 +1166,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { @@ -1232,7 +1232,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { @@ -1299,7 +1299,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { From 0b5720446693efe1fd0ccdfc141bd7f21b2c32ae Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:57:09 +0530 Subject: [PATCH 101/240] fix: hubspot: search for contact using secondary prop (#3258) * fix: hubspot: search for contact using secondary prop * chore: include network.ts file * chore: address comments --- src/v0/destinations/hs/HSTransform-v2.js | 4 +- src/v0/destinations/hs/config.js | 4 + src/v0/destinations/hs/util.js | 113 ++++++++++++-- src/v0/destinations/hs/util.test.js | 14 +- test/integrations/destinations/hs/network.ts | 31 ++++ .../destinations/hs/router/data.ts | 141 ++++++++++++++++++ 6 files changed, 288 insertions(+), 19 deletions(-) diff --git a/src/v0/destinations/hs/HSTransform-v2.js b/src/v0/destinations/hs/HSTransform-v2.js index 2acdd82152..3699e1c789 100644 --- a/src/v0/destinations/hs/HSTransform-v2.js +++ b/src/v0/destinations/hs/HSTransform-v2.js @@ -12,7 +12,6 @@ const { defaultPatchRequestConfig, getFieldValueFromMessage, getSuccessRespEvents, - addExternalIdToTraits, defaultBatchRequestConfig, removeUndefinedAndNullValues, getDestinationExternalID, @@ -42,6 +41,7 @@ const { getEventAndPropertiesFromConfig, getHsSearchId, populateTraits, + addExternalIdToHSTraits, } = require('./util'); const { JSON_MIME_TYPE } = require('../../util/constant'); @@ -110,7 +110,7 @@ const processIdentify = async (message, destination, propertyMap) => { GENERIC_TRUE_VALUES.includes(mappedToDestination.toString()) && operation ) { - addExternalIdToTraits(message); + addExternalIdToHSTraits(message); if (!objectType) { throw new InstrumentationError('objectType not found'); } diff --git a/src/v0/destinations/hs/config.js b/src/v0/destinations/hs/config.js index fb9790f0e5..67ad3b5bed 100644 --- a/src/v0/destinations/hs/config.js +++ b/src/v0/destinations/hs/config.js @@ -84,6 +84,9 @@ const RETL_SOURCE = 'rETL'; const mappingConfig = getMappingConfig(ConfigCategory, __dirname); const hsCommonConfigJson = mappingConfig[ConfigCategory.COMMON.name]; +const primaryToSecondaryFields = { + email: 'hs_additional_emails', +}; module.exports = { BASE_ENDPOINT, CONTACT_PROPERTY_MAP_ENDPOINT, @@ -112,5 +115,6 @@ module.exports = { RETL_SOURCE, RETL_CREATE_ASSOCIATION_OPERATION, MAX_CONTACTS_PER_REQUEST, + primaryToSecondaryFields, DESTINATION: 'HS', }; diff --git a/src/v0/destinations/hs/util.js b/src/v0/destinations/hs/util.js index 838da08b3b..ffb2df5237 100644 --- a/src/v0/destinations/hs/util.js +++ b/src/v0/destinations/hs/util.js @@ -1,5 +1,6 @@ /* eslint-disable no-await-in-loop */ const lodash = require('lodash'); +const set = require('set-value'); const get = require('get-value'); const { NetworkInstrumentationError, @@ -28,6 +29,7 @@ const { IDENTIFY_CRM_SEARCH_ALL_OBJECTS, SEARCH_LIMIT_VALUE, hsCommonConfigJson, + primaryToSecondaryFields, DESTINATION, MAX_CONTACTS_PER_REQUEST, } = require('./config'); @@ -576,16 +578,30 @@ const performHubSpotSearch = async ( checkAfter = after; // assigning to the new value if no after we assign it to 0 and no more calls will take place const results = processedResponse.response?.results; + const extraProp = primaryToSecondaryFields[identifierType]; if (results) { searchResults.push( - ...results.map((result) => ({ - id: result.id, - property: result.properties[identifierType], - })), + ...results.map((result) => { + const contact = { + id: result.id, + property: result.properties[identifierType], + }; + // Following maps the extra property to the contact object which + // help us to know if the contact was found using secondary property + if (extraProp) { + contact[extraProp] = result.properties?.[extraProp]; + } + return contact; + }), ); } } - + /* + searchResults = { + id: 'existing_contact_id', + property: 'existing_contact_email', // when email is identifier + hs_additional_emails: ['secondary_email'] // when email is identifier + } */ return searchResults; }; @@ -612,7 +628,25 @@ const getRequestData = (identifierType, chunk) => { limit: SEARCH_LIMIT_VALUE, after: 0, }; - + /* In case of email as identifier we add a filter for hs_additional_emails field + * and append hs_additional_emails to properties list + * We are doing this because there might be emails exisitng as hs_additional_emails for some conatct but + * will not come up in search API until we search with hs_additional_emails as well. + * Not doing this resulted in erro 409 Duplicate records found + */ + const secondaryProp = primaryToSecondaryFields[identifierType]; + if (secondaryProp) { + requestData.filterGroups.push({ + filters: [ + { + propertyName: secondaryProp, + values: chunk, + operator: 'IN', + }, + ], + }); + requestData.properties.push(secondaryProp); + } return requestData; }; @@ -623,7 +657,7 @@ const getRequestData = (identifierType, chunk) => { */ const getExistingContactsData = async (inputs, destination) => { const { Config } = destination; - const updateHubspotIds = []; + const hsIdsToBeUpdated = []; const firstMessage = inputs[0].message; if (!firstMessage) { @@ -651,13 +685,19 @@ const getExistingContactsData = async (inputs, destination) => { destination, ); if (searchResults.length > 0) { - updateHubspotIds.push(...searchResults); + hsIdsToBeUpdated.push(...searchResults); } } - return updateHubspotIds; + return hsIdsToBeUpdated; }; - -const setHsSearchId = (input, id) => { +/** + * This functions sets HsSearchId in the externalId array + * @param {*} input -> Input message + * @param {*} id -> Id to be added + * @param {*} useSecondaryProp -> Let us know if that id was found using secondary property and not primnary + * @returns + */ +const setHsSearchId = (input, id, useSecondaryProp = false) => { const { message } = input; const resultExternalId = []; const externalIdArray = message.context?.externalId; @@ -668,6 +708,11 @@ const setHsSearchId = (input, id) => { if (type.includes(DESTINATION)) { extIdObjParam.hsSearchId = id; } + if (useSecondaryProp) { + // we are using it so that when final payload is made + // then primary key shouldn't be overidden + extIdObjParam.useSecondaryObject = useSecondaryProp; + } resultExternalId.push(extIdObjParam); }); } @@ -680,20 +725,24 @@ const setHsSearchId = (input, id) => { * We do search for all the objects before router transform and assign the type (create/update) * accordingly to context.hubspotOperation * + * For email as primary key we use `hs_additional_emails` as well property to search existing contacts * */ const splitEventsForCreateUpdate = async (inputs, destination) => { // get all the id and properties of already existing objects needed for update. - const updateHubspotIds = await getExistingContactsData(inputs, destination); + const hsIdsToBeUpdated = await getExistingContactsData(inputs, destination); const resultInput = inputs.map((input) => { const { message } = input; const inputParam = input; - const { destinationExternalId } = getDestinationExternalIDInfoForRetl(message, DESTINATION); + const { destinationExternalId, identifierType } = getDestinationExternalIDInfoForRetl( + message, + DESTINATION, + ); - const filteredInfo = updateHubspotIds.filter( + const filteredInfo = hsIdsToBeUpdated.filter( (update) => - update.property.toString().toLowerCase() === destinationExternalId.toString().toLowerCase(), + update.property.toString().toLowerCase() === destinationExternalId.toString().toLowerCase(), // second condition is for secondary property for identifier type ); if (filteredInfo.length > 0) { @@ -701,6 +750,26 @@ const splitEventsForCreateUpdate = async (inputs, destination) => { inputParam.message.context.hubspotOperation = 'updateObject'; return inputParam; } + const secondaryProp = primaryToSecondaryFields[identifierType]; + if (secondaryProp) { + // second condition is for secondary property for identifier type + const filteredInfoForSecondaryProp = hsIdsToBeUpdated.filter((update) => + update[secondaryProp] + ?.toString() + .toLowerCase() + .includes(destinationExternalId.toString().toLowerCase()), + ); + if (filteredInfoForSecondaryProp.length > 0) { + inputParam.message.context.externalId = setHsSearchId( + input, + filteredInfoForSecondaryProp[0].id, + true, + ); + inputParam.message.context.hubspotOperation = 'updateObject'; + return inputParam; + } + } + // if not found in the existing contacts, then it's a new contact inputParam.message.context.hubspotOperation = 'createObject'; return inputParam; }); @@ -748,8 +817,22 @@ const populateTraits = async (propertyMap, traits, destination) => { return populatedTraits; }; +const addExternalIdToHSTraits = (message) => { + const externalIdObj = message.context?.externalId?.[0]; + if (externalIdObj.useSecondaryObject) { + /* this condition help us to NOT override the primary key value with the secondary key value + example: + for `email` as primary key and `hs_additonal_emails` as secondary key we don't want to override `email` with `hs_additional_emails`. + neither we want to map anything for `hs_additional_emails` as this property can not be set + */ + return; + } + set(getFieldValueFromMessage(message, 'traits'), externalIdObj.identifierType, externalIdObj.id); +}; + module.exports = { validateDestinationConfig, + addExternalIdToHSTraits, formatKey, fetchFinalSetOfTraits, getProperties, diff --git a/src/v0/destinations/hs/util.test.js b/src/v0/destinations/hs/util.test.js index 30e89d3aee..ea2e10dc3d 100644 --- a/src/v0/destinations/hs/util.test.js +++ b/src/v0/destinations/hs/util.test.js @@ -4,6 +4,7 @@ const { validatePayloadDataTypes, getObjectAndIdentifierType, } = require('./util'); +const { primaryToSecondaryFields } = require('./config'); const propertyMap = { firstName: 'string', @@ -205,7 +206,7 @@ describe('extractUniqueValues utility test cases', () => { describe('getRequestDataAndRequestOptions utility test cases', () => { it('Should return an object with requestData and requestOptions', () => { const identifierType = 'email'; - const chunk = 'test1@gmail.com'; + const chunk = ['test1@gmail.com']; const accessToken = 'dummyAccessToken'; const expectedRequestData = { @@ -219,8 +220,17 @@ describe('getRequestDataAndRequestOptions utility test cases', () => { }, ], }, + { + filters: [ + { + propertyName: primaryToSecondaryFields[identifierType], + values: chunk, + operator: 'IN', + }, + ], + }, ], - properties: [identifierType], + properties: [identifierType, primaryToSecondaryFields[identifierType]], limit: 100, after: 0, }; diff --git a/test/integrations/destinations/hs/network.ts b/test/integrations/destinations/hs/network.ts index e29cc27562..3d3b8fd83f 100644 --- a/test/integrations/destinations/hs/network.ts +++ b/test/integrations/destinations/hs/network.ts @@ -460,6 +460,37 @@ export const networkCallsData = [ status: 200, }, }, + { + httpReq: { + url: 'https://api.hubapi.com/crm/v3/objects/contacts/search', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer dummy-access-token-hs-additonal-email', + }, + }, + httpRes: { + data: { + total: 1, + results: [ + { + id: '103689', + properties: { + createdate: '2022-07-15T15:25:08.975Z', + email: 'primary@email.com', + hs_object_id: '103604', + hs_additional_emails: 'abc@extraemail.com;secondary@email.com', + lastmodifieddate: '2022-07-15T15:26:49.590Z', + }, + createdAt: '2022-07-15T15:25:08.975Z', + updatedAt: '2022-07-15T15:26:49.590Z', + archived: false, + }, + ], + }, + status: 200, + }, + }, { httpReq: { url: 'https://api.hubapi.com/crm/v3/objects/contacts/search', diff --git a/test/integrations/destinations/hs/router/data.ts b/test/integrations/destinations/hs/router/data.ts index 3a30232f9f..e1c3e04356 100644 --- a/test/integrations/destinations/hs/router/data.ts +++ b/test/integrations/destinations/hs/router/data.ts @@ -1688,4 +1688,145 @@ export const data = [ }, }, }, + { + name: 'hs', + description: 'getting duplicate records for secondary property', + feature: 'router', + module: 'destination', + version: 'v0', + scenario: 'buisness', + successCriteria: + 'should return 200 status code with contact needs to be updated and no email property', + input: { + request: { + body: { + input: [ + { + message: { + type: 'identify', + sentAt: '2024-03-19T18:46:36.348Z', + traits: { + lastname: 'Peñarete', + firstname: 'Karen', + }, + userId: 'secondary@email.com', + channel: 'sources', + context: { + externalId: [ + { + id: 'secondary@email.com', + type: 'HS-contacts', + identifierType: 'email', + }, + ], + mappedToDestination: 'true', + }, + originalTimestamp: '2024-03-19T18:46:36.348Z', + }, + metadata: { jobId: 3, userId: 'u1' }, + destination: { + Config: { + authorizationType: 'newPrivateAppApi', + accessToken: 'dummy-access-token-hs-additonal-email', + hubID: 'dummy-hubId', + apiKey: 'dummy-apikey', + apiVersion: 'newApi', + lookupField: 'email', + hubspotEvents: [], + }, + secretConfig: {}, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + name: 'Hubspot', + enabled: true, + workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', + deleted: false, + createdAt: '2020-12-30T08:39:32.005Z', + updatedAt: '2021-02-03T16:22:31.374Z', + destinationDefinition: { + id: '1aIXqM806xAVm92nx07YwKbRrO9', + name: 'HS', + displayName: 'Hubspot', + createdAt: '2020-04-09T09:24:31.794Z', + updatedAt: '2021-01-11T11:03:28.103Z', + }, + transformations: [], + isConnectionEnabled: true, + isProcessorEnabled: true, + }, + }, + ], + destType: 'hs', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.hubapi.com/crm/v3/objects/contacts/batch/update', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer dummy-access-token-hs-additonal-email', + }, + params: {}, + body: { + JSON: { + inputs: [ + { + properties: { lastname: 'Peñarete', firstname: 'Karen' }, + id: '103689', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [{ jobId: 3, userId: 'u1' }], + batched: true, + statusCode: 200, + destination: { + Config: { + authorizationType: 'newPrivateAppApi', + accessToken: 'dummy-access-token-hs-additonal-email', + hubID: 'dummy-hubId', + apiKey: 'dummy-apikey', + apiVersion: 'newApi', + lookupField: 'email', + hubspotEvents: [], + }, + secretConfig: {}, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + name: 'Hubspot', + enabled: true, + workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', + deleted: false, + createdAt: '2020-12-30T08:39:32.005Z', + updatedAt: '2021-02-03T16:22:31.374Z', + destinationDefinition: { + id: '1aIXqM806xAVm92nx07YwKbRrO9', + name: 'HS', + displayName: 'Hubspot', + createdAt: '2020-04-09T09:24:31.794Z', + updatedAt: '2021-01-11T11:03:28.103Z', + }, + transformations: [], + isConnectionEnabled: true, + isProcessorEnabled: true, + }, + }, + ], + }, + }, + }, + }, ]; From a0d4ca82f67ed3a8f73c539c96f678e9af748ad2 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 15 Apr 2024 09:03:08 +0000 Subject: [PATCH 102/240] chore(release): 1.62.0 --- CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1670fa232d..27b65204f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,43 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.62.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.60.0...v1.62.0) (2024-04-15) + + +### Features + +* adding product level tracking for awin ([#3246](https://github.com/rudderlabs/rudder-transformer/issues/3246)) ([48cf9f2](https://github.com/rudderlabs/rudder-transformer/commit/48cf9f282b803382dd2961b4b834d08a06eaab63)) +* consent field support for ga4 ([#3213](https://github.com/rudderlabs/rudder-transformer/issues/3213)) ([92515a5](https://github.com/rudderlabs/rudder-transformer/commit/92515a5fd8a2798c48010078f62b360ec6a49979)) +* consent field support for gaoc for API v15 and upgrade the api version from v14 to v16 ([#3121](https://github.com/rudderlabs/rudder-transformer/issues/3121)) ([2aac2a6](https://github.com/rudderlabs/rudder-transformer/commit/2aac2a62547b7a7c617735fc3d6e88e0a1bed76e)), closes [#3190](https://github.com/rudderlabs/rudder-transformer/issues/3190) +* do away myaxios ([#3222](https://github.com/rudderlabs/rudder-transformer/issues/3222)) ([9214594](https://github.com/rudderlabs/rudder-transformer/commit/9214594bab2c86a4ae6f75e12531f778490cf127)) +* for reddit adding currency and value for addToCart, viewConent event as well ([#3239](https://github.com/rudderlabs/rudder-transformer/issues/3239)) ([ad235e7](https://github.com/rudderlabs/rudder-transformer/commit/ad235e785bf6039e11231a915be098130b25ec3b)) +* logger upgrade in services, dest, source ([#3228](https://github.com/rudderlabs/rudder-transformer/issues/3228)) ([c204113](https://github.com/rudderlabs/rudder-transformer/commit/c204113eab37a782f217488d0d626a8d6df345d3)) +* onboard new destination bloomreach ([#3185](https://github.com/rudderlabs/rudder-transformer/issues/3185)) ([d9b7e1f](https://github.com/rudderlabs/rudder-transformer/commit/d9b7e1f70565d59979aee3e62f60e39edb9a23c7)) +* onboarding linkedin conversion api ([#3194](https://github.com/rudderlabs/rudder-transformer/issues/3194)) ([eb7b197](https://github.com/rudderlabs/rudder-transformer/commit/eb7b197322c617b14c2579de8cb4d4dacf8e1df3)) +* rakuten: adding a default value for tr ([#3240](https://github.com/rudderlabs/rudder-transformer/issues/3240)) ([3748f24](https://github.com/rudderlabs/rudder-transformer/commit/3748f24e21634fc74c5e5b3761551c64c8e69942)) +* snapchat conversion: add event level_complete ([7370191](https://github.com/rudderlabs/rudder-transformer/commit/73701915b8e24b0a00afc48ddef7c687ecf02055)) +* update movable ink batch size ([#3223](https://github.com/rudderlabs/rudder-transformer/issues/3223)) ([667095f](https://github.com/rudderlabs/rudder-transformer/commit/667095fa8316cd95a066f15b848ad503c6b4af80)) + + +### Bug Fixes + +* adding check for reserved key words in extract custom fields ([#3264](https://github.com/rudderlabs/rudder-transformer/issues/3264)) ([3399c47](https://github.com/rudderlabs/rudder-transformer/commit/3399c47fdce1b3d19e29306ca3c5692a2fbc30fb)) +* deployment file paths ([#3216](https://github.com/rudderlabs/rudder-transformer/issues/3216)) ([808727d](https://github.com/rudderlabs/rudder-transformer/commit/808727de17e400ed102a843ab3b30f81f8900f24)) +* email mappings ([5d654b3](https://github.com/rudderlabs/rudder-transformer/commit/5d654b326b8a2e39413f9541da541ab86b2a56fa)) +* email mappings ([#3247](https://github.com/rudderlabs/rudder-transformer/issues/3247)) ([791cbf5](https://github.com/rudderlabs/rudder-transformer/commit/791cbf55fc6940af4e3208212b82c891c6618fc3)) +* fixed userId mapping, now mapping to uid instead of id ([#3192](https://github.com/rudderlabs/rudder-transformer/issues/3192)) ([70a468b](https://github.com/rudderlabs/rudder-transformer/commit/70a468bf16ecd5ee0b6fecee4b837895d19c525f)) +* fixed userId mapping, now mapping to uid instead of id ([#3262](https://github.com/rudderlabs/rudder-transformer/issues/3262)) ([9c6b251](https://github.com/rudderlabs/rudder-transformer/commit/9c6b251a6c784cc391f27e846a008fbe2901e2c8)) +* hs bugsnag error ([#3252](https://github.com/rudderlabs/rudder-transformer/issues/3252)) ([9daf1c9](https://github.com/rudderlabs/rudder-transformer/commit/9daf1c989258bd410d5780c1b11c4f6df9654af5)) +* hubspot: search for contact using secondary prop ([#3258](https://github.com/rudderlabs/rudder-transformer/issues/3258)) ([0b57204](https://github.com/rudderlabs/rudder-transformer/commit/0b5720446693efe1fd0ccdfc141bd7f21b2c32ae)) +* impact: support custom product mapping ([#3249](https://github.com/rudderlabs/rudder-transformer/issues/3249)) ([cb8ff2f](https://github.com/rudderlabs/rudder-transformer/commit/cb8ff2fb943c49df4ac083bd179d9674b40eb602)) +* marketo bulk ignore null while checking data type mismatch ([#3263](https://github.com/rudderlabs/rudder-transformer/issues/3263)) ([6e3274b](https://github.com/rudderlabs/rudder-transformer/commit/6e3274bfba9e7838d1f81d845a070427b67e75f5)) +* merge conflict with main ([#3233](https://github.com/rudderlabs/rudder-transformer/issues/3233)) ([042dd6d](https://github.com/rudderlabs/rudder-transformer/commit/042dd6d16236dede8126984ea590fa167d4a4a87)), closes [#3216](https://github.com/rudderlabs/rudder-transformer/issues/3216) +* ninetailed: remove page support ([#3218](https://github.com/rudderlabs/rudder-transformer/issues/3218)) ([2f30c56](https://github.com/rudderlabs/rudder-transformer/commit/2f30c56af62e983d09b5d4f2da9a0ba22f5c1612)) +* shopify invalid_event metric prometheus label ([#3200](https://github.com/rudderlabs/rudder-transformer/issues/3200)) ([345c87d](https://github.com/rudderlabs/rudder-transformer/commit/345c87d7c530c621ae3fd6c504d64e5a14e31f22)) +* shopify: send 500 for identifier call in case of failure ([#3235](https://github.com/rudderlabs/rudder-transformer/issues/3235)) ([8eb4c4e](https://github.com/rudderlabs/rudder-transformer/commit/8eb4c4e9b8daebbaeb1d12ff0c17915fe19c2b50)) +* snapchat conversion: add event level_complete ([#3231](https://github.com/rudderlabs/rudder-transformer/issues/3231)) ([39368a0](https://github.com/rudderlabs/rudder-transformer/commit/39368a09e48acc324faa855186bc623e5c347881)) +* update correct staging deployment file ([#3189](https://github.com/rudderlabs/rudder-transformer/issues/3189)) ([ac7e887](https://github.com/rudderlabs/rudder-transformer/commit/ac7e8879fcd230dae09f757a759fb42e4cdc09d0)) + ### [1.61.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.61.0...v1.61.1) (2024-04-03) ## [1.61.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.60.0...v1.61.0) (2024-04-02) diff --git a/package-lock.json b/package-lock.json index fd42692109..fdfab6703c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.61.1", + "version": "1.62.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.61.1", + "version": "1.62.0", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index c839bc8acc..1343f27fbe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.61.1", + "version": "1.62.0", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From 54eabc6b1c0f49bb3388e45bc27505bbd51b66c0 Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Tue, 16 Apr 2024 12:23:31 +0530 Subject: [PATCH 103/240] fix: hubspot: hs_additional_email comparision logic (#3277) --- src/v0/destinations/hs/util.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/v0/destinations/hs/util.js b/src/v0/destinations/hs/util.js index ffb2df5237..b30207fe15 100644 --- a/src/v0/destinations/hs/util.js +++ b/src/v0/destinations/hs/util.js @@ -752,11 +752,18 @@ const splitEventsForCreateUpdate = async (inputs, destination) => { } const secondaryProp = primaryToSecondaryFields[identifierType]; if (secondaryProp) { - // second condition is for secondary property for identifier type + /* second condition is for secondary property for identifier type + For example: + update[secondaryProp] = "abc@e.com;cd@e.com;k@w.com" + destinationExternalId = "cd@e.com" + So we are splitting all the emails in update[secondaryProp] into an array using ';' + and then checking if array includes destinationExternalId + */ const filteredInfoForSecondaryProp = hsIdsToBeUpdated.filter((update) => update[secondaryProp] ?.toString() .toLowerCase() + .split(';') .includes(destinationExternalId.toString().toLowerCase()), ); if (filteredInfoForSecondaryProp.length > 0) { From 2e6f8e5d0f24b287daebbdb68f672b29ef46351b Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Tue, 16 Apr 2024 13:45:07 +0530 Subject: [PATCH 104/240] chore: cleaned changelog --- CHANGELOG.md | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27b65204f0..6ec145991f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,29 +2,21 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -## [1.62.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.60.0...v1.62.0) (2024-04-15) +## [1.62.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.61.1...v1.62.0) (2024-04-15) ### Features -* adding product level tracking for awin ([#3246](https://github.com/rudderlabs/rudder-transformer/issues/3246)) ([48cf9f2](https://github.com/rudderlabs/rudder-transformer/commit/48cf9f282b803382dd2961b4b834d08a06eaab63)) -* consent field support for ga4 ([#3213](https://github.com/rudderlabs/rudder-transformer/issues/3213)) ([92515a5](https://github.com/rudderlabs/rudder-transformer/commit/92515a5fd8a2798c48010078f62b360ec6a49979)) -* consent field support for gaoc for API v15 and upgrade the api version from v14 to v16 ([#3121](https://github.com/rudderlabs/rudder-transformer/issues/3121)) ([2aac2a6](https://github.com/rudderlabs/rudder-transformer/commit/2aac2a62547b7a7c617735fc3d6e88e0a1bed76e)), closes [#3190](https://github.com/rudderlabs/rudder-transformer/issues/3190) * do away myaxios ([#3222](https://github.com/rudderlabs/rudder-transformer/issues/3222)) ([9214594](https://github.com/rudderlabs/rudder-transformer/commit/9214594bab2c86a4ae6f75e12531f778490cf127)) * for reddit adding currency and value for addToCart, viewConent event as well ([#3239](https://github.com/rudderlabs/rudder-transformer/issues/3239)) ([ad235e7](https://github.com/rudderlabs/rudder-transformer/commit/ad235e785bf6039e11231a915be098130b25ec3b)) * logger upgrade in services, dest, source ([#3228](https://github.com/rudderlabs/rudder-transformer/issues/3228)) ([c204113](https://github.com/rudderlabs/rudder-transformer/commit/c204113eab37a782f217488d0d626a8d6df345d3)) -* onboard new destination bloomreach ([#3185](https://github.com/rudderlabs/rudder-transformer/issues/3185)) ([d9b7e1f](https://github.com/rudderlabs/rudder-transformer/commit/d9b7e1f70565d59979aee3e62f60e39edb9a23c7)) -* onboarding linkedin conversion api ([#3194](https://github.com/rudderlabs/rudder-transformer/issues/3194)) ([eb7b197](https://github.com/rudderlabs/rudder-transformer/commit/eb7b197322c617b14c2579de8cb4d4dacf8e1df3)) * rakuten: adding a default value for tr ([#3240](https://github.com/rudderlabs/rudder-transformer/issues/3240)) ([3748f24](https://github.com/rudderlabs/rudder-transformer/commit/3748f24e21634fc74c5e5b3761551c64c8e69942)) -* snapchat conversion: add event level_complete ([7370191](https://github.com/rudderlabs/rudder-transformer/commit/73701915b8e24b0a00afc48ddef7c687ecf02055)) -* update movable ink batch size ([#3223](https://github.com/rudderlabs/rudder-transformer/issues/3223)) ([667095f](https://github.com/rudderlabs/rudder-transformer/commit/667095fa8316cd95a066f15b848ad503c6b4af80)) ### Bug Fixes * adding check for reserved key words in extract custom fields ([#3264](https://github.com/rudderlabs/rudder-transformer/issues/3264)) ([3399c47](https://github.com/rudderlabs/rudder-transformer/commit/3399c47fdce1b3d19e29306ca3c5692a2fbc30fb)) * deployment file paths ([#3216](https://github.com/rudderlabs/rudder-transformer/issues/3216)) ([808727d](https://github.com/rudderlabs/rudder-transformer/commit/808727de17e400ed102a843ab3b30f81f8900f24)) -* email mappings ([5d654b3](https://github.com/rudderlabs/rudder-transformer/commit/5d654b326b8a2e39413f9541da541ab86b2a56fa)) * email mappings ([#3247](https://github.com/rudderlabs/rudder-transformer/issues/3247)) ([791cbf5](https://github.com/rudderlabs/rudder-transformer/commit/791cbf55fc6940af4e3208212b82c891c6618fc3)) * fixed userId mapping, now mapping to uid instead of id ([#3192](https://github.com/rudderlabs/rudder-transformer/issues/3192)) ([70a468b](https://github.com/rudderlabs/rudder-transformer/commit/70a468bf16ecd5ee0b6fecee4b837895d19c525f)) * fixed userId mapping, now mapping to uid instead of id ([#3262](https://github.com/rudderlabs/rudder-transformer/issues/3262)) ([9c6b251](https://github.com/rudderlabs/rudder-transformer/commit/9c6b251a6c784cc391f27e846a008fbe2901e2c8)) @@ -32,12 +24,7 @@ All notable changes to this project will be documented in this file. See [standa * hubspot: search for contact using secondary prop ([#3258](https://github.com/rudderlabs/rudder-transformer/issues/3258)) ([0b57204](https://github.com/rudderlabs/rudder-transformer/commit/0b5720446693efe1fd0ccdfc141bd7f21b2c32ae)) * impact: support custom product mapping ([#3249](https://github.com/rudderlabs/rudder-transformer/issues/3249)) ([cb8ff2f](https://github.com/rudderlabs/rudder-transformer/commit/cb8ff2fb943c49df4ac083bd179d9674b40eb602)) * marketo bulk ignore null while checking data type mismatch ([#3263](https://github.com/rudderlabs/rudder-transformer/issues/3263)) ([6e3274b](https://github.com/rudderlabs/rudder-transformer/commit/6e3274bfba9e7838d1f81d845a070427b67e75f5)) -* merge conflict with main ([#3233](https://github.com/rudderlabs/rudder-transformer/issues/3233)) ([042dd6d](https://github.com/rudderlabs/rudder-transformer/commit/042dd6d16236dede8126984ea590fa167d4a4a87)), closes [#3216](https://github.com/rudderlabs/rudder-transformer/issues/3216) -* ninetailed: remove page support ([#3218](https://github.com/rudderlabs/rudder-transformer/issues/3218)) ([2f30c56](https://github.com/rudderlabs/rudder-transformer/commit/2f30c56af62e983d09b5d4f2da9a0ba22f5c1612)) -* shopify invalid_event metric prometheus label ([#3200](https://github.com/rudderlabs/rudder-transformer/issues/3200)) ([345c87d](https://github.com/rudderlabs/rudder-transformer/commit/345c87d7c530c621ae3fd6c504d64e5a14e31f22)) * shopify: send 500 for identifier call in case of failure ([#3235](https://github.com/rudderlabs/rudder-transformer/issues/3235)) ([8eb4c4e](https://github.com/rudderlabs/rudder-transformer/commit/8eb4c4e9b8daebbaeb1d12ff0c17915fe19c2b50)) -* snapchat conversion: add event level_complete ([#3231](https://github.com/rudderlabs/rudder-transformer/issues/3231)) ([39368a0](https://github.com/rudderlabs/rudder-transformer/commit/39368a09e48acc324faa855186bc623e5c347881)) -* update correct staging deployment file ([#3189](https://github.com/rudderlabs/rudder-transformer/issues/3189)) ([ac7e887](https://github.com/rudderlabs/rudder-transformer/commit/ac7e8879fcd230dae09f757a759fb42e4cdc09d0)) ### [1.61.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.61.0...v1.61.1) (2024-04-03) From 34e861d4821fed6699b90ee6be663b02504b98fd Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Tue, 16 Apr 2024 14:14:48 +0530 Subject: [PATCH 105/240] chore: change log info to debug --- src/controllers/delivery.ts | 2 +- src/controllers/destination.ts | 2 +- src/v0/destinations/twitter_ads/transform.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/delivery.ts b/src/controllers/delivery.ts index 8ac7c9902a..0dc27553cb 100644 --- a/src/controllers/delivery.ts +++ b/src/controllers/delivery.ts @@ -24,7 +24,7 @@ const NON_DETERMINABLE = 'Non-determinable'; export class DeliveryController { public static async deliverToDestination(ctx: Context) { - logger.info('Native(Delivery):: Request to transformer::', ctx.request.body); + logger.debug('Native(Delivery):: Request to transformer::', ctx.request.body); let deliveryResponse: DeliveryV0Response; const requestMetadata = MiscService.getRequestMetadata(ctx); const deliveryRequest = ctx.request.body as ProxyV0Request; diff --git a/src/controllers/destination.ts b/src/controllers/destination.ts index 35606ea62e..92ef4b4c19 100644 --- a/src/controllers/destination.ts +++ b/src/controllers/destination.ts @@ -166,7 +166,7 @@ export class DestinationController { } public static batchProcess(ctx: Context) { - logger.info('Native(Process-Transform-Batch):: Requst to transformer::', ctx.request.body); + logger.debug('Native(Process-Transform-Batch):: Requst to transformer::', ctx.request.body); const startTime = new Date(); const requestMetadata = MiscService.getRequestMetadata(ctx); const routerRequest = ctx.request.body as RouterTransformationRequest; diff --git a/src/v0/destinations/twitter_ads/transform.js b/src/v0/destinations/twitter_ads/transform.js index 365663925e..328d8c4a9f 100644 --- a/src/v0/destinations/twitter_ads/transform.js +++ b/src/v0/destinations/twitter_ads/transform.js @@ -157,7 +157,7 @@ function validateRequest(message) { } function process(event, requestMetadata, logger) { - logger.info(`[TWITTER ADS]: Transforming request received with info`); + logger.debug(`[TWITTER ADS]: Transforming request received with info`); const { message, metadata, destination } = event; validateRequest(message); From f0dff674d8661eb348bb39b46376fe232ac86789 Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Tue, 16 Apr 2024 14:15:48 +0530 Subject: [PATCH 106/240] chore: minor CHANGELOG.md cleanup --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ec145991f..9e221a32f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,6 @@ All notable changes to this project will be documented in this file. See [standa * adding check for reserved key words in extract custom fields ([#3264](https://github.com/rudderlabs/rudder-transformer/issues/3264)) ([3399c47](https://github.com/rudderlabs/rudder-transformer/commit/3399c47fdce1b3d19e29306ca3c5692a2fbc30fb)) * deployment file paths ([#3216](https://github.com/rudderlabs/rudder-transformer/issues/3216)) ([808727d](https://github.com/rudderlabs/rudder-transformer/commit/808727de17e400ed102a843ab3b30f81f8900f24)) * email mappings ([#3247](https://github.com/rudderlabs/rudder-transformer/issues/3247)) ([791cbf5](https://github.com/rudderlabs/rudder-transformer/commit/791cbf55fc6940af4e3208212b82c891c6618fc3)) -* fixed userId mapping, now mapping to uid instead of id ([#3192](https://github.com/rudderlabs/rudder-transformer/issues/3192)) ([70a468b](https://github.com/rudderlabs/rudder-transformer/commit/70a468bf16ecd5ee0b6fecee4b837895d19c525f)) * fixed userId mapping, now mapping to uid instead of id ([#3262](https://github.com/rudderlabs/rudder-transformer/issues/3262)) ([9c6b251](https://github.com/rudderlabs/rudder-transformer/commit/9c6b251a6c784cc391f27e846a008fbe2901e2c8)) * hs bugsnag error ([#3252](https://github.com/rudderlabs/rudder-transformer/issues/3252)) ([9daf1c9](https://github.com/rudderlabs/rudder-transformer/commit/9daf1c989258bd410d5780c1b11c4f6df9654af5)) * hubspot: search for contact using secondary prop ([#3258](https://github.com/rudderlabs/rudder-transformer/issues/3258)) ([0b57204](https://github.com/rudderlabs/rudder-transformer/commit/0b5720446693efe1fd0ccdfc141bd7f21b2c32ae)) From ec3eda85866bbba7813a8007408dac0c57c27c65 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Wed, 17 Apr 2024 04:07:51 +0530 Subject: [PATCH 107/240] fix: send group_id as string in monday destination (#3278) --- src/v0/destinations/monday/util.js | 17 +++++++++-------- .../destinations/monday/processor/data.ts | 10 +++++----- .../destinations/monday/router/data.ts | 4 ++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/v0/destinations/monday/util.js b/src/v0/destinations/monday/util.js index 872fad42a7..0694028eb2 100644 --- a/src/v0/destinations/monday/util.js +++ b/src/v0/destinations/monday/util.js @@ -27,7 +27,7 @@ const getGroupId = (groupTitle, board) => { } }); if (groupId) { - return groupId; + return JSON.stringify(groupId); } throw new ConfigurationError(`Group ${groupTitle} doesn't exist in the board`); }; @@ -239,19 +239,20 @@ const populatePayload = (message, Config, boardDeatailsResponse) => { columnToPropertyMapping, boardDeatailsResponse.response?.data, ); + const items = [ + `board_id: ${boardId}`, + `item_name: ${JSON.stringify(message.properties?.name)}`, + `column_values: ${JSON.stringify(columnValues)}`, + ]; if (groupTitle) { if (!message.properties?.name) { throw new InstrumentationError('Item name is required to create an item'); } const groupId = getGroupId(groupTitle, boardDeatailsResponse.response?.data); - payload.query = `mutation { create_item (board_id: ${boardId}, group_id: ${groupId} item_name: ${JSON.stringify( - message.properties?.name, - )}, column_values: ${JSON.stringify(columnValues)}) {id}}`; - } else { - payload.query = `mutation { create_item (board_id: ${boardId}, item_name: ${JSON.stringify( - message.properties?.name, - )}, column_values: ${JSON.stringify(columnValues)}) {id}}`; + items.push(`group_id: ${groupId}`); } + const itemsQuery = items.join(', '); + payload.query = `mutation { create_item (${itemsQuery}) {id}}`; return payload; }; diff --git a/test/integrations/destinations/monday/processor/data.ts b/test/integrations/destinations/monday/processor/data.ts index 4e5280efcb..082ff822fd 100644 --- a/test/integrations/destinations/monday/processor/data.ts +++ b/test/integrations/destinations/monday/processor/data.ts @@ -74,7 +74,7 @@ export const data = [ FORM: {}, JSON: { query: - 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{}") {id}}', + 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{}") {id}}', }, JSON_ARRAY: {}, XML: {}, @@ -172,7 +172,7 @@ export const data = [ FORM: {}, JSON: { query: - 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{}") {id}}', + 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{}") {id}}', }, JSON_ARRAY: {}, XML: {}, @@ -716,7 +716,7 @@ export const data = [ body: { JSON: { query: - 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"}}") {id}}', + 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"}}") {id}}', }, JSON_ARRAY: {}, XML: {}, @@ -827,7 +827,7 @@ export const data = [ body: { JSON: { query: - 'mutation { create_item (board_id: 339283933, group_id: group_title item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"}}") {id}}', + 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"}}", group_id: "group_title") {id}}', }, JSON_ARRAY: {}, XML: {}, @@ -1188,7 +1188,7 @@ export const data = [ body: { JSON: { query: - 'mutation { create_item (board_id: 339283933, group_id: group_title item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"},\\"checkbox\\":{\\"checked\\":true},\\"numbers\\":\\"45\\",\\"text\\":\\"texting\\",\\"country\\":{\\"countryName\\":\\"Unites States\\",\\"countryCode\\":\\"US\\"},\\"location\\":{\\"address\\":\\"New York\\",\\"lat\\":\\"51.23\\",\\"lng\\":\\"35.3\\"},\\"phone\\":{\\"phone\\":\\"2626277272\\",\\"countryShortName\\":\\"US\\"},\\"rating\\":3,\\"link\\":{\\"url\\":\\"demo.com\\",\\"text\\":\\"websiteLink\\"},\\"long_text\\":{\\"text\\":\\"property description\\"},\\"world_clock\\":{\\"timezone\\":\\"America/New_York\\"}}") {id}}', + 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"},\\"checkbox\\":{\\"checked\\":true},\\"numbers\\":\\"45\\",\\"text\\":\\"texting\\",\\"country\\":{\\"countryName\\":\\"Unites States\\",\\"countryCode\\":\\"US\\"},\\"location\\":{\\"address\\":\\"New York\\",\\"lat\\":\\"51.23\\",\\"lng\\":\\"35.3\\"},\\"phone\\":{\\"phone\\":\\"2626277272\\",\\"countryShortName\\":\\"US\\"},\\"rating\\":3,\\"link\\":{\\"url\\":\\"demo.com\\",\\"text\\":\\"websiteLink\\"},\\"long_text\\":{\\"text\\":\\"property description\\"},\\"world_clock\\":{\\"timezone\\":\\"America/New_York\\"}}", group_id: "group_title") {id}}', }, JSON_ARRAY: {}, XML: {}, diff --git a/test/integrations/destinations/monday/router/data.ts b/test/integrations/destinations/monday/router/data.ts index 3be8b129c5..abd649d805 100644 --- a/test/integrations/destinations/monday/router/data.ts +++ b/test/integrations/destinations/monday/router/data.ts @@ -113,7 +113,7 @@ export const data = [ FORM: {}, JSON: { query: - 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{}") {id}}', + 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{}") {id}}', }, JSON_ARRAY: {}, XML: {}, @@ -159,7 +159,7 @@ export const data = [ body: { JSON: { query: - 'mutation { create_item (board_id: 339283933, group_id: group_title item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"}}") {id}}', + 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"}}", group_id: "group_title") {id}}', }, JSON_ARRAY: {}, XML: {}, From 44b29caf2537b7dc0bd178aa346ca92d143e79d0 Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Wed, 17 Apr 2024 11:16:30 +0530 Subject: [PATCH 108/240] fix: awin product_id mapping backward compatible (#3285) --- src/v0/destinations/awin/utils.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/v0/destinations/awin/utils.js b/src/v0/destinations/awin/utils.js index be5f22474d..f0daea9b99 100644 --- a/src/v0/destinations/awin/utils.js +++ b/src/v0/destinations/awin/utils.js @@ -54,7 +54,11 @@ const trackProduct = (properties, advertiserId, commissionParts) => { productsArray.forEach((product) => { const productPayloadNew = { advertiserId, - orderReference: properties.order_id || properties.orderId, + orderReference: + properties.order_id || + properties.orderId || + properties.orderReference || + properties.order_reference, productId: product.product_id || product.productId, productName: product.name, productItemPrice: product.price, From 8592e664eb568e70a00261e275ab2faed8f6f618 Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Wed, 17 Apr 2024 13:39:24 +0530 Subject: [PATCH 109/240] feat: adding custom properties support to bluecore (#3282) * feat: adding custom properties support to bluecore * Update src/cdk/v2/destinations/bluecore/utils.js Co-authored-by: Sankeerth * fix: small fix --------- Co-authored-by: Sankeerth --- src/cdk/v2/destinations/bluecore/config.js | 22 ++++++++- .../destinations/bluecore/procWorkflow.yaml | 6 +-- src/cdk/v2/destinations/bluecore/utils.js | 45 ++++++++++++++++++- .../destinations/bluecore/ecommTestData.ts | 24 ++++++++-- .../destinations/bluecore/identifyTestData.ts | 13 ++++-- .../destinations/bluecore/trackTestData.ts | 39 +++++++++++++--- 6 files changed, 129 insertions(+), 20 deletions(-) diff --git a/src/cdk/v2/destinations/bluecore/config.js b/src/cdk/v2/destinations/bluecore/config.js index 9b9cde9c66..98e1bb4b23 100644 --- a/src/cdk/v2/destinations/bluecore/config.js +++ b/src/cdk/v2/destinations/bluecore/config.js @@ -1,6 +1,6 @@ const { getMappingConfig } = require('../../../../v0/util'); -const BASE_URL = 'https://api.bluecore.com/api/track/mobile/v1'; +const BASE_URL = 'https://api.bluecore.app/api/track/mobile/v1'; const CONFIG_CATEGORIES = { IDENTIFY: { @@ -46,6 +46,24 @@ const EVENT_NAME_MAPPING = [ const BLUECORE_EXCLUSION_FIELDS = ['query', 'order_id', 'total']; +const IDENTIFY_EXCLUSION_LIST = [ + 'name', + 'firstName', + 'first_name', + 'firstname', + 'lastName', + 'last_name', + 'lastname', + 'email', + 'age', + 'sex', + 'address', + 'action', + 'event', +]; + +const TRACK_EXCLUSION_LIST = [...IDENTIFY_EXCLUSION_LIST, 'query', 'order_id', 'total', 'products']; + const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); module.exports = { CONFIG_CATEGORIES, @@ -53,4 +71,6 @@ module.exports = { EVENT_NAME_MAPPING, BASE_URL, BLUECORE_EXCLUSION_FIELDS, + IDENTIFY_EXCLUSION_LIST, + TRACK_EXCLUSION_LIST, }; diff --git a/src/cdk/v2/destinations/bluecore/procWorkflow.yaml b/src/cdk/v2/destinations/bluecore/procWorkflow.yaml index 480bced699..9828ac593c 100644 --- a/src/cdk/v2/destinations/bluecore/procWorkflow.yaml +++ b/src/cdk/v2/destinations/bluecore/procWorkflow.yaml @@ -26,7 +26,7 @@ steps: condition: $.outputs.messageType === {{$.EventType.IDENTIFY}} template: | const payload = $.constructProperties(.message); - payload.token = .destination.Config.bluecoreNamespace; + payload.properties.token = .destination.Config.bluecoreNamespace; $.verifyPayload(payload, .message); payload.event = payload.event ?? 'customer_patch'; payload.properties.distinct_id = $.populateAccurateDistinctId(payload, .message); @@ -50,7 +50,7 @@ steps: const temporaryProductArray = newPayload.properties.products ?? $.createProductForStandardEcommEvent(^.message, eventName); newPayload.properties.products = $.normalizeProductArray(temporaryProductArray); newPayload.event = eventName; - newPayload.token = ^.destination.Config.bluecoreNamespace; + newPayload.properties.token = ^.destination.Config.bluecoreNamespace; $.verifyPayload(newPayload, ^.message); $.removeUndefinedNullValuesAndEmptyObjectArray(newPayload) )[]; @@ -61,7 +61,7 @@ steps: const response = $.defaultRequestConfig(); response.body.JSON = .; response.method = "POST"; - response.endpoint = "https://api.bluecore.com/api/track/mobile/v1"; + response.endpoint = "https://api.bluecore.app/api/track/mobile/v1"; response.headers = { "Content-Type": "application/json" }; diff --git a/src/cdk/v2/destinations/bluecore/utils.js b/src/cdk/v2/destinations/bluecore/utils.js index 22ec254fe2..91eda60d0d 100644 --- a/src/cdk/v2/destinations/bluecore/utils.js +++ b/src/cdk/v2/destinations/bluecore/utils.js @@ -12,9 +12,10 @@ const { validateEventName, constructPayload, getDestinationExternalID, + extractCustomFields, } = require('../../../../v0/util'); const { CommonUtils } = require('../../../../util/common'); -const { EVENT_NAME_MAPPING } = require('./config'); +const { EVENT_NAME_MAPPING, IDENTIFY_EXCLUSION_LIST, TRACK_EXCLUSION_LIST } = require('./config'); const { EventType } = require('../../../../constants'); const { MAPPING_CONFIG, CONFIG_CATEGORIES } = require('./config'); @@ -167,6 +168,41 @@ const normalizeProductArray = (products) => { return finalProductArray; }; +const mapCustomProperties = (message) => { + let customerProperties; + const customProperties = { properties: {} }; + const messageType = message.type.toUpperCase(); + switch (messageType) { + case 'IDENTIFY': + customerProperties = extractCustomFields( + message, + {}, + ['traits', 'context.traits'], + IDENTIFY_EXCLUSION_LIST, + ); + customProperties.properties.customer = customerProperties; + break; + case 'TRACK': + customerProperties = extractCustomFields( + message, + {}, + ['traits', 'context.traits'], + IDENTIFY_EXCLUSION_LIST, + ); + customProperties.properties = extractCustomFields( + message, + {}, + ['properties'], + TRACK_EXCLUSION_LIST, + ); + customProperties.properties.customer = customerProperties; + break; + default: + break; + } + return customProperties; +}; + /** * Constructs properties based on the given message. * @@ -178,7 +214,12 @@ const constructProperties = (message) => { const commonPayload = constructPayload(message, MAPPING_CONFIG[commonCategory.name]); const category = CONFIG_CATEGORIES[message.type.toUpperCase()]; const typeSpecificPayload = constructPayload(message, MAPPING_CONFIG[category.name]); - const finalPayload = lodash.merge(commonPayload, typeSpecificPayload); + const typeSpecificCustomProperties = mapCustomProperties(message); + const finalPayload = lodash.merge( + commonPayload, + typeSpecificPayload, + typeSpecificCustomProperties, + ); return finalPayload; }; diff --git a/test/integrations/destinations/bluecore/ecommTestData.ts b/test/integrations/destinations/bluecore/ecommTestData.ts index de7584df78..19b63e7bda 100644 --- a/test/integrations/destinations/bluecore/ecommTestData.ts +++ b/test/integrations/destinations/bluecore/ecommTestData.ts @@ -73,7 +73,7 @@ const commonOutputHeaders = { 'Content-Type': 'application/json', }; -const eventEndPoint = 'https://api.bluecore.com/api/track/mobile/v1'; +const eventEndPoint = 'https://api.bluecore.app/api/track/mobile/v1'; export const ecomTestData = [ { @@ -296,7 +296,11 @@ export const ecomTestData = [ customer: { age: '22', email: 'test@rudderstack.com', + anonymousId: '9c6bd77ea9da3e68', + id: 'user@1', + phone: '9112340375', }, + product_id: '123', products: [ { id: '123', @@ -304,9 +308,11 @@ export const ecomTestData = [ property2: 'value2', }, ], + property1: 'value1', + property2: 'value2', + token: 'dummy_sandbox', }, event: 'viewed_product', - token: 'dummy_sandbox', }, userId: '', }), @@ -379,8 +385,11 @@ export const ecomTestData = [ JSON: { properties: { distinct_id: 'user@1', + product_id: '123', customer: { age: '22', + anonymousId: '9c6bd77ea9da3e68', + id: 'user@1', }, products: [ { @@ -389,9 +398,11 @@ export const ecomTestData = [ property2: 'value2', }, ], + property1: 'value1', + property2: 'value2', + token: 'dummy_sandbox', }, event: 'wishlist', - token: 'dummy_sandbox', }, userId: '', }), @@ -406,8 +417,11 @@ export const ecomTestData = [ JSON: { properties: { distinct_id: 'user@1', + product_id: '123', customer: { age: '22', + anonymousId: '9c6bd77ea9da3e68', + id: 'user@1', }, products: [ { @@ -416,9 +430,11 @@ export const ecomTestData = [ property2: 'value2', }, ], + token: 'dummy_sandbox', + property1: 'value1', + property2: 'value2', }, event: 'add_to_cart', - token: 'dummy_sandbox', }, userId: '', }), diff --git a/test/integrations/destinations/bluecore/identifyTestData.ts b/test/integrations/destinations/bluecore/identifyTestData.ts index 660e335bc6..fee27ccf0f 100644 --- a/test/integrations/destinations/bluecore/identifyTestData.ts +++ b/test/integrations/destinations/bluecore/identifyTestData.ts @@ -55,6 +55,10 @@ const commonOutputCustomerProperties = { first_name: 'Test', last_name: 'Rudderlabs', sex: 'non-binary', + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + db: '19950715', + gender: 'non-binary', + phone: '+1234589947', address: { city: 'Kolkata', state: 'WB', @@ -71,7 +75,7 @@ const anonymousId = '97c46c81-3140-456d-b2a9-690d70aaca35'; const userId = 'user@1'; const sentAt = '2021-01-03T17:02:53.195Z'; const originalTimestamp = '2021-01-03T17:02:53.193Z'; -const commonEndpoint = 'https://api.bluecore.com/api/track/mobile/v1'; +const commonEndpoint = 'https://api.bluecore.app/api/track/mobile/v1'; export const identifyData = [ { @@ -118,8 +122,8 @@ export const identifyData = [ properties: { distinct_id: 'abc@gmail.com', customer: { ...commonOutputCustomerProperties, email: 'abc@gmail.com' }, + token: 'dummy_sandbox', }, - token: 'dummy_sandbox', event: 'customer_patch', }, }), @@ -302,8 +306,9 @@ export const identifyData = [ properties: { distinct_id: 'user@1', customer: { ...commonOutputCustomerProperties, email: 'abc@gmail.com' }, + token: 'dummy_sandbox', }, - token: 'dummy_sandbox', + event: 'identify', }, }), @@ -361,8 +366,8 @@ export const identifyData = [ properties: { distinct_id: '54321', customer: { ...commonOutputCustomerProperties, email: 'abc@gmail.com' }, + token: 'dummy_sandbox', }, - token: 'dummy_sandbox', event: 'customer_patch', }, }), diff --git a/test/integrations/destinations/bluecore/trackTestData.ts b/test/integrations/destinations/bluecore/trackTestData.ts index 72d48bf93d..7474127558 100644 --- a/test/integrations/destinations/bluecore/trackTestData.ts +++ b/test/integrations/destinations/bluecore/trackTestData.ts @@ -86,7 +86,7 @@ const commonOutputHeaders = { 'Content-Type': 'application/json', }; -const eventEndPoint = 'https://api.bluecore.com/api/track/mobile/v1'; +const eventEndPoint = 'https://api.bluecore.app/api/track/mobile/v1'; export const trackTestData = [ { @@ -140,6 +140,9 @@ export const trackTestData = [ customer: { age: '22', email: 'test@rudderstack.com', + anonymousId: '9c6bd77ea9da3e68', + id: 'user@1', + phone: '9112340375', }, products: [ { @@ -155,9 +158,11 @@ export const trackTestData = [ quantity: 3, }, ], + property1: 'value1', + property2: 'value2', + token: 'dummy_sandbox', }, event: 'TestEven001', - token: 'dummy_sandbox', }, userId: '', }), @@ -216,13 +221,19 @@ export const trackTestData = [ JSON: { properties: { distinct_id: 'test@rudderstack.com', + product_id: '123', + property1: 'value1', + property2: 'value2', + token: 'dummy_sandbox', customer: { age: '22', email: 'test@rudderstack.com', + anonymousId: '9c6bd77ea9da3e68', + id: 'user@1', + phone: '9112340375', }, }, event: 'TestEven001', - token: 'dummy_sandbox', }, userId: '', }), @@ -283,11 +294,17 @@ export const trackTestData = [ distinct_id: 'test@rudderstack.com', customer: { age: '22', + anonymousId: '9c6bd77ea9da3e68', email: 'test@rudderstack.com', + id: 'user@1', + phone: '9112340375', }, + product_id: '123', + property1: 'value1', + property2: 'value2', + token: 'dummy_sandbox', }, event: 'optin', - token: 'dummy_sandbox', }, userId: '', }), @@ -346,13 +363,19 @@ export const trackTestData = [ JSON: { properties: { distinct_id: 'test@rudderstack.com', + product_id: '123', + property1: 'value1', + property2: 'value2', + token: 'dummy_sandbox', customer: { age: '22', + anonymousId: '9c6bd77ea9da3e68', + id: 'user@1', email: 'test@rudderstack.com', + phone: '9112340375', }, }, event: 'unsubscribe', - token: 'dummy_sandbox', }, userId: '', }), @@ -405,9 +428,12 @@ export const trackTestData = [ JSON: { properties: { distinct_id: '54321', + token: 'dummy_sandbox', customer: { age: '22', email: 'abc@gmail.com', + anonymousId: '9c6bd77ea9da3e68', + id: 'user@1', }, products: [ { @@ -423,9 +449,10 @@ export const trackTestData = [ quantity: 3, }, ], + property1: 'value1', + property2: 'value2', }, event: 'TestEven001', - token: 'dummy_sandbox', }, userId: '', }), From 86eaa07cf17de190e6b39c7087c1190faaaf927a Mon Sep 17 00:00:00 2001 From: devops-github-rudderstack <88187154+devops-github-rudderstack@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:26:38 +0530 Subject: [PATCH 110/240] chore(release): pull hotfix-release/v1.62.1 into main (#3292) * fix: revert mixpanel deprecate /track (#3291) * chore(release): 1.62.1 --------- Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Co-authored-by: GitHub Actions --- CHANGELOG.md | 7 + package-lock.json | 4 +- package.json | 2 +- src/util/prometheus.js | 6 + src/v0/destinations/mp/config.js | 2 + src/v0/destinations/mp/transform.js | 70 ++-- src/v0/destinations/mp/util.js | 8 +- src/v0/destinations/mp/util.test.js | 14 + test/integrations/destinations/mp/common.ts | 2 +- .../destinations/mp/processor/data.ts | 363 ++++++++---------- .../destinations/mp/router/data.ts | 12 +- 11 files changed, 244 insertions(+), 246 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e221a32f8..5aeb88f374 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [1.62.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.62.0...v1.62.1) (2024-04-18) + + +### Bug Fixes + +* revert mixpanel deprecate /track ([#3291](https://github.com/rudderlabs/rudder-transformer/issues/3291)) ([ec068b4](https://github.com/rudderlabs/rudder-transformer/commit/ec068b49bd4a5652a762c60a8257c883e4709d1a)) + ## [1.62.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.61.1...v1.62.0) (2024-04-15) diff --git a/package-lock.json b/package-lock.json index fdfab6703c..b524ed27ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.62.0", + "version": "1.62.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.62.0", + "version": "1.62.1", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index 1343f27fbe..57da0144ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.62.0", + "version": "1.62.1", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { diff --git a/src/util/prometheus.js b/src/util/prometheus.js index a46eae12c9..882dff9e75 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -587,6 +587,12 @@ class Prometheus { type: 'gauge', labelNames: ['destination_id'], }, + { + name: 'mixpanel_batch_track_pack_size', + help: 'mixpanel_batch_track_pack_size', + type: 'gauge', + labelNames: ['destination_id'], + }, { name: 'mixpanel_batch_import_pack_size', help: 'mixpanel_batch_import_pack_size', diff --git a/src/v0/destinations/mp/config.js b/src/v0/destinations/mp/config.js index 3abdf2eebb..35b40294f5 100644 --- a/src/v0/destinations/mp/config.js +++ b/src/v0/destinations/mp/config.js @@ -49,6 +49,7 @@ const MP_IDENTIFY_EXCLUSION_LIST = [ ]; const GEO_SOURCE_ALLOWED_VALUES = [null, 'reverse_geocoding']; +const TRACK_MAX_BATCH_SIZE = 50; const IMPORT_MAX_BATCH_SIZE = 2000; const ENGAGE_MAX_BATCH_SIZE = 2000; const GROUPS_MAX_BATCH_SIZE = 200; @@ -67,6 +68,7 @@ module.exports = { MP_IDENTIFY_EXCLUSION_LIST, getCreateDeletionTaskEndpoint, DISTINCT_ID_MAX_BATCH_SIZE, + TRACK_MAX_BATCH_SIZE, IMPORT_MAX_BATCH_SIZE, ENGAGE_MAX_BATCH_SIZE, GROUPS_MAX_BATCH_SIZE, diff --git a/src/v0/destinations/mp/transform.js b/src/v0/destinations/mp/transform.js index 195c42fbee..09a7862f9a 100644 --- a/src/v0/destinations/mp/transform.js +++ b/src/v0/destinations/mp/transform.js @@ -24,6 +24,7 @@ const { mappingConfig, BASE_ENDPOINT, BASE_ENDPOINT_EU, + TRACK_MAX_BATCH_SIZE, IMPORT_MAX_BATCH_SIZE, ENGAGE_MAX_BATCH_SIZE, GROUPS_MAX_BATCH_SIZE, @@ -46,19 +47,21 @@ const mPEventPropertiesConfigJson = mappingConfig[ConfigCategory.EVENT_PROPERTIE const setImportCredentials = (destConfig) => { const endpoint = destConfig.dataResidency === 'eu' ? `${BASE_ENDPOINT_EU}/import/` : `${BASE_ENDPOINT}/import/`; + const headers = { 'Content-Type': 'application/json' }; const params = { strict: destConfig.strictMode ? 1 : 0 }; - const { serviceAccountUserName, serviceAccountSecret, projectId, token } = destConfig; - let credentials; - if (token) { - credentials = `${token}:`; + const { apiSecret, serviceAccountUserName, serviceAccountSecret, projectId } = destConfig; + if (apiSecret) { + headers.Authorization = `Basic ${base64Convertor(`${apiSecret}:`)}`; } else if (serviceAccountUserName && serviceAccountSecret && projectId) { - credentials = `${serviceAccountUserName}:${serviceAccountSecret}`; + headers.Authorization = `Basic ${base64Convertor( + `${serviceAccountUserName}:${serviceAccountSecret}`, + )}`; params.projectId = projectId; + } else { + throw new InstrumentationError( + 'Event timestamp is older than 5 days and no API secret or service account credentials (i.e. username, secret and projectId) are provided in destination configuration', + ); } - const headers = { - 'Content-Type': 'application/json', - Authorization: `Basic ${base64Convertor(credentials)}`, - }; return { endpoint, headers, params }; }; @@ -67,26 +70,37 @@ const responseBuilderSimple = (payload, message, eventType, destConfig) => { response.method = defaultPostRequestConfig.requestMethod; response.userId = message.userId || message.anonymousId; response.body.JSON_ARRAY = { batch: JSON.stringify([removeUndefinedValues(payload)]) }; - const { dataResidency } = destConfig; + const { apiSecret, serviceAccountUserName, serviceAccountSecret, projectId, dataResidency } = + destConfig; const duration = getTimeDifference(message.timestamp); switch (eventType) { case EventType.ALIAS: case EventType.TRACK: case EventType.SCREEN: - case EventType.PAGE: { - if (duration.years > 5) { + case EventType.PAGE: + if ( + !apiSecret && + !(serviceAccountUserName && serviceAccountSecret && projectId) && + duration.days <= 5 + ) { + response.endpoint = + dataResidency === 'eu' ? `${BASE_ENDPOINT_EU}/track/` : `${BASE_ENDPOINT}/track/`; + response.headers = {}; + } else if (duration.years > 5) { throw new InstrumentationError('Event timestamp should be within last 5 years'); + } else { + const credentials = setImportCredentials(destConfig); + response.endpoint = credentials.endpoint; + response.headers = credentials.headers; + response.params = { + project_id: credentials.params?.projectId, + strict: credentials.params.strict, + }; + break; } - const credentials = setImportCredentials(destConfig); - response.endpoint = credentials.endpoint; - response.headers = credentials.headers; - response.params = { - project_id: credentials.params?.projectId, - strict: credentials.params.strict, - }; break; - } - case 'merge': { + case 'merge': + // eslint-disable-next-line no-case-declarations const credentials = setImportCredentials(destConfig); response.endpoint = credentials.endpoint; response.headers = credentials.headers; @@ -95,7 +109,7 @@ const responseBuilderSimple = (payload, message, eventType, destConfig) => { strict: credentials.params.strict, }; break; - } + default: response.endpoint = dataResidency === 'eu' ? `${BASE_ENDPOINT_EU}/engage/` : `${BASE_ENDPOINT}/engage/`; @@ -470,6 +484,7 @@ const processRouterDest = async (inputs, reqMetadata) => { const batchSize = { engage: 0, groups: 0, + track: 0, import: 0, }; @@ -501,16 +516,23 @@ const processRouterDest = async (inputs, reqMetadata) => { ); transformedPayloads = lodash.flatMap(transformedPayloads); - const { engageEvents, groupsEvents, importEvents, batchErrorRespList } = + const { engageEvents, groupsEvents, trackEvents, importEvents, batchErrorRespList } = groupEventsByEndpoint(transformedPayloads); const engageRespList = batchEvents(engageEvents, ENGAGE_MAX_BATCH_SIZE, reqMetadata); const groupsRespList = batchEvents(groupsEvents, GROUPS_MAX_BATCH_SIZE, reqMetadata); + const trackRespList = batchEvents(trackEvents, TRACK_MAX_BATCH_SIZE, reqMetadata); const importRespList = batchEvents(importEvents, IMPORT_MAX_BATCH_SIZE, reqMetadata); - const batchSuccessRespList = [...engageRespList, ...groupsRespList, ...importRespList]; + const batchSuccessRespList = [ + ...engageRespList, + ...groupsRespList, + ...trackRespList, + ...importRespList, + ]; batchSize.engage += engageRespList.length; batchSize.groups += groupsRespList.length; + batchSize.track += trackRespList.length; batchSize.import += importRespList.length; return [...batchSuccessRespList, ...batchErrorRespList]; diff --git a/src/v0/destinations/mp/util.js b/src/v0/destinations/mp/util.js index b2807d6e11..d564e805ad 100644 --- a/src/v0/destinations/mp/util.js +++ b/src/v0/destinations/mp/util.js @@ -136,7 +136,7 @@ const createIdentifyResponse = (message, type, destination, responseBuilderSimpl * @returns */ const isImportAuthCredentialsAvailable = (destination) => - destination.Config.token || + destination.Config.apiSecret || (destination.Config.serviceAccountSecret && destination.Config.serviceAccountUserName && destination.Config.projectId); @@ -179,6 +179,7 @@ const groupEventsByEndpoint = (events) => { const eventMap = { engage: [], groups: [], + track: [], import: [], }; const batchErrorRespList = []; @@ -203,6 +204,7 @@ const groupEventsByEndpoint = (events) => { return { engageEvents: eventMap.engage, groupsEvents: eventMap.groups, + trackEvents: eventMap.track, importEvents: eventMap.import, batchErrorRespList, }; @@ -347,6 +349,7 @@ const generatePageOrScreenCustomEventName = (message, userDefinedEventTemplate) * @param {Object} batchSize - The object containing the batch size for different endpoints. * @param {number} batchSize.engage - The batch size for engage endpoint. * @param {number} batchSize.groups - The batch size for group endpoint. + * @param {number} batchSize.track - The batch size for track endpoint. * @param {number} batchSize.import - The batch size for import endpoint. * @param {string} destinationId - The ID of the destination. * @returns {void} @@ -358,6 +361,9 @@ const recordBatchSizeMetrics = (batchSize, destinationId) => { stats.gauge('mixpanel_batch_group_pack_size', batchSize.groups, { destination_id: destinationId, }); + stats.gauge('mixpanel_batch_track_pack_size', batchSize.track, { + destination_id: destinationId, + }); stats.gauge('mixpanel_batch_import_pack_size', batchSize.import, { destination_id: destinationId, }); diff --git a/src/v0/destinations/mp/util.test.js b/src/v0/destinations/mp/util.test.js index 3666081f59..40cdb34649 100644 --- a/src/v0/destinations/mp/util.test.js +++ b/src/v0/destinations/mp/util.test.js @@ -18,6 +18,7 @@ describe('Unit test cases for groupEventsByEndpoint', () => { expect(result).toEqual({ engageEvents: [], groupsEvents: [], + trackEvents: [], importEvents: [], batchErrorRespList: [], }); @@ -121,6 +122,19 @@ describe('Unit test cases for groupEventsByEndpoint', () => { }, }, ], + trackEvents: [ + { + message: { + endpoint: '/track', + body: { + JSON_ARRAY: { + batch: '[{prop:4}]', + }, + }, + userId: 'user1', + }, + }, + ], importEvents: [ { message: { diff --git a/test/integrations/destinations/mp/common.ts b/test/integrations/destinations/mp/common.ts index d40afa0c02..82f0e3202b 100644 --- a/test/integrations/destinations/mp/common.ts +++ b/test/integrations/destinations/mp/common.ts @@ -7,7 +7,7 @@ const defaultMockFns = () => { const sampleDestination: Destination = { Config: { apiKey: 'dummyApiKey', - token: 'test_api_token', + token: 'dummyApiKey', prefixProperties: true, useNativeSDK: false, }, diff --git a/test/integrations/destinations/mp/processor/data.ts b/test/integrations/destinations/mp/processor/data.ts index db5bc840c2..2d70d15384 100644 --- a/test/integrations/destinations/mp/processor/data.ts +++ b/test/integrations/destinations/mp/processor/data.ts @@ -12,7 +12,7 @@ export const data = [ request: { body: [ { - destination: overrideDestination(sampleDestination, { token: 'test_api_token' }), + destination: overrideDestination(sampleDestination, { token: 'dummyApiKey' }), message: { anonymousId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', channel: 'web', @@ -87,17 +87,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","campaign_id":"test_name","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","campaign_id":"test_name","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -194,17 +191,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Viewed a Contact Us page","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us","category":"Contact","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Viewed a Contact Us page","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us","category":"Contact","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -278,17 +272,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Screen","properties":{"category":"communication","ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', + '[{"event":"Loaded a Screen","properties":{"category":"communication","ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', }, XML: {}, FORM: {}, @@ -369,17 +360,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Screen","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","ip":"0.0.0.0","$user_id":"hjiklmk","$screen_dpi":2,"mp_lib":"RudderLabs Android SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjiklmk","time":1579847342402,"name":"Contact Us","category":"Contact"}}]', + '[{"event":"Loaded a Screen","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","ip":"0.0.0.0","$user_id":"hjiklmk","$screen_dpi":2,"mp_lib":"RudderLabs Android SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjiklmk","time":1579847342402,"name":"Contact Us","category":"Contact"}}]', }, XML: {}, FORM: {}, @@ -452,17 +440,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Screen","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', + '[{"event":"Loaded a Screen","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', }, XML: {}, FORM: {}, @@ -565,7 +550,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -679,7 +664,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":45.89}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":45.89}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -701,7 +686,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$add":{"counter":1,"item_purchased":"2"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$add":{"counter":1,"item_purchased":"2"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -716,17 +701,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":45.89,"counter":1,"item_purchased":"2","number_of_logins":"","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","campaign_id":"test_name","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342403,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":45.89,"counter":1,"item_purchased":"2","number_of_logins":"","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","campaign_id":"test_name","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342403,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -814,17 +796,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"test_api_token"}}]', + '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"dummyApiKey"}}]', }, XML: {}, FORM: {}, @@ -954,7 +933,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -969,17 +948,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1113,7 +1089,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":34}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":34}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -1128,17 +1104,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","revenue":34,"key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","first_name":"Mickey","lastName":"Mouse","name":"Mickey Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","revenue":34,"key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","first_name":"Mickey","lastName":"Mouse","name":"Mickey Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1262,17 +1235,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":" new Order Completed totally","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":" new Order Completed totally","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1396,17 +1366,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":" Order Completed ","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"Billing Amount":"77","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":" Order Completed ","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"Billing Amount":"77","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1574,7 +1541,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -1661,7 +1628,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', + '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -1683,7 +1650,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', + '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', }, XML: {}, FORM: {}, @@ -1770,7 +1737,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"company":["testComp","testComp1"]},"$ip":"0.0.0.0"}]', + '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"company":["testComp","testComp1"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -1792,7 +1759,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":["testComp","testComp1"]}}]', + '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp","$set":{"company":["testComp","testComp1"]}}]', }, XML: {}, FORM: {}, @@ -1814,7 +1781,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp1","$set":{"company":["testComp","testComp1"]}}]', + '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp1","$set":{"company":["testComp","testComp1"]}}]', }, XML: {}, FORM: {}, @@ -1902,7 +1869,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', + '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -1924,7 +1891,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', + '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', }, XML: {}, FORM: {}, @@ -2052,7 +2019,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -2067,17 +2034,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api-eu.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api-eu.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstname":"Mickey","lastname":"Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstname":"Mickey","lastname":"Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -2165,7 +2129,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","$android_devices":["test_device_token"],"$os":"Android","$android_model":"Android SDK built for x86","$android_os_version":"8.1.0","$android_manufacturer":"Google","$android_app_version":"1.0","$android_app_version_code":"1.0","$android_brand":"Google"},"$token":"test_api_token","$distinct_id":"5094f5704b9cf2b3","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","$android_devices":["test_device_token"],"$os":"Android","$android_model":"Android SDK built for x86","$android_os_version":"8.1.0","$android_manufacturer":"Google","$android_app_version":"1.0","$android_app_version_code":"1.0","$android_brand":"Google"},"$token":"dummyApiKey","$distinct_id":"5094f5704b9cf2b3","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -2252,7 +2216,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"test_api_token","$distinct_id":"test_user_id","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"dummyApiKey","$distinct_id":"test_user_id","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -2269,7 +2233,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -2277,7 +2241,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"test_api_token"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"dummyApiKey"}}]', }, XML: {}, FORM: {}, @@ -2364,17 +2328,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Page","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","category":"communication","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Loaded a Page","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","category":"communication","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -2462,17 +2423,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"test_api_token"}}]', + '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"dummyApiKey"}}]', }, XML: {}, FORM: {}, @@ -2565,7 +2523,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","createdat":"2020-01-23T08:54:02.362Z","$ios_devices":["test_device_token"],"$ios_device_model":"Android SDK built for x86","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"test_api_token","$distinct_id":"test_user_id","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","createdat":"2020-01-23T08:54:02.362Z","$ios_devices":["test_device_token"],"$ios_device_model":"Android SDK built for x86","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"dummyApiKey","$distinct_id":"test_user_id","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -2582,7 +2540,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -2590,7 +2548,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"test_api_token"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"dummyApiKey"}}]', }, XML: {}, FORM: {}, @@ -2683,7 +2641,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -2775,7 +2733,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -2868,7 +2826,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -2966,7 +2924,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3062,7 +3020,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3157,7 +3115,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3251,7 +3209,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3346,7 +3304,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3447,7 +3405,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3544,29 +3502,17 @@ export const data = [ status: 200, body: [ { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, - body: { - JSON: {}, - JSON_ARRAY: { - batch: - '[{"event":"FirstTrackCall12","properties":{"foo":"bar","$deviceId":"nkasdnkasd","anonymousId":"ea776ad0-3136-44fb-9216-5b1578609a2b","userId":"as09sufa09usaf09as0f9uasf","id":"as09sufa09usaf09as0f9uasf","firstName":"Bob","lastName":"Marley","name":"Bob Marley","age":43,"email":"bob@marleymail.com","phone":"+447748544123","birthday":"1987-01-01T20:08:59+0000","createdAt":"2022-01-21T14:10:12+0000","address":"51,B.L.T road, Kolkata-700060","description":"I am great","gender":"male","title":"Founder","username":"bobm","website":"https://bobm.com","randomProperty":"randomValue","$user_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$current_url":"http://127.0.0.1:7307/Testing/App_for_testingTool/","$referrer":"http://127.0.0.1:7307/Testing/","$screen_height":900,"$screen_width":1440,"$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.1.18","$insert_id":"0d5c1a4a-27e4-41da-a246-4d01f44e74bd","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1632986123523,"$browser":"Chrome","$browser_version":"93.0.4577.82"}}]', - }, - XML: {}, - FORM: {}, - }, - files: {}, - userId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', + error: + 'Event timestamp is older than 5 days and no API secret or service account credentials (i.e. username, secret and projectId) are provided in destination configuration', + statTags: { + destType: 'MP', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', }, - statusCode: 200, + statusCode: 400, }, ], }, @@ -3740,7 +3686,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -3748,7 +3694,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"MainActivity","properties":{"name":"MainActivity","automatic":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$user_id":"test_user_id","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"test_api_token","distinct_id":"test_user_id","time":1520845503421}}]', + '[{"event":"MainActivity","properties":{"name":"MainActivity","automatic":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$user_id":"test_user_id","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"dummyApiKey","distinct_id":"test_user_id","time":1520845503421}}]', }, XML: {}, FORM: {}, @@ -4019,7 +3965,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402,"$ignore_time":true}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402,"$ignore_time":true}]', }, XML: {}, FORM: {}, @@ -4125,7 +4071,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4330,7 +4276,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"user1234","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"user1234","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4348,14 +4294,18 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + Authorization: + 'Basic cnVkZGVyLmQyYTNmMS5tcC1zZXJ2aWNlLWFjY291bnQ6amF0cFF4Y2pNaDhlZXRrMXhySDNLalFJYnp5NGlYOGI=', + }, + params: { + project_id: '123456', + strict: 0, }, - params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["user1234","e6ab2c5e-2cda-44a9-a962-e2f67df78bca"],"token":"test_api_token"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["user1234","e6ab2c5e-2cda-44a9-a962-e2f67df78bca"],"token":"dummyApiKey"}}]', }, XML: {}, FORM: {}, @@ -4440,7 +4390,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + Authorization: 'Basic ZHVtbXlBcGlLZXk6', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -4448,7 +4398,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', + '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', }, XML: {}, FORM: {}, @@ -4530,7 +4480,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + Authorization: 'Basic ZHVtbXlBcGlLZXk6', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -4538,7 +4488,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Opened","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', + '[{"event":"Application Opened","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', }, XML: {}, FORM: {}, @@ -4626,7 +4576,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"groupId":["testGroupId"]},"$ip":"0.0.0.0"}]', + '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"groupId":["testGroupId"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -4648,7 +4598,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"test_api_token","$group_key":"groupId","$group_id":"testGroupId","$set":{"company":"testComp","groupId":"groupIdInTraits"}}]', + '[{"$token":"dummyApiKey","$group_key":"groupId","$group_id":"testGroupId","$set":{"company":"testComp","groupId":"groupIdInTraits"}}]', }, XML: {}, FORM: {}, @@ -4675,7 +4625,7 @@ export const data = [ description: 'Track: set device id and user id when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { - token: 'test_api_token', + token: 'dummyApiKey', identityMergeApi: 'simplified', }), message: { @@ -4731,17 +4681,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Product Viewed","properties":{"name":"T-Shirt","$user_id":"userId01","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"test_api_token","distinct_id":"userId01","time":1579847342402,"$device_id":"anonId01"}}]', + '[{"event":"Product Viewed","properties":{"name":"T-Shirt","$user_id":"userId01","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"dummyApiKey","distinct_id":"userId01","time":1579847342402,"$device_id":"anonId01"}}]', }, XML: {}, FORM: {}, @@ -4770,7 +4717,7 @@ export const data = [ { description: 'Identify: skip merge event when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { - token: 'test_api_token', + token: 'dummyApiKey', identityMergeApi: 'simplified', }), message: { @@ -4851,7 +4798,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"userId01","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"userId01","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4879,7 +4826,7 @@ export const data = [ 'Identify: append $device: to deviceId while creating the user when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'test_api_token', + token: 'dummyApiKey', identityMergeApi: 'simplified', }), message: { @@ -4959,7 +4906,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"$device:anonId01","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"$device:anonId01","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4986,7 +4933,7 @@ export const data = [ description: 'Unsupported alias call when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'test_api_token', + token: 'dummyApiKey', identityMergeApi: 'simplified', }), message: { @@ -5075,7 +5022,7 @@ export const data = [ 'Track revenue event: set device id and user id when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'test_api_token', + token: 'dummyApiKey', identityMergeApi: 'simplified', }), message: { @@ -5146,7 +5093,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":18.9}},"$token":"test_api_token","$distinct_id":"userId01"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":18.9}},"$token":"dummyApiKey","$distinct_id":"userId01"}]', }, XML: {}, FORM: {}, @@ -5161,17 +5108,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":18.9,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$user_id":"userId01","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"test_api_token","distinct_id":"userId01","time":1579847342403,"$device_id":"anonId01","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":18.9,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$user_id":"userId01","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"dummyApiKey","distinct_id":"userId01","time":1579847342403,"$device_id":"anonId01","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -5201,7 +5145,7 @@ export const data = [ description: 'Page with anonymous user when simplified api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'test_api_token', + token: 'dummyApiKey', identityMergeApi: 'simplified', }), message: { @@ -5268,17 +5212,14 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/import/', - headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', - 'Content-Type': 'application/json', - }, - params: { strict: 0 }, + endpoint: 'https://api.mixpanel.com/track/', + headers: {}, + params: {}, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"$device:anonId01","time":1579847342402,"$device_id":"anonId01","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"$device:anonId01","time":1579847342402,"$device_id":"anonId01","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -5308,7 +5249,7 @@ export const data = [ description: 'Group call with anonymous user when simplified api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'test_api_token', + token: 'dummyApiKey', identityMergeApi: 'simplified', groupKeySettings: [{ groupKey: 'company' }], }), @@ -5371,7 +5312,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"test_api_token","$distinct_id":"$device:anonId01","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', + '[{"$token":"dummyApiKey","$distinct_id":"$device:anonId01","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -5393,7 +5334,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', + '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', }, XML: {}, FORM: {}, @@ -5419,7 +5360,7 @@ export const data = [ { destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'test_api_token', + token: 'dummyApiKey', identityMergeApi: 'simplified', groupKeySettings: [{ groupKey: 'company' }], }), @@ -5538,7 +5479,7 @@ export const data = [ }, destination: overrideDestination(sampleDestination, { apiKey: 'dummyApiKey', - token: 'test_api_token', + token: 'dummyApiKey', apiSecret: 'dummyApiKey', useNewMapping: true, }), @@ -5564,7 +5505,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":12.13}},"$token":"test_api_token","$distinct_id":"39da706ec83d0e90"}]', + '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":12.13}},"$token":"dummyApiKey","$distinct_id":"39da706ec83d0e90"}]', }, XML: {}, FORM: {}, @@ -5581,7 +5522,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + Authorization: 'Basic ZHVtbXlBcGlLZXk6', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -5589,7 +5530,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":12.13,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', + '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":12.13,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', }, XML: {}, FORM: {}, @@ -5653,7 +5594,7 @@ export const data = [ }, destination: overrideDestination(sampleDestination, { apiKey: 'dummyApiKey', - token: 'test_api_token', + token: 'dummyApiKey', apiSecret: 'dummyApiKey', useNewMapping: true, }), @@ -5679,7 +5620,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":23.45}},"$token":"test_api_token","$distinct_id":"39da706ec83d0e90"}]', + '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":23.45}},"$token":"dummyApiKey","$distinct_id":"39da706ec83d0e90"}]', }, XML: {}, FORM: {}, @@ -5696,7 +5637,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + Authorization: 'Basic ZHVtbXlBcGlLZXk6', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -5704,7 +5645,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":23.45,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":null}}]', + '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":23.45,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":null}}]', }, XML: {}, FORM: {}, @@ -5731,7 +5672,7 @@ export const data = [ description: 'Track: with strict mode enabled', destination: overrideDestination(sampleDestination, { apiKey: 'dummyApiKey', - token: 'test_api_token', + token: 'dummyApiKey', apiSecret: 'some_api_secret', dataResidency: 'eu', strictMode: true, @@ -5795,7 +5736,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"test_api_token","$distinct_id":"test_user_id","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"dummyApiKey","$distinct_id":"test_user_id","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -5812,7 +5753,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', 'Content-Type': 'application/json', }, params: { strict: 1 }, @@ -5820,7 +5761,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"test_api_token"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"dummyApiKey"}}]', }, XML: {}, FORM: {}, diff --git a/test/integrations/destinations/mp/router/data.ts b/test/integrations/destinations/mp/router/data.ts index 8716c9daa0..059e222e92 100644 --- a/test/integrations/destinations/mp/router/data.ts +++ b/test/integrations/destinations/mp/router/data.ts @@ -442,7 +442,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', }, params: { strict: 1 }, body: { @@ -509,7 +509,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', }, params: { strict: 1 }, body: { @@ -577,7 +577,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', }, params: { strict: 1 }, body: { @@ -1166,7 +1166,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', }, params: { strict: 1 }, body: { @@ -1232,7 +1232,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', }, params: { strict: 1 }, body: { @@ -1299,7 +1299,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', }, params: { strict: 1 }, body: { From 63a5c7333ec019c95e9e801f8beac4dd56885c9e Mon Sep 17 00:00:00 2001 From: Jayachand Date: Thu, 18 Apr 2024 15:55:06 +0530 Subject: [PATCH 111/240] chore: support event validation for old and new tracking plan payload formats (#3255) * chore: support event validation for old and new tracking plan payload formats --- src/util/eventValidation.js | 2 +- src/util/trackingPlan.js | 5 + test/__tests__/eventValidation.test.js | 235 +++++++++++++++++++++++++ 3 files changed, 241 insertions(+), 1 deletion(-) diff --git a/src/util/eventValidation.js b/src/util/eventValidation.js index 68d895dcc5..9f3ecd859d 100644 --- a/src/util/eventValidation.js +++ b/src/util/eventValidation.js @@ -126,7 +126,7 @@ async function validate(event) { trackingPlanId, trackingPlanVersion, event.message.type, - event.message.event, + event.message.type === 'track' ? event.message.event : '', workspaceId, ); diff --git a/src/util/trackingPlan.js b/src/util/trackingPlan.js index a77265a5b8..ebfbc6049f 100644 --- a/src/util/trackingPlan.js +++ b/src/util/trackingPlan.js @@ -55,6 +55,11 @@ async function getEventSchema(tpId, tpVersion, eventType, eventName, workspaceId let eventSchema; const tp = await getTrackingPlan(tpId, tpVersion, workspaceId); + if (Object.hasOwn(tp, 'events')) { + const ev = tp.events.find((e) => e.name === eventName && e.eventType === eventType); + return ev?.rules; + } + if (eventType !== 'track') { if (Object.prototype.hasOwnProperty.call(tp.rules, eventType)) { eventSchema = tp.rules[eventType]; diff --git a/test/__tests__/eventValidation.test.js b/test/__tests__/eventValidation.test.js index eb58879eb7..b802b6f886 100644 --- a/test/__tests__/eventValidation.test.js +++ b/test/__tests__/eventValidation.test.js @@ -73,6 +73,97 @@ const trackingPlan = { create_time: "2021-12-14T19:19:13.666Z", update_time: "2021-12-15T13:29:59.272Z" }; + +const newTrackingPlan = { + name: "Demo Tracking Plan", + version: 1, + events: [ + { + id: "ev_22HzyIUtuhfoI80iDfAgf47GHpw", + name: "Product clicked new", + eventType: "track", + description: "Fired when an product is clicked.", + rules: { + type: "object", + $schema: "http://json-schema.org/draft-07/schema#", + required: ["properties"], + properties: { + properties: { + $schema: "http://json-schema.org/draft-07/schema#", + additionalProperties: false, + properties: { + email: { + type: ["string"] + }, + name: { + type: ["string"] + }, + prop_float: { + type: ["number"] + }, + prop_integer: { + type: ["number"] + }, + revenue: { + type: ["number"] + } + }, + type: "object", + required: [ + "email", + "name", + "prop_float", + "prop_integer", + "revenue" + ], + allOf: [ + { + properties: { + prop_integer: { + const: 2 + }, + prop_float: { + const: 2.3 + } + } + } + ] + } + } + } + }, + { + id: "ev_22HzyIUtuhfoI80iDfAgf47GHpx", + name: "", + eventType: "group", + rules: { + type: "object", + $schema: "http://json-schema.org/draft-07/schema#", + required: ["traits"], + properties: { + traits: { + additionalProperties: false, + properties: { + company: { + type: ["string"] + }, + org: { + type: ["string"] + }, + }, + type: "object", + required: [ + "company", + ] + } + } + } + } + ], + workspaceId: "dummy_workspace_id", + createdAt: "2021-12-14T19:19:13.666Z", + updatedAt: "2021-12-15T13:29:59.272Z" +}; const sourceTpConfig = { track: { allowUnplannedEvents: "true", @@ -1364,6 +1455,134 @@ const eventValidationTestCases = [ } ]; +const eventValidationWithNewPlanTestCases = [ + { + testCase: "Group is part of new Tracking Plan + additional property violation", + event: { + metadata: { + trackingPlanId: "dummy_tracking_plan_id_new", + trackingPlanVersion: "dummy_version_new", + workspaceId: "dummy_workspace_id", + mergedTpConfig, + sourceTpConfig + }, + message: { + type: "group", + userId: "user12345", + groupId: "group1", + traits: { + company: "Company", + employees: 123 + }, + context: { + traits: { + trait1: "new-val" + }, + ip: "14.5.67.21", + library: { + name: "http" + } + }, + timestamp: "2020-01-21T00:21:34.208Z" + } + }, + trackingPlan: newTrackingPlan, + output: { + dropEvent: true, + violationType: violationTypes.AdditionalProperties + } + }, + { + testCase: + "Compatibility for Spread sheet plugin + Track is not part of new Tracking Plan and allowUnplannedEvents is set to text TRUE", + event: { + metadata: { + trackingPlanId: "dummy_tracking_plan_id_new", + trackingPlanVersion: "dummy_version_new", + workspaceId: "dummy_workspace_id", + mergedTpConfig: { + allowUnplannedEvents: "TRUE", + ajvOptions: {} + }, + sourceTpConfig: { + track: { + allowUnplannedEvents: "TRUE", + ajvOptions: {} + }, + global: { + allowUnplannedEvents: "FALSE", + ajvOptions: {} + } + } + }, + message: { + type: "track", + userId: "user-demo", + event: "New Product clicked", + properties: { + name: "Rubik's Cube", + revenue: 4.99, + prop_integer: 2, + prop_float: 2.3, + email: "demo@rudderstack.com" + }, + context: { + ip: "14.5.67.21" + }, + timestamp: "2020-02-02T00:23:09.544Z" + } + }, + trackingPlan: newTrackingPlan, + output: { + dropEvent: false, + violationType: "None" + } + }, + { + testCase: + "Track is part of new Tracking Plan + no track config and unplannedProperties is set to drop", + event: { + metadata: { + trackingPlanId: "dummy_tracking_plan_id_new", + trackingPlanVersion: "dummy_version_new", + workspaceId: "dummy_workspace_id", + mergedTpConfig: { + unplannedProperties: "drop", + ajvOptions: {} + }, + sourceTpConfig: { + global: { + unplannedProperties: "drop", + ajvOptions: {} + } + } + }, + message: { + type: "track", + userId: "user-demo", + event: "Product clicked new", + properties: { + name: "Rubik's Cube", + revenue: 4.99, + prop_integer: 2, + prop_float: 2.3, + email: "demo@rudderstack.com", + mobile: "999888777666" + }, + context: { + ip: "14.5.67.21" + }, + timestamp: "2020-02-02T00:23:09.544Z" + } + }, + trackingPlan: newTrackingPlan, + output: { + dropEvent: true, + violationType: violationTypes.AdditionalProperties + } + }, +]; + describe("Supported Event types testing", () => { eventTypesTestCases.forEach(testCase => { it(`should return isSupportedOrNot ${testCase.output} for this input eventType ${testCase.eventType} everytime`, () => { @@ -1389,6 +1608,22 @@ describe("Handle validation", () => { }); }); +describe("Handle validation with new tracking plan payload", () => { + eventValidationWithNewPlanTestCases.forEach(testCase => { + it(`should return dropEvent: ${testCase.output.dropEvent}, violationType: ${testCase.output.violationType}`, async () => { + fetch.mockResolvedValue({ + json: jest.fn().mockResolvedValue(testCase.trackingPlan), + status: 200 + }); + const { dropEvent, violationType } = await handleValidation( + testCase.event + ); + expect(dropEvent).toEqual(testCase.output.dropEvent); + expect(violationType).toEqual(testCase.output.violationType); + }); + }); +}); + describe("HandleValidationErrors", () => { validationErrorsTestCases.forEach(testCase => { it(`should return dropEvent ${testCase.output} for ${testCase.test}`, () => { From e92b052e03182deb41b20b3ec3741306afa50380 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Thu, 18 Apr 2024 17:18:27 +0530 Subject: [PATCH 112/240] fix: twitter_ads logger (#3295) --- src/v0/destinations/twitter_ads/transform.js | 3 +- .../destinations/twitter_ads/router/data.ts | 204 ++++++++++++++++++ 2 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 test/integrations/destinations/twitter_ads/router/data.ts diff --git a/src/v0/destinations/twitter_ads/transform.js b/src/v0/destinations/twitter_ads/transform.js index 328d8c4a9f..268dca3636 100644 --- a/src/v0/destinations/twitter_ads/transform.js +++ b/src/v0/destinations/twitter_ads/transform.js @@ -156,8 +156,7 @@ function validateRequest(message) { } } -function process(event, requestMetadata, logger) { - logger.debug(`[TWITTER ADS]: Transforming request received with info`); +function process(event) { const { message, metadata, destination } = event; validateRequest(message); diff --git a/test/integrations/destinations/twitter_ads/router/data.ts b/test/integrations/destinations/twitter_ads/router/data.ts new file mode 100644 index 0000000000..ce9aea6595 --- /dev/null +++ b/test/integrations/destinations/twitter_ads/router/data.ts @@ -0,0 +1,204 @@ +const authHeaderConstant = + 'OAuth oauth_consumer_key="qwe", oauth_nonce="V1kMh028kZLLhfeYozuL0B45Pcx6LvuW", oauth_signature="Di4cuoGv4PnCMMEeqfWTcqhvdwc%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1685603652", oauth_token="dummyAccessToken", oauth_version="1.0"'; + +export const data = [ + { + name: 'twitter_ads', + description: 'tests router flow', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'track', + event: 'ABC Searched', + channel: 'web', + context: { + source: 'test', + userAgent: 'chrome', + traits: { + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + email: 'abc@gmail.com', + phone: '+1234589947', + ge: 'male', + db: '19950715', + lastname: 'Rudderlabs', + firstName: 'Test', + address: { + city: 'Kolkata', + state: 'WB', + zip: '700114', + country: 'IN', + }, + }, + device: { + advertisingId: 'abc123', + }, + library: { + name: 'rudder-sdk-ruby-sync', + version: '1.0.6', + }, + }, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: { + conversionTime: '2023-06-01T06:03:08.739Z', + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, + currency: 'USD', + priceCurrency: 'USD', + conversionId: '213123', + numberItems: '2323', + phone: '+919927455678', + twclid: '543', + shipping: 3, + subtotal: 22.5, + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', + email: 'abc@ax.com', + contents: [ + { + price: '123.3345', + quantity: '12', + id: '12', + }, + { + price: 200, + quantity: 11, + id: '4', + }, + ], + }, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { + All: true, + }, + }, + metadata: { + secret: { + consumerKey: 'qwe', + consumerSecret: 'fdghv', + accessToken: 'dummyAccessToken', + accessTokenSecret: 'testAccessTokenSecret', + }, + }, + destination: { + Config: { + pixelId: 'dummyPixelId', + rudderAccountId: '2EOknn1JNH7WK1MfNku4fGYKkRK', + twitterAdsEventNames: [ + { + rudderEventName: 'ABC Searched', + twitterEventId: 'tw-234234324234', + }, + { + rudderEventName: 'Home Page Viewed', + twitterEventId: 'tw-odt2o-odt2q', + }, + ], + }, + }, + }, + ], + destType: 'twitter_ads', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batched: false, + batchedRequest: { + body: { + FORM: {}, + JSON: { + conversions: [ + { + contents: [ + { content_id: '12', content_price: 123.3345, num_items: 12 }, + { content_id: '4', content_price: 200, num_items: 11 }, + ], + conversion_id: '213123', + conversion_time: '2023-06-01T06:03:08.739Z', + event_id: 'tw-234234324234', + identifiers: [ + { + hashed_email: + '4c3c8a8cba2f3bb1e9e617301f85d1f68e816a01c7b716f482f2ab9adb8181fb', + }, + { + hashed_phone_number: + 'b308962b96b40cce7981493a372db9478edae79f83c2d8ca6cd15a39566f8c56', + }, + { twclid: '543' }, + ], + number_items: 2, + price_currency: 'USD', + value: '25', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + }, + endpoint: 'https://ads-api.twitter.com/12/measurement/conversions/dummyPixelId', + files: {}, + headers: { + Authorization: + 'OAuth oauth_consumer_key="qwe", oauth_nonce="V1kMh028kZLLhfeYozuL0B45Pcx6LvuW", oauth_signature="Di4cuoGv4PnCMMEeqfWTcqhvdwc%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1685603652", oauth_token="dummyAccessToken", oauth_version="1.0"', + 'Content-Type': 'application/json', + }, + method: 'POST', + params: {}, + type: 'REST', + version: '1', + }, + destination: { + Config: { + pixelId: 'dummyPixelId', + rudderAccountId: '2EOknn1JNH7WK1MfNku4fGYKkRK', + twitterAdsEventNames: [ + { rudderEventName: 'ABC Searched', twitterEventId: 'tw-234234324234' }, + { rudderEventName: 'Home Page Viewed', twitterEventId: 'tw-odt2o-odt2q' }, + ], + }, + }, + metadata: [ + { + secret: { + accessToken: 'dummyAccessToken', + accessTokenSecret: 'testAccessTokenSecret', + consumerKey: 'qwe', + consumerSecret: 'fdghv', + }, + }, + ], + statusCode: 200, + }, + ], + }, + }, + }, + }, +].map((tc) => ({ + ...tc, + mockFns: (_) => { + jest.mock('../../../../../src/v0/destinations/twitter_ads/util', () => ({ + getAuthHeaderForRequest: (_a, _b) => { + return { Authorization: authHeaderConstant }; + }, + })); + }, +})); From 68c64db23231252e17aa5a78b716989a7a2d7099 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 18 Apr 2024 11:51:18 +0000 Subject: [PATCH 113/240] chore(release): 1.62.2 --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5aeb88f374..87ca4738ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [1.62.2](https://github.com/rudderlabs/rudder-transformer/compare/v1.62.1...v1.62.2) (2024-04-18) + + +### Bug Fixes + +* twitter_ads logger ([#3295](https://github.com/rudderlabs/rudder-transformer/issues/3295)) ([e92b052](https://github.com/rudderlabs/rudder-transformer/commit/e92b052e03182deb41b20b3ec3741306afa50380)) + ### [1.62.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.62.0...v1.62.1) (2024-04-18) diff --git a/package-lock.json b/package-lock.json index b524ed27ff..b5b413f936 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.62.1", + "version": "1.62.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.62.1", + "version": "1.62.2", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index 57da0144ac..a291bbab90 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.62.1", + "version": "1.62.2", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From 4bf65be47678b7d058d6389b175b836e8692b698 Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Fri, 19 Apr 2024 10:52:16 +0530 Subject: [PATCH 114/240] chore: upgrade node version to v18.20.1 from v18.19.1 (#3287) --- .nvmrc | 2 +- Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.nvmrc b/.nvmrc index 3c5535cf60..99c98cdd6a 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.19.1 +18.20.1 diff --git a/Dockerfile b/Dockerfile index 8cd4005a7b..9fe3c1cdb2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1.4 -FROM node:18.19.1-alpine3.18 AS base +FROM node:18.20.1-alpine3.18 AS base ENV HUSKY 0 RUN apk update From b7b4611d9d7b051ce95b213b8391abb0bcf7b3a2 Mon Sep 17 00:00:00 2001 From: gitcommitshow <56937085+gitcommitshow@users.noreply.github.com> Date: Fri, 19 Apr 2024 21:46:08 +0530 Subject: [PATCH 115/240] docs: update cla link --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bdd76d916c..48055816e4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,5 +49,5 @@ We look forward to your feedback on improving this project. [slack]: https://resources.rudderstack.com/join-rudderstack-slack [issue]: https://github.com/rudderlabs/rudder-transformer/issues/new -[cla]: https://rudderlabs.wufoo.com/forms/rudderlabs-contributor-license-agreement +[cla]: https://forms.gle/845JRGVZaC6kPZy68 [config-generator]: https://github.com/rudderlabs/config-generator From 5402b219ccdeaafb710c8c2828e983e9864a415f Mon Sep 17 00:00:00 2001 From: Gauravudia Date: Mon, 22 Apr 2024 12:17:47 +0530 Subject: [PATCH 116/240] fix: handle empty userId --- .../destinations/bloomreach/procWorkflow.yaml | 6 +- .../movable_ink/procWorkflow.yaml | 4 +- .../destinations/bloomreach/processor/page.ts | 59 +++++++++++++- .../bloomreach/processor/validation.ts | 78 ++++++++++++++++++- .../movable_ink/processor/identify.ts | 1 + 5 files changed, 140 insertions(+), 8 deletions(-) diff --git a/src/cdk/v2/destinations/bloomreach/procWorkflow.yaml b/src/cdk/v2/destinations/bloomreach/procWorkflow.yaml index f092d90382..5a9dcaa18d 100644 --- a/src/cdk/v2/destinations/bloomreach/procWorkflow.yaml +++ b/src/cdk/v2/destinations/bloomreach/procWorkflow.yaml @@ -39,7 +39,7 @@ steps: const userId = .message.().( {{{{$.getGenericPaths("userIdOnly")}}}}; ); - $.assert(userId ?? .message.anonymousId, "Either one of userId or anonymousId is required. Aborting"); + $.assert(userId || .message.anonymousId, "Either one of userId or anonymousId is required. Aborting"); - name: prepareIdentifyPayload condition: $.context.messageType === {{$.EventType.IDENTIFY}} @@ -64,7 +64,7 @@ steps: - name: pageEventName condition: $.context.messageType === {{$.EventType.PAGE}} template: | - const category = .message.category ?? .message.properties.category; + const category = .message.category || .message.properties.category; const name = .message.name || .message.properties.name; const eventNameArray = ["Viewed"]; category ? eventNameArray.push(category); @@ -74,7 +74,7 @@ steps: - name: screenEventName condition: $.context.messageType === {{$.EventType.SCREEN}} template: | - const category = .message.category ?? .message.properties.category; + const category = .message.category || .message.properties.category; const name = .message.name || .message.properties.name; const eventNameArray = ["Viewed"]; category ? eventNameArray.push(category); diff --git a/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml b/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml index 394190049b..fef11124b3 100644 --- a/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml +++ b/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml @@ -33,7 +33,7 @@ steps: {{{{$.getGenericPaths("email")}}}}; ); - $.assert(userId ?? email ?? .message.anonymousId, "Either one of userId or email or anonymousId is required. Aborting"); + $.assert(userId || email || .message.anonymousId, "Either one of userId or email or anonymousId is required. Aborting"); $.validateEventPayload(.message); - name: preparePayload @@ -50,7 +50,7 @@ steps: )); $.context.payload = { ...(.message), - userId: userId ?? email, + userId: userId || email, timestamp: timestampInUnix, anonymousId: .message.anonymousId } diff --git a/test/integrations/destinations/bloomreach/processor/page.ts b/test/integrations/destinations/bloomreach/processor/page.ts index 0c2d27989d..3081feeb26 100644 --- a/test/integrations/destinations/bloomreach/processor/page.ts +++ b/test/integrations/destinations/bloomreach/processor/page.ts @@ -15,7 +15,7 @@ export const page: ProcessorTestData[] = [ { id: 'bloomreach-page-test-1', name: destType, - description: 'Page call with category, name', + description: 'Page call with category from properties and root-level name', scenario: 'Framework+Business', successCriteria: 'Response should contain event_name = "Viewed {{ category }} {{ name }} Page" and properties and status code should be 200', @@ -69,4 +69,61 @@ export const page: ProcessorTestData[] = [ }, }, }, + { + id: 'bloomreach-page-test-2', + name: destType, + description: 'Page call with category, name from properties', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain event_name = "Viewed {{ category }} {{ name }} Page" and properties and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'page', + anonymousId: 'anonId123', + name: '', + properties: { ...properties, name: 'Integration' }, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { cookie: 'anonId123' }, + properties: { ...properties, name: 'Integration' }, + timestamp: 1709566376, + event_type: 'Viewed Docs Integration Page', + }, + name: 'customers/events', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/bloomreach/processor/validation.ts b/test/integrations/destinations/bloomreach/processor/validation.ts index ff959d74c6..1a6199abb0 100644 --- a/test/integrations/destinations/bloomreach/processor/validation.ts +++ b/test/integrations/destinations/bloomreach/processor/validation.ts @@ -1,6 +1,13 @@ import { ProcessorTestData } from '../../../testTypes'; -import { generateMetadata } from '../../../testUtils'; -import { destType, destination, processorInstrumentationErrorStatTags } from '../common'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { + destType, + destination, + processorInstrumentationErrorStatTags, + traits, + headers, + endpoint, +} from '../common'; export const validation: ProcessorTestData[] = [ { @@ -128,4 +135,71 @@ export const validation: ProcessorTestData[] = [ }, }, }, + { + id: 'bloomreach-validation-test-4', + name: destType, + description: 'Empty userId and non empty anonymousId', + scenario: 'Framework', + successCriteria: 'Response should contain all the mapping and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + userId: '', + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { registered: '', cookie: 'anonId123' }, + properties: { + email: 'test@example.com', + first_name: 'John', + last_name: 'Doe', + phone: '1234567890', + city: 'New York', + country: 'USA', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, + }, + update_timestamp: 1709566376, + }, + name: 'customers', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/movable_ink/processor/identify.ts b/test/integrations/destinations/movable_ink/processor/identify.ts index e5bbf5a9a7..0fe806df4a 100644 --- a/test/integrations/destinations/movable_ink/processor/identify.ts +++ b/test/integrations/destinations/movable_ink/processor/identify.ts @@ -20,6 +20,7 @@ export const identify: ProcessorTestData[] = [ destination, message: { type: 'identify', + userId: '', anonymousId: 'anonId123', traits, integrations: { From bac3cc5670c149454a6063a55a4b901043b0ff02 Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Mon, 22 Apr 2024 16:40:16 +0530 Subject: [PATCH 117/240] fix(delighted): replace myAxios utility with handleHttpRequest utility (#3237) --- src/v0/destinations/delighted/util.js | 107 +++++++++--------- .../destinations/delighted/network.ts | 14 +++ .../destinations/delighted/processor/data.ts | 89 +++++++++++++++ 3 files changed, 159 insertions(+), 51 deletions(-) diff --git a/src/v0/destinations/delighted/util.js b/src/v0/destinations/delighted/util.js index c690bf5f5c..53f416b48d 100644 --- a/src/v0/destinations/delighted/util.js +++ b/src/v0/destinations/delighted/util.js @@ -1,14 +1,10 @@ -const { - NetworkInstrumentationError, - InstrumentationError, - NetworkError, -} = require('@rudderstack/integrations-lib'); -const myAxios = require('../../../util/myAxios'); +const { InstrumentationError, NetworkError } = require('@rudderstack/integrations-lib'); const { getDynamicErrorType } = require('../../../adapters/utils/networkUtils'); const { getValueFromMessage } = require('../../util'); const { ENDPOINT } = require('./config'); const tags = require('../../util/tags'); const { JSON_MIME_TYPE } = require('../../util/constant'); +const { handleHttpRequest } = require('../../../adapters/network'); const isValidEmail = (email) => { const re = @@ -41,6 +37,30 @@ const isValidUserIdOrError = (channel, userId) => { }; }; +/** + * Returns final status + * @param {*} status + * @returns + */ +const getErrorStatus = (status) => { + let errStatus; + switch (status) { + case 422: + case 401: + case 406: + case 403: + errStatus = 400; + break; + case 500: + case 503: + errStatus = 500; + break; + default: + errStatus = status; + } + return errStatus; +}; + const userValidity = async (channel, Config, userId) => { const paramsdata = {}; if (channel === 'email') { @@ -50,53 +70,38 @@ const userValidity = async (channel, Config, userId) => { } const basicAuth = Buffer.from(Config.apiKey).toString('base64'); - let response; - try { - response = await myAxios.get( - `${ENDPOINT}`, - { - headers: { - Authorization: `Basic ${basicAuth}`, - 'Content-Type': JSON_MIME_TYPE, - }, - params: paramsdata, + const { processedResponse } = await handleHttpRequest( + 'get', + `${ENDPOINT}`, + { + headers: { + Authorization: `Basic ${basicAuth}`, + 'Content-Type': JSON_MIME_TYPE, }, - { - destType: 'delighted', - feature: 'transformation', - requestMethod: 'GET', - endpointPath: '/people.json', - module: 'router', - }, - ); - if (response && response.data && response.status === 200 && Array.isArray(response.data)) { - return response.data.length > 0; - } - throw new NetworkInstrumentationError('Invalid response'); - } catch (error) { - let errMsg = ''; - let errStatus = 400; - if (error.response && error.response.data) { - errMsg = JSON.stringify(error.response.data); - switch (error.response.status) { - case 422: - case 401: - case 406: - case 403: - errStatus = 400; - break; - case 500: - case 503: - errStatus = 500; - break; - default: - errStatus = 400; - } - } - throw new NetworkError(`Error occurred while checking user : ${errMsg}`, errStatus, { - [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(errStatus), - }); + params: paramsdata, + }, + { + destType: 'delighted', + feature: 'transformation', + requestMethod: 'GET', + endpointPath: '/people.json', + module: 'router', + }, + ); + + if (processedResponse.status === 200 && Array.isArray(processedResponse?.response)) { + return processedResponse.response.length > 0; } + + const errStatus = getErrorStatus(processedResponse.status); + throw new NetworkError( + `Error occurred while checking user: ${JSON.stringify(processedResponse?.response || 'Invalid response')}`, + errStatus, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(errStatus), + }, + processedResponse, + ); }; const eventValidity = (Config, message) => { const event = getValueFromMessage(message, 'event'); diff --git a/test/integrations/destinations/delighted/network.ts b/test/integrations/destinations/delighted/network.ts index d9896a25e8..1ccc785ea3 100644 --- a/test/integrations/destinations/delighted/network.ts +++ b/test/integrations/destinations/delighted/network.ts @@ -27,4 +27,18 @@ export const networkCallsData = [ status: 200, }, }, + { + httpReq: { + url: 'https://api.delighted.com/v1/people.json', + method: 'GET', + headers: { Authorization: `Basic ZHVtbXlBcGlLZXlmb3JmYWlsdXJl` }, + params: { + email: 'test@rudderlabs.com', + }, + }, + httpRes: { + status: 429, + data: {}, + }, + }, ]; diff --git a/test/integrations/destinations/delighted/processor/data.ts b/test/integrations/destinations/delighted/processor/data.ts index 7a5ad7de9d..f35c2d8ecb 100644 --- a/test/integrations/destinations/delighted/processor/data.ts +++ b/test/integrations/destinations/delighted/processor/data.ts @@ -944,4 +944,93 @@ export const data = [ }, }, }, + { + name: 'delighted', + description: 'Too many request test', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + apiKey: 'dummyApiKeyforfailure', + channel: 'email', + delay: 0, + eventNamesSettings: [ + { + event: 'Product Reviewed', + }, + ], + }, + }, + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + type: 'track', + userId: 'test@rudderlabs.com', + event: 'Product Reviewed', + properties: { + review_id: '12345', + product_id: '123', + rating: 3, + review_body: 'Average product, expected much more.', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '{"message":"Error occurred while checking user: {}","destinationResponse":{"response":{},"status":429}}', + statTags: { + destType: 'DELIGHTED', + errorCategory: 'network', + errorType: 'throttled', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 429, + }, + ], + }, + }, + }, ]; From 650ea9ca9ca6351aadc51bfb5038b8b2507990ec Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Wed, 24 Apr 2024 16:11:30 +0530 Subject: [PATCH 118/240] feat(iterable): component test refactor (#3250) --- .../iterable/processor/aliasTestData.ts | 97 + .../destinations/iterable/processor/data.ts | 3793 +---------------- .../iterable/processor/identifyTestData.ts | 407 ++ .../iterable/processor/pageScreenTestData.ts | 409 ++ .../iterable/processor/trackTestData.ts | 717 ++++ .../iterable/processor/validationTestData.ts | 258 ++ test/integrations/testUtils.ts | 1 + 7 files changed, 1900 insertions(+), 3782 deletions(-) create mode 100644 test/integrations/destinations/iterable/processor/aliasTestData.ts create mode 100644 test/integrations/destinations/iterable/processor/identifyTestData.ts create mode 100644 test/integrations/destinations/iterable/processor/pageScreenTestData.ts create mode 100644 test/integrations/destinations/iterable/processor/trackTestData.ts create mode 100644 test/integrations/destinations/iterable/processor/validationTestData.ts diff --git a/test/integrations/destinations/iterable/processor/aliasTestData.ts b/test/integrations/destinations/iterable/processor/aliasTestData.ts new file mode 100644 index 0000000000..cac43767bb --- /dev/null +++ b/test/integrations/destinations/iterable/processor/aliasTestData.ts @@ -0,0 +1,97 @@ +import { generateMetadata, transformResultBuilder } from './../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const destination: Destination = { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + apiKey: 'testApiKey', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, +}; + +const headers = { + api_key: 'testApiKey', + 'Content-Type': 'application/json', +}; + +const properties = { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', +}; + +const sentAt = '2020-08-28T16:26:16.473Z'; +const originalTimestamp = '2020-08-28T16:26:06.468Z'; + +export const aliasTestData: ProcessorTestData[] = [ + { + id: 'iterable-alias-test-1', + name: 'iterable', + description: 'Alias call with userId and previousId', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain update email payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + anonymousId: 'anonId', + userId: 'new@email.com', + previousId: 'old@email.com', + name: 'ApplicationLoaded', + context: {}, + properties, + type: 'alias', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: 'https://api.iterable.com/api/users/updateEmail', + JSON: { + currentEmail: 'old@email.com', + newEmail: 'new@email.com', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/iterable/processor/data.ts b/test/integrations/destinations/iterable/processor/data.ts index 19b370b513..12e5738641 100644 --- a/test/integrations/destinations/iterable/processor/data.ts +++ b/test/integrations/destinations/iterable/processor/data.ts @@ -1,3784 +1,13 @@ +import { identifyTestData } from './identifyTestData'; +import { trackTestData } from './trackTestData'; +import { pageScreenTestData } from './pageScreenTestData'; +import { aliasTestData } from './aliasTestData'; +import { validationTestData } from './validationTestData'; + export const data = [ - { - name: 'iterable', - description: 'Test 0', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'page', - sentAt: '2020-08-28T16:26:16.473Z', - context: { - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-6f62b91e789a636929ca38aed01c5f6e-103c720d-81bd-4742-98d6-d45a65aed23e', - properties: { - url: 'https://dominos.com', - title: 'Pizza', - referrer: 'https://google.com', - }, - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Invalid page call', - statTags: { - destType: 'ITERABLE', - errorCategory: 'dataValidation', - errorType: 'configuration', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 1', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'identify', - sentAt: '2020-08-28T16:26:06.466Z', - traits: { - city: 'Bangalore', - name: 'manashi', - email: 'manashi@website.com', - country: 'India', - }, - context: { - traits: { - city: 'Bangalore', - name: 'manashi', - email: 'manashi@website.com', - country: 'India', - preferUserId: false, - }, - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-cc3ef811f686139ee527b806ee0129ef-163a3a88-266f-447e-8cce-34a8f42f8dcd', - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.462Z', - }, - destination: { - Config: { - preferUserId: false, - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - JSON: { - email: 'manashi@website.com', - userId: 'abcdeeeeeeeexxxx102', - dataFields: { - city: 'Bangalore', - name: 'manashi', - email: 'manashi@website.com', - country: 'India', - }, - preferUserId: false, - mergeNestedObjects: true, - }, - }, - type: 'REST', - files: {}, - method: 'POST', - params: {}, - headers: { - api_key: '62d12498c37c4fd8a1a546c2d35c2f60', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://api.iterable.com/api/users/update', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 2', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'track', - event: 'Email Opened', - sentAt: '2020-08-28T16:26:16.473Z', - context: { - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-570110489d3e99b234b18af9a9eca9d4-6009779e-82d7-469d-aaeb-5ccf162b0453', - properties: { - subject: 'resume validate', - sendtime: '2020-01-01', - sendlocation: 'akashdeep@gmail.com', - }, - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - JSON: { - userId: 'abcdeeeeeeeexxxx102', - createdAt: 1598631966468, - eventName: 'Email Opened', - dataFields: { - subject: 'resume validate', - sendtime: '2020-01-01', - sendlocation: 'akashdeep@gmail.com', - }, - }, - }, - type: 'REST', - files: {}, - method: 'POST', - params: {}, - headers: { - api_key: '62d12498c37c4fd8a1a546c2d35c2f60', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://api.iterable.com/api/events/track', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 3', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - }, - userId: '12345', - eventName: 'ApplicationLoaded page', - createdAt: 1571051718299, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 4', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - campaignId: '123456', - templateId: '1213458', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: true, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - campaignId: '123456', - templateId: '1213458', - }, - userId: '12345', - eventName: 'Loaded a Page', - createdAt: 1571051718299, - campaignId: 123456, - templateId: 1213458, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 5', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - name: 'test-name', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: false, - trackNamedPages: true, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - name: 'test-name', - }, - userId: '12345', - eventName: 'ApplicationLoaded page', - createdAt: 1571051718299, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 6', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - }, - userId: '12345', - eventName: 'ApplicationLoaded page', - createdAt: 1571051718299, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 7', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'screen', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - }, - userId: '12345', - eventName: 'ApplicationLoaded screen', - createdAt: 1571051718299, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 8', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'screen', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - campaignId: '123456', - templateId: '1213458', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: true, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - campaignId: '123456', - templateId: '1213458', - }, - userId: '12345', - eventName: 'Loaded a Screen', - createdAt: 1571051718299, - campaignId: 123456, - templateId: 1213458, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 9', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'screen', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - name: 'test-name', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: false, - trackNamedPages: true, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - name: 'test-name', - }, - userId: '12345', - eventName: 'ApplicationLoaded screen', - createdAt: 1571051718299, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 10', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'screen', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - }, - userId: '12345', - eventName: 'ApplicationLoaded screen', - createdAt: 1571051718299, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 11', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'ruchira@rudderlabs.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - id: '72e528f869711c3d', - manufacturer: 'Google', - model: 'sdk_gphone_x86', - name: 'generic_x86_arm', - token: 'some_device_token', - type: 'android', - }, - screen: { - density: 2, - }, - }, - type: 'group', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2019-10-14T09:03:17.562Z', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Message type group not supported', - statTags: { - destType: 'ITERABLE', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 12', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'ruchira@rudderlabs.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - id: '72e528f869711c3d', - manufacturer: 'Google', - model: 'sdk_gphone_x86', - name: 'generic_x86_arm', - token: 'some_device_token', - type: 'android', - }, - screen: { - density: 2, - }, - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2019-10-14T09:03:17.562Z', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - mergeNestedObjects: false, - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/users/update', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'ruchira@rudderlabs.com', - dataFields: { - email: 'ruchira@rudderlabs.com', - }, - userId: '123456', - preferUserId: true, - mergeNestedObjects: false, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 13', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - token: 'sample_push_token', - name: 'sample_device_name', - model: 'sample_device_model', - manufacturer: 'sample_device_manufacturer', - type: 'ios', - }, - traits: { - email: 'ruchira@rudderlabs.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - id: '72e528f869711c3d', - manufacturer: 'Google', - model: 'sdk_gphone_x86', - name: 'generic_x86_arm', - token: 'some_device_token', - type: 'android', - }, - screen: { - density: 2, - }, - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2019-10-14T09:03:17.562Z', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/users/update', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'ruchira@rudderlabs.com', - dataFields: { - email: 'ruchira@rudderlabs.com', - }, - userId: '123456', - preferUserId: true, - mergeNestedObjects: true, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 14', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - token: 'sample_push_token', - name: 'sample_device_name', - model: 'sample_device_model', - manufacturer: 'sample_device_manufacturer', - type: 'android', - }, - traits: { - email: 'ruchira@rudderlabs.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - id: '72e528f869711c3d', - manufacturer: 'Google', - model: 'sdk_gphone_x86', - name: 'generic_x86_arm', - token: 'some_device_token', - type: 'android', - }, - screen: { - density: 2, - }, - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2019-10-14T09:03:17.562Z', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/users/update', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'ruchira@rudderlabs.com', - dataFields: { - email: 'ruchira@rudderlabs.com', - }, - userId: '123456', - preferUserId: true, - mergeNestedObjects: true, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 15', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - event: 'product added', - type: 'track', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - campaignId: '1', - templateId: '0', - orderId: 10000, - total: 1000, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - price: '19', - position: '1', - category: 'cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - quantity: '2', - }, - { - product_id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - price: '192', - quantity: 22, - position: '12', - category: 'Cars2', - url: 'https://www.example.com/product/path2', - image_url: 'https://www.example.com/product/path.jpg2', - }, - ], - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/commerce/updateCart', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - user: { - email: 'sayan@gmail.com', - dataFields: { - email: 'sayan@gmail.com', - }, - userId: '12345', - preferUserId: true, - mergeNestedObjects: true, - }, - items: [ - { - id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - categories: ['cars'], - price: 19, - quantity: 2, - imageUrl: 'https://www.example.com/product/path.jpg', - url: 'https://www.example.com/product/path', - }, - { - id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - categories: ['Cars2'], - price: 192, - quantity: 22, - imageUrl: 'https://www.example.com/product/path.jpg2', - url: 'https://www.example.com/product/path2', - }, - ], - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 16', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - event: 'order completed', - type: 'track', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - orderId: 10000, - total: '1000', - campaignId: '123456', - templateId: '1213458', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - price: '19', - position: '1', - category: 'Cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - quantity: 2, - }, - { - product_id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - price: '192', - quantity: '22', - position: '12', - category: 'Cars2', - url: 'https://www.example.com/product/path2', - image_url: 'https://www.example.com/product/path.jpg2', - }, - ], - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/commerce/trackPurchase', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - dataFields: { - orderId: 10000, - total: '1000', - campaignId: '123456', - templateId: '1213458', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - price: '19', - position: '1', - category: 'Cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - quantity: 2, - }, - { - product_id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - price: '192', - quantity: '22', - position: '12', - category: 'Cars2', - url: 'https://www.example.com/product/path2', - image_url: 'https://www.example.com/product/path.jpg2', - }, - ], - }, - id: '10000', - createdAt: 1571051718299, - campaignId: 123456, - templateId: 1213458, - total: 1000, - user: { - email: 'sayan@gmail.com', - dataFields: { - email: 'sayan@gmail.com', - }, - userId: '12345', - preferUserId: true, - mergeNestedObjects: true, - }, - items: [ - { - id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - categories: ['Cars'], - price: 19, - quantity: 2, - imageUrl: 'https://www.example.com/product/path.jpg', - url: 'https://www.example.com/product/path', - }, - { - id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - categories: ['Cars2'], - price: 192, - quantity: 22, - imageUrl: 'https://www.example.com/product/path.jpg2', - url: 'https://www.example.com/product/path2', - }, - ], - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 17', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2019-10-14T11:15:18.300Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'test track event GA3', - properties: { - email: 'ruchira@rudderlabs.com', - campaignId: '1', - templateId: '0', - category: 'test-category', - user_actual_role: 'system_admin, system_user', - user_actual_id: 12345, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'ruchira@rudderlabs.com', - dataFields: { - campaignId: '1', - templateId: '0', - category: 'test-category', - user_actual_role: 'system_admin, system_user', - user_actual_id: 12345, - email: 'ruchira@rudderlabs.com', - }, - userId: '12345', - eventName: 'test track event GA3', - createdAt: 1571051718300, - campaignId: 1, - templateId: 0, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 18', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - id: '72e528f869711c3d', - manufacturer: 'Google', - model: 'sdk_gphone_x86', - name: 'generic_x86_arm', - token: 'some_device_token', - type: 'android', - }, - screen: { - density: 2, - }, - }, - traits: { - email: 'ruchira@rudderlabs.com', - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2019-10-14T09:03:17.562Z', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/users/update', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'ruchira@rudderlabs.com', - dataFields: { - email: 'ruchira@rudderlabs.com', - }, - userId: '123456', - preferUserId: true, - mergeNestedObjects: true, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 19', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - id: '72e528f869711c3d', - manufacturer: 'Google', - model: 'sdk_gphone_x86', - name: 'generic_x86_arm', - token: 'some_device_token', - type: 'android', - }, - screen: { - density: 2, - }, - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2019-10-14T09:03:17.562Z', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'userId or email is mandatory for this request', - statTags: { - destType: 'ITERABLE', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 20', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'page', - sentAt: '2020-08-28T16:26:16.473Z', - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-6f62b91e789a636929ca38aed01c5f6e-103c720d-81bd-4742-98d6-d45a65aed23e', - properties: { - url: 'https://dominos.com', - title: 'Pizza', - referrer: 'https://google.com', - }, - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Invalid page call', - statTags: { - destType: 'ITERABLE', - errorCategory: 'dataValidation', - errorType: 'configuration', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 21', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'track', - event: 'Product Added', - sentAt: '2021-07-09T05:27:17.908Z', - userId: '8751', - channel: 'web', - context: { - os: { - name: '', - version: '', - }, - app: { - name: 'RudderLabs JavaScript SDK', - build: '1.0.0', - version: '1.0.16', - namespace: 'com.rudderlabs.javascript', - }, - page: { - url: 'https://joybird.com/cabinets/vira-console-cabinet/', - path: '/cabinets/vira-console-cabinet/', - title: 'Vira Console Cabinet | Joybird', - search: '', - referrer: '$direct', - referring_domain: '', - }, - locale: 'en-us', - screen: { - density: 2, - }, - traits: { - email: 'jessica@jlpdesign.net', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.16', - }, - campaign: {}, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15', - }, - rudderId: '1c42e104-97ec-4f54-a328-2379623583fe', - messageId: 'e58f6624-a1c3-48f4-a6af-610389602304', - timestamp: '2021-07-09T05:27:18.131Z', - properties: { - sku: 'JB24691400-W05', - name: 'Vira Console Cabinet', - price: 797, - cart_id: 'bd9b8dbf4ef8ee01d4206b04fe2ee6ae', - variant: 'Oak', - quantity: 1, - quickship: true, - full_price: 1328, - product_id: 10606, - non_interaction: 1, - }, - receivedAt: '2021-07-09T05:27:18.131Z', - request_ip: '162.224.233.114', - anonymousId: '8a7ff986-62d8-45ca-9a16-8895b3f9d341', - integrations: { - All: true, - }, - originalTimestamp: '2021-07-09T05:27:17.908Z', - }, - destination: { - Config: { - credentials: 'abc', - eventToTopicMap: [ - { - from: 'track', - to: 'projects/big-query-integration-poc/topics/test', - }, - ], - }, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/commerce/updateCart', - headers: { - 'Content-Type': 'application/json', - }, - params: {}, - body: { - JSON: { - user: { - email: 'jessica@jlpdesign.net', - dataFields: { - email: 'jessica@jlpdesign.net', - }, - userId: '8751', - preferUserId: true, - mergeNestedObjects: true, - }, - items: [ - { - id: 10606, - sku: 'JB24691400-W05', - name: 'Vira Console Cabinet', - price: 797, - quantity: 1, - }, - ], - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 22', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'sources', - context: { - externalId: [ - { - id: 'lynnanderson@smith.net', - identifierType: 'email', - type: 'ITERABLE-users', - }, - ], - mappedToDestination: 'true', - sources: { - batch_id: 'f5f240d0-0acb-46e0-b043-57fb0aabbadd', - job_id: '1zAj94bEy8komdqnYtSoDp0VmGs/Syncher', - job_run_id: 'c5tar6cqgmgmcjvupdhg', - task_id: 'tt_10_rows_check', - task_run_id: 'c5tar6cqgmgmcjvupdi0', - version: 'release.v1.6.8', - }, - }, - messageId: '2f052f7c-f694-4849-a7ed-a432f7ffa0a4', - originalTimestamp: '2021-10-28T14:03:50.503Z', - receivedAt: '2021-10-28T14:03:46.567Z', - recordId: '8', - request_ip: '10.1.94.92', - rudderId: 'c0f6843e-e3d6-4946-9752-fa339fbadef2', - sentAt: '2021-10-28T14:03:50.503Z', - timestamp: '2021-10-28T14:03:46.566Z', - traits: { - administrative_unit: 'Minnesota', - am_pm: 'AM', - boolean: true, - firstname: 'Jacqueline', - pPower: 'AM', - userId: 'Jacqueline', - }, - type: 'identify', - userId: 'lynnanderson@smith.net', - }, - destination: { - ID: '1zia9wKshXt80YksLmUdJnr7IHI', - Name: 'test_iterable', - DestinationDefinition: { - ID: '1iVQvTRMsPPyJzwol0ifH93QTQ6', - Name: 'ITERABLE', - DisplayName: 'Iterable', - Config: { - destConfig: { - defaultConfig: [ - 'apiKey', - 'mapToSingleEvent', - 'trackAllPages', - 'trackCategorisedPages', - 'trackNamedPages', - ], - }, - excludeKeys: [], - includeKeys: [], - saveDestinationResponse: true, - secretKeys: [], - supportedMessageTypes: ['identify', 'page', 'screen', 'track'], - supportedSourceTypes: [ - 'android', - 'ios', - 'web', - 'unity', - 'amp', - 'cloud', - 'warehouse', - 'reactnative', - 'flutter', - 'cordova', - ], - supportsVisualMapper: true, - transformAt: 'processor', - transformAtV1: 'processor', - }, - ResponseRules: null, - }, - Config: { - apiKey: '12345', - mapToSingleEvent: true, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: true, - }, - Enabled: true, - Transformations: [], - IsProcessorEnabled: true, - }, - libraries: [], - request: { - query: {}, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/users/update', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'lynnanderson@smith.net', - dataFields: { - administrative_unit: 'Minnesota', - am_pm: 'AM', - boolean: true, - firstname: 'Jacqueline', - pPower: 'AM', - userId: 'Jacqueline', - email: 'lynnanderson@smith.net', - }, - userId: 'lynnanderson@smith.net', - preferUserId: true, - mergeNestedObjects: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 23', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'sources', - context: { - externalId: [ - { - id: 'Matthew', - identifierType: 'userId', - type: 'ITERABLE-users', - }, - ], - mappedToDestination: 'true', - sources: { - batch_id: '230d7c79-a2c2-4b2a-90bb-06ba988d3bb4', - job_id: '1zjj9aF5UkmavBi4HtM3kWOGvy0/Syncher', - job_run_id: 'c5tb4gsqgmgmcjvuplhg', - task_id: 'tt_10_rows', - task_run_id: 'c5tb4gsqgmgmcjvupli0', - version: 'release.v1.6.8', - }, - }, - messageId: 'c4c97310-463b-4300-9215-5cfddcb2a769', - originalTimestamp: '2021-10-28T14:23:43.254Z', - receivedAt: '2021-10-28T14:23:38.300Z', - recordId: '3', - request_ip: '10.1.94.92', - rudderId: '7300f5e3-bdb5-489e-ac7e-47876e487de9', - sentAt: '2021-10-28T14:23:43.254Z', - timestamp: '2021-10-28T14:23:38.299Z', - traits: { - price: 'GB', - }, - type: 'identify', - userId: 'Matthew', - }, - destination: { - ID: '1zjjHN4RQ6t4DPj3HVpp0b6XW4A', - Name: 'test_userId_uniq', - DestinationDefinition: { - ID: '1iVQvTRMsPPyJzwol0ifH93QTQ6', - Name: 'ITERABLE', - DisplayName: 'Iterable', - Config: { - destConfig: { - defaultConfig: [ - 'apiKey', - 'mapToSingleEvent', - 'trackAllPages', - 'trackCategorisedPages', - 'trackNamedPages', - ], - }, - excludeKeys: [], - includeKeys: [], - saveDestinationResponse: true, - secretKeys: [], - supportedMessageTypes: ['identify', 'page', 'screen', 'track'], - supportedSourceTypes: [ - 'android', - 'ios', - 'web', - 'unity', - 'amp', - 'cloud', - 'warehouse', - 'reactnative', - 'flutter', - 'cordova', - ], - supportsVisualMapper: true, - transformAt: 'processor', - transformAtV1: 'processor', - }, - ResponseRules: null, - }, - Config: { - apiKey: '12345', - mapToSingleEvent: true, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: true, - }, - Enabled: true, - Transformations: [], - IsProcessorEnabled: true, - }, - libraries: [], - request: { - query: {}, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/users/update', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - dataFields: { - price: 'GB', - userId: 'Matthew', - }, - userId: 'Matthew', - preferUserId: true, - mergeNestedObjects: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 24', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - token: 'sample_push_token', - name: 'sample_device_name', - model: 'sample_device_model', - manufacturer: 'sample_device_manufacturer', - type: 'watchos', - }, - traits: { - email: 'ruchira@rudderlabs.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - screen: { - density: 2, - }, - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2019-10-14T09:03:17.562Z', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/users/update', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'ruchira@rudderlabs.com', - dataFields: { - email: 'ruchira@rudderlabs.com', - }, - userId: '123456', - preferUserId: true, - mergeNestedObjects: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 25', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'alias', - sentAt: '2020-08-28T16:26:16.473Z', - context: { - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-6f62b91e789a636929ca38aed01c5f6e-103c720d-81bd-4742-98d6-d45a65aed23e', - properties: { - url: 'https://dominos.com', - title: 'Pizza', - referrer: 'https://google.com', - }, - userId: 'new@email.com', - previousId: 'old@email.com', - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - JSON: { - currentEmail: 'old@email.com', - newEmail: 'new@email.com', - }, - }, - type: 'REST', - files: {}, - method: 'POST', - params: {}, - headers: { - api_key: '62d12498c37c4fd8a1a546c2d35c2f60', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://api.iterable.com/api/users/updateEmail', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 26', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'alias', - sentAt: '2020-08-28T16:26:16.473Z', - context: { - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-6f62b91e789a636929ca38aed01c5f6e-103c720d-81bd-4742-98d6-d45a65aed23e', - properties: { - url: 'https://dominos.com', - title: 'Pizza', - referrer: 'https://google.com', - }, - userId: 'new@email.com', - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Missing required value from "previousId"', - statTags: { - destType: 'ITERABLE', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 27', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'alias', - sentAt: '2020-08-28T16:26:16.473Z', - context: { - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-6f62b91e789a636929ca38aed01c5f6e-103c720d-81bd-4742-98d6-d45a65aed23e', - properties: { - url: 'https://dominos.com', - title: 'Pizza', - referrer: 'https://google.com', - }, - previousId: 'old@email.com', - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Missing required value from "userId"', - statTags: { - destType: 'ITERABLE', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 28', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'john@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - event: 'product added', - type: 'track', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - campaignId: '1', - templateId: '0', - orderId: 10000, - total: 1000, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - price: '19', - position: '1', - category: ['bikes', 'cars', 'motors'], - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - quantity: '2', - }, - { - product_id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - price: '192', - quantity: 22, - position: '12', - category: ['Bikes2', 'cars2', 'motors2'], - url: 'https://www.example.com/product/path2', - image_url: 'https://www.example.com/product/path.jpg2', - }, - ], - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/commerce/updateCart', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - user: { - email: 'john@gmail.com', - dataFields: { - email: 'john@gmail.com', - }, - userId: '12345', - preferUserId: true, - mergeNestedObjects: true, - }, - items: [ - { - id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - categories: ['bikes', 'cars', 'motors'], - price: 19, - quantity: 2, - imageUrl: 'https://www.example.com/product/path.jpg', - url: 'https://www.example.com/product/path', - }, - { - id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - categories: ['Bikes2', 'cars2', 'motors2'], - price: 192, - quantity: 22, - imageUrl: 'https://www.example.com/product/path.jpg2', - url: 'https://www.example.com/product/path2', - }, - ], - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 29', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - event: 'product added', - type: 'track', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - campaignId: '1', - templateId: '0', - orderId: 10000, - total: 1000, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - price: '19', - position: '1', - category: 'shirts,pants,trousers', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - quantity: '2', - }, - { - product_id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - price: '192', - quantity: 22, - position: '12', - category: 'Cars2', - url: 'https://www.example.com/product/path2', - image_url: 'https://www.example.com/product/path.jpg2', - }, - ], - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/commerce/updateCart', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - user: { - email: 'sayan@gmail.com', - dataFields: { - email: 'sayan@gmail.com', - }, - userId: '12345', - preferUserId: true, - mergeNestedObjects: true, - }, - items: [ - { - id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - categories: ['shirts', 'pants', 'trousers'], - price: 19, - quantity: 2, - imageUrl: 'https://www.example.com/product/path.jpg', - url: 'https://www.example.com/product/path', - }, - { - id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - categories: ['Cars2'], - price: 192, - quantity: 22, - imageUrl: 'https://www.example.com/product/path.jpg2', - url: 'https://www.example.com/product/path2', - }, - ], - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 30', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - event: 'product added', - type: 'track', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - campaignId: '1', - templateId: '0', - orderId: 10000, - total: 1000, - product_id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - price: '192', - quantity: 22, - position: '12', - category: 'Cars2', - url: 'https://www.example.com/product/path2', - image_url: 'https://www.example.com/product/path.jpg2', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/commerce/updateCart', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - user: { - email: 'sayan@gmail.com', - dataFields: { - email: 'sayan@gmail.com', - }, - userId: '12345', - preferUserId: true, - mergeNestedObjects: true, - }, - items: [ - { - id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - categories: ['Cars2'], - price: 192, - quantity: 22, - imageUrl: 'https://www.example.com/product/path.jpg2', - url: 'https://www.example.com/product/path2', - }, - ], - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 31', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - passcode: - 'fbee74a147828e2932c701d19dc1f2dcfa4ac0048be3aa3a88d427090a59dc1c0fa002f1', - accountId: '476550467', - trackAnonymous: true, - enableObjectIdMapping: false, - }, - }, - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - event: 'order completed', - type: 'track', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - product_id: 1234, - name: 'Shoes', - price: 45, - quantity: 1, - orderId: 10000, - total: '1000', - campaignId: '123456', - templateId: '1213458', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/commerce/trackPurchase', - headers: { - 'Content-Type': 'application/json', - }, - params: {}, - body: { - JSON: { - dataFields: { - product_id: 1234, - name: 'Shoes', - price: 45, - quantity: 1, - orderId: 10000, - total: '1000', - campaignId: '123456', - templateId: '1213458', - }, - id: '10000', - createdAt: 1571051718299, - campaignId: 123456, - templateId: 1213458, - total: 1000, - user: { - email: 'sayan@gmail.com', - dataFields: { - email: 'sayan@gmail.com', - }, - userId: '12345', - preferUserId: true, - mergeNestedObjects: true, - }, - items: [ - { - id: 1234, - name: 'Shoes', - price: 45, - quantity: 1, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 32', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'track', - sentAt: '2020-08-28T16:26:16.473Z', - context: { - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-570110489d3e99b234b18af9a9eca9d4-6009779e-82d7-469d-aaeb-5ccf162b0453', - properties: { - subject: 'resume validate', - sendtime: '2020-01-01', - sendlocation: 'akashdeep@gmail.com', - }, - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - JSON: { - userId: 'abcdeeeeeeeexxxx102', - createdAt: 1598631966468, - dataFields: { - subject: 'resume validate', - sendtime: '2020-01-01', - sendlocation: 'akashdeep@gmail.com', - }, - }, - }, - type: 'REST', - files: {}, - method: 'POST', - params: {}, - headers: { - api_key: '62d12498c37c4fd8a1a546c2d35c2f60', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://api.iterable.com/api/events/track', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 33', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'track', - event: '', - sentAt: '2020-08-28T16:26:16.473Z', - context: { - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-570110489d3e99b234b18af9a9eca9d4-6009779e-82d7-469d-aaeb-5ccf162b0453', - properties: { - subject: 'resume validate', - sendtime: '2020-01-01', - sendlocation: 'akashdeep@gmail.com', - }, - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - JSON: { - userId: 'abcdeeeeeeeexxxx102', - createdAt: 1598631966468, - dataFields: { - subject: 'resume validate', - sendtime: '2020-01-01', - sendlocation: 'akashdeep@gmail.com', - }, - }, - }, - type: 'REST', - files: {}, - method: 'POST', - params: {}, - headers: { - api_key: '62d12498c37c4fd8a1a546c2d35c2f60', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://api.iterable.com/api/events/track', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, + ...identifyTestData, + ...trackTestData, + ...pageScreenTestData, + ...aliasTestData, + ...validationTestData, ]; diff --git a/test/integrations/destinations/iterable/processor/identifyTestData.ts b/test/integrations/destinations/iterable/processor/identifyTestData.ts new file mode 100644 index 0000000000..d05f87a11f --- /dev/null +++ b/test/integrations/destinations/iterable/processor/identifyTestData.ts @@ -0,0 +1,407 @@ +import { + generateMetadata, + transformResultBuilder, + generateIndentifyPayload, +} from './../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const destination: Destination = { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + apiKey: 'testApiKey', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, +}; + +const headers = { + api_key: 'testApiKey', + 'Content-Type': 'application/json', +}; + +const user1Traits = { + name: 'manashi', + country: 'India', + city: 'Bangalore', + email: 'manashi@website.com', +}; + +const user2Traits = { + am_pm: 'AM', + pPower: 'AM', + boolean: true, + userId: 'Jacqueline', + firstname: 'Jacqueline', + administrative_unit: 'Minnesota', +}; + +const userId = 'userId'; +const anonymousId = 'anonId'; +const sentAt = '2020-08-28T16:26:16.473Z'; +const originalTimestamp = '2020-08-28T16:26:06.468Z'; + +const updateUserEndpoint = 'https://api.iterable.com/api/users/update'; + +export const identifyTestData: ProcessorTestData[] = [ + { + id: 'iterable-identify-test-1', + name: 'iterable', + description: 'Indentify call to update user in iterable with user traits', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain update user payload with all user traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + anonymousId, + context: { + traits: user1Traits, + }, + traits: user1Traits, + type: 'identify', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateUserEndpoint, + JSON: { + email: user1Traits.email, + userId: anonymousId, + dataFields: user1Traits, + preferUserId: false, + mergeNestedObjects: true, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-identify-test-2', + name: 'iterable', + description: 'Indentify call to update user email', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain update user payload with new email sent in payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateIndentifyPayload({ + userId, + anonymousId, + context: { + traits: { email: 'ruchira@rudderlabs.com' }, + }, + type: 'identify', + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateUserEndpoint, + JSON: { + email: 'ruchira@rudderlabs.com', + userId, + dataFields: { + email: 'ruchira@rudderlabs.com', + }, + preferUserId: false, + mergeNestedObjects: true, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-identify-test-3', + name: 'iterable', + description: 'Indentify call to update user email with preferUserId config set to true', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain update user payload with new email sent in payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...destination, Config: { ...destination.Config, preferUserId: true } }, + message: generateIndentifyPayload({ + userId, + anonymousId, + context: { + traits: { email: 'ruchira@rudderlabs.com' }, + }, + type: 'identify', + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateUserEndpoint, + JSON: { + email: 'ruchira@rudderlabs.com', + userId, + dataFields: { + email: 'ruchira@rudderlabs.com', + }, + preferUserId: true, + mergeNestedObjects: true, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-identify-test-4', + name: 'iterable', + description: + 'Indentify call to update user email with traits present at root instead of context.traits', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain update user payload with new email sent in payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...destination, Config: { ...destination.Config, preferUserId: true } }, + message: generateIndentifyPayload({ + userId, + anonymousId, + context: { + traits: {}, + }, + traits: { email: 'ruchira@rudderlabs.com' }, + type: 'identify', + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateUserEndpoint, + JSON: { + email: 'ruchira@rudderlabs.com', + userId, + dataFields: { + email: 'ruchira@rudderlabs.com', + }, + preferUserId: true, + mergeNestedObjects: true, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-identify-test-5', + name: 'iterable', + description: 'Iterable rEtl test to update user', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain update user payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...destination, Config: { ...destination.Config, preferUserId: true } }, + message: { + userId, + anonymousId, + context: { + externalId: [ + { + id: 'lynnanderson@smith.net', + identifierType: 'email', + type: 'ITERABLE-users', + }, + ], + mappedToDestination: 'true', + }, + traits: user2Traits, + type: 'identify', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateUserEndpoint, + JSON: { + email: 'lynnanderson@smith.net', + userId, + dataFields: { ...user2Traits, email: 'lynnanderson@smith.net' }, + preferUserId: true, + mergeNestedObjects: true, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-identify-test-6', + name: 'iterable', + description: 'Iterable rEtl test to update user traits', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain update user payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + userId: 'Matthew', + anonymousId, + context: { + externalId: [ + { + id: 'Matthew', + identifierType: 'userId', + type: 'ITERABLE-users', + }, + ], + mappedToDestination: 'true', + }, + traits: user2Traits, + type: 'identify', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateUserEndpoint, + JSON: { + userId: 'Matthew', + dataFields: { ...user2Traits, userId: 'Matthew' }, + preferUserId: false, + mergeNestedObjects: true, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/iterable/processor/pageScreenTestData.ts b/test/integrations/destinations/iterable/processor/pageScreenTestData.ts new file mode 100644 index 0000000000..074d6b56df --- /dev/null +++ b/test/integrations/destinations/iterable/processor/pageScreenTestData.ts @@ -0,0 +1,409 @@ +import { generateMetadata, transformResultBuilder } from './../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const destination: Destination = { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + apiKey: 'testApiKey', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, +}; + +const headers = { + api_key: 'testApiKey', + 'Content-Type': 'application/json', +}; + +const properties = { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', +}; + +const anonymousId = 'anonId'; +const sentAt = '2020-08-28T16:26:16.473Z'; +const originalTimestamp = '2020-08-28T16:26:06.468Z'; + +const pageEndpoint = 'https://api.iterable.com/api/events/track'; + +export const pageScreenTestData: ProcessorTestData[] = [ + { + id: 'iterable-page-test-1', + name: 'iterable', + description: 'Page call with name and properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain page name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + anonymousId, + name: 'ApplicationLoaded', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties, + type: 'page', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: pageEndpoint, + JSON: { + userId: anonymousId, + dataFields: properties, + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'ApplicationLoaded page', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-page-test-2', + name: 'iterable', + description: 'Page call with name and properties and mapToSingleEvent config set to true', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain page name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, mapToSingleEvent: true }, + }, + message: { + anonymousId, + name: 'ApplicationLoaded', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties: { ...properties, campaignId: '123456', templateId: '1213458' }, + type: 'page', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: pageEndpoint, + JSON: { + campaignId: 123456, + templateId: 1213458, + userId: anonymousId, + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'Loaded a Page', + dataFields: { ...properties, campaignId: '123456', templateId: '1213458' }, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-page-test-3', + name: 'iterable', + description: 'Page call with name and properties and trackNamedPages config set to true', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain page name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, trackNamedPages: true, trackAllPages: false }, + }, + message: { + anonymousId, + name: 'ApplicationLoaded', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties, + type: 'page', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: pageEndpoint, + JSON: { + userId: anonymousId, + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'ApplicationLoaded page', + dataFields: properties, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-screen-test-1', + name: 'iterable', + description: 'Screen call with name and properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain screen name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, trackCategorisedPages: true, trackAllPages: false }, + }, + message: { + anonymousId, + name: 'ApplicationLoaded', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties, + type: 'screen', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: pageEndpoint, + JSON: { + userId: anonymousId, + dataFields: properties, + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'ApplicationLoaded screen', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-screen-test-2', + name: 'iterable', + description: 'Screen call with name and properties and mapToSingleEvent config set to true', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain screen name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, mapToSingleEvent: true }, + }, + message: { + anonymousId, + name: 'ApplicationLoaded', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties: { ...properties, campaignId: '123456', templateId: '1213458' }, + type: 'screen', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: pageEndpoint, + JSON: { + campaignId: 123456, + templateId: 1213458, + userId: anonymousId, + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'Loaded a Screen', + dataFields: { ...properties, campaignId: '123456', templateId: '1213458' }, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-screen-test-3', + name: 'iterable', + description: 'Page call with name and properties and trackNamedPages config set to true', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain page name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, trackNamedPages: true, trackAllPages: false }, + }, + message: { + anonymousId, + name: 'ApplicationLoaded', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties, + type: 'screen', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: pageEndpoint, + JSON: { + userId: anonymousId, + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'ApplicationLoaded screen', + dataFields: properties, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/iterable/processor/trackTestData.ts b/test/integrations/destinations/iterable/processor/trackTestData.ts new file mode 100644 index 0000000000..296275ad77 --- /dev/null +++ b/test/integrations/destinations/iterable/processor/trackTestData.ts @@ -0,0 +1,717 @@ +import { + generateMetadata, + generateTrackPayload, + transformResultBuilder, +} from './../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const destination: Destination = { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + apiKey: 'testApiKey', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, +}; + +const headers = { + api_key: 'testApiKey', + 'Content-Type': 'application/json', +}; + +const properties = { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', +}; + +const customEventProperties = { + campaignId: '1', + templateId: '0', + user_actual_id: 12345, + category: 'test-category', + email: 'ruchira@rudderlabs.com', + user_actual_role: 'system_admin, system_user', +}; + +const productInfo = { + price: 797, + variant: 'Oak', + quantity: 1, + quickship: true, + full_price: 1328, + product_id: 10606, + non_interaction: 1, + sku: 'JB24691400-W05', + name: 'Vira Console Cabinet', + cart_id: 'bd9b8dbf4ef8ee01d4206b04fe2ee6ae', +}; + +const orderCompletedProductInfo = { + price: 45, + quantity: 1, + total: '1000', + name: 'Shoes', + orderId: 10000, + product_id: 1234, + campaignId: '123456', + templateId: '1213458', +}; + +const products = [ + { + product_id: '507f1f77bcf86cd799439011', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + price: '19', + position: '1', + category: 'cars', + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg', + quantity: '2', + }, + { + product_id: '507f1f77bcf86cd7994390112', + sku: '45790-322', + name: 'Monopoly: 3rd Edition2', + price: '192', + quantity: 22, + position: '12', + category: 'Cars2', + url: 'https://www.example.com/product/path2', + image_url: 'https://www.example.com/product/path.jpg2', + }, +]; + +const items = [ + { + id: '507f1f77bcf86cd799439011', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + categories: ['cars'], + price: 19, + quantity: 2, + imageUrl: 'https://www.example.com/product/path.jpg', + url: 'https://www.example.com/product/path', + }, + { + id: '507f1f77bcf86cd7994390112', + sku: '45790-322', + name: 'Monopoly: 3rd Edition2', + categories: ['Cars2'], + price: 192, + quantity: 22, + imageUrl: 'https://www.example.com/product/path.jpg2', + url: 'https://www.example.com/product/path2', + }, +]; + +const userId = 'userId'; +const anonymousId = 'anonId'; +const sentAt = '2020-08-28T16:26:16.473Z'; +const originalTimestamp = '2020-08-28T16:26:06.468Z'; + +const endpoint = 'https://api.iterable.com/api/events/track'; +const updateCartEndpoint = 'https://api.iterable.com/api/commerce/updateCart'; +const trackPurchaseEndpoint = 'https://api.iterable.com/api/commerce/trackPurchase'; + +export const trackTestData: ProcessorTestData[] = [ + { + id: 'iterable-track-test-1', + name: 'iterable', + description: 'Track call to add event with user', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event properties and event name', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + anonymousId, + event: 'Email Opened', + type: 'track', + context: {}, + properties, + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint, + JSON: { + userId: 'anonId', + createdAt: 1598631966468, + eventName: 'Email Opened', + dataFields: properties, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-2', + name: 'iterable', + description: 'Track call for product added event with all properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateTrackPayload({ + userId, + anonymousId, + event: 'product added', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties: { + campaignId: '1', + templateId: '0', + orderId: 10000, + total: 1000, + products, + }, + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateCartEndpoint, + JSON: { + user: { + email: 'sayan@gmail.com', + dataFields: { + email: 'sayan@gmail.com', + }, + userId, + preferUserId: false, + mergeNestedObjects: true, + }, + items, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-3', + name: 'iterable', + description: 'Track call for order completed event with all properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateTrackPayload({ + userId, + anonymousId, + event: 'order completed', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties: { + orderId: 10000, + total: '1000', + campaignId: '123456', + templateId: '1213458', + products, + }, + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: trackPurchaseEndpoint, + JSON: { + dataFields: { + orderId: 10000, + total: '1000', + campaignId: '123456', + templateId: '1213458', + products, + }, + id: '10000', + createdAt: 1598631966468, + campaignId: 123456, + templateId: 1213458, + total: 1000, + user: { + email: 'sayan@gmail.com', + dataFields: { + email: 'sayan@gmail.com', + }, + userId, + preferUserId: false, + mergeNestedObjects: true, + }, + items, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-4', + name: 'iterable', + description: 'Track call for custom event with all properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain custom event name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateTrackPayload({ + userId, + anonymousId, + event: 'test track event GA3', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties: customEventProperties, + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint, + JSON: { + email: 'ruchira@rudderlabs.com', + dataFields: customEventProperties, + userId, + eventName: 'test track event GA3', + createdAt: 1598631966468, + campaignId: 1, + templateId: 0, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-5', + name: 'iterable', + description: 'Track call for product added event with product info as properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateTrackPayload({ + userId, + anonymousId, + event: 'product added', + context: { + traits: { + email: 'jessica@jlpdesign.net', + }, + }, + properties: productInfo, + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateCartEndpoint, + JSON: { + user: { + email: 'jessica@jlpdesign.net', + dataFields: { + email: 'jessica@jlpdesign.net', + }, + userId, + preferUserId: false, + mergeNestedObjects: true, + }, + items: [ + { + id: productInfo.product_id, + sku: productInfo.sku, + name: productInfo.name, + price: productInfo.price, + quantity: productInfo.quantity, + }, + ], + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-6', + name: 'iterable', + description: 'Track call for product added event with product info as properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateTrackPayload({ + userId, + anonymousId, + event: 'product added', + context: { + traits: { + email: 'jessica@jlpdesign.net', + }, + }, + properties: { + campaignId: '1', + templateId: '0', + orderId: 10000, + total: 1000, + ...products[1], + }, + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateCartEndpoint, + JSON: { + user: { + email: 'jessica@jlpdesign.net', + dataFields: { + email: 'jessica@jlpdesign.net', + }, + userId, + preferUserId: false, + mergeNestedObjects: true, + }, + items: [ + { + price: 192, + url: products[1].url, + sku: products[1].sku, + name: products[1].name, + id: products[1].product_id, + quantity: products[1].quantity, + imageUrl: products[1].image_url, + categories: [products[1].category], + }, + ], + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-7', + name: 'iterable', + description: 'Track call for order completed event with product info as properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateTrackPayload({ + userId, + anonymousId, + event: 'order completed', + context: { + traits: { + email: 'jessica@jlpdesign.net', + }, + }, + properties: orderCompletedProductInfo, + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: trackPurchaseEndpoint, + JSON: { + dataFields: orderCompletedProductInfo, + user: { + email: 'jessica@jlpdesign.net', + dataFields: { + email: 'jessica@jlpdesign.net', + }, + userId, + preferUserId: false, + mergeNestedObjects: true, + }, + id: '10000', + total: 1000, + campaignId: 123456, + templateId: 1213458, + createdAt: 1598631966468, + items: [ + { + id: orderCompletedProductInfo.product_id, + name: orderCompletedProductInfo.name, + price: orderCompletedProductInfo.price, + quantity: orderCompletedProductInfo.quantity, + }, + ], + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-8', + name: 'iterable', + description: 'Track call without event name and userId', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + anonymousId, + type: 'track', + context: {}, + properties, + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint, + JSON: { + userId: anonymousId, + createdAt: 1598631966468, + dataFields: properties, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-8', + name: 'iterable', + description: 'Track call without event name', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + userId, + anonymousId, + type: 'track', + context: {}, + properties, + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint, + JSON: { + userId, + createdAt: 1598631966468, + dataFields: properties, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/iterable/processor/validationTestData.ts b/test/integrations/destinations/iterable/processor/validationTestData.ts new file mode 100644 index 0000000000..86728a868b --- /dev/null +++ b/test/integrations/destinations/iterable/processor/validationTestData.ts @@ -0,0 +1,258 @@ +import { generateMetadata } from './../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const destination: Destination = { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + apiKey: 'testApiKey', + mapToSingleEvent: false, + trackAllPages: false, + trackCategorisedPages: true, + trackNamedPages: false, + }, + Enabled: true, +}; + +const properties = { + url: 'https://dominos.com', + title: 'Pizza', + referrer: 'https://google.com', +}; + +const sentAt = '2020-08-28T16:26:16.473Z'; +const originalTimestamp = '2020-08-28T16:26:06.468Z'; + +const expectedStatTags = { + destType: 'ITERABLE', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +export const validationTestData: ProcessorTestData[] = [ + { + id: 'iterable-validation-test-1', + name: 'iterable', + description: "[Error]: Page call without it's required configuration", + scenario: 'Framework', + successCriteria: + 'Response should contain status code 400 and it should throw configuration error with respective message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + userId: 'sajal12', + anonymousId: 'abcdeeeeeeeexxxx102', + context: { + traits: { + email: 'abc@example.com', + }, + }, + properties, + type: 'page', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'Invalid page call', + statTags: { ...expectedStatTags, errorType: 'configuration' }, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-validation-test-2', + name: 'iterable', + description: '[Error]: Identify call without userId and email', + scenario: 'Framework', + successCriteria: + 'Response should contain status code 400 and it should throw instrumentation error with respective message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + context: {}, + type: 'identify', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'userId or email is mandatory for this request', + statTags: expectedStatTags, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-validation-test-3', + name: 'iterable', + description: '[Error]: Message type is not supported', + scenario: 'Framework', + successCriteria: + 'Response should contain status code 400 and it should throw instrumentation error with respective message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + context: {}, + type: 'group', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'Message type group not supported', + statTags: expectedStatTags, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-validation-test-4', + name: 'iterable', + description: '[Error]: Missing required value for alias call', + scenario: 'Framework', + successCriteria: + 'Response should contain status code 400 and it should throw instrumentation error with respective message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + context: {}, + type: 'alias', + properties, + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'Missing required value from "previousId"', + statTags: expectedStatTags, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-validation-test-5', + name: 'iterable', + description: '[Error]: Missing userId value for alias call', + scenario: 'Framework', + successCriteria: + 'Response should contain status code 400 and it should throw instrumentation error with respective message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + context: {}, + type: 'alias', + previousId: 'old@email.com', + anonymousId: 'anonId', + properties, + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'Missing required value from "userId"', + statTags: expectedStatTags, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/testUtils.ts b/test/integrations/testUtils.ts index c16aeff98c..0a2727f4d0 100644 --- a/test/integrations/testUtils.ts +++ b/test/integrations/testUtils.ts @@ -292,6 +292,7 @@ export const generatePageOrScreenPayload: any = (parametersOverride: any, eventT 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', }), event: parametersOverride.event, + name: parametersOverride.name, anonymousId: parametersOverride.anonymousId || 'default-anonymousId', properties: parametersOverride.properties, type: eventType || 'page', From 89a71b757998cb55a63c59151b5e3a118084970b Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Wed, 24 Apr 2024 16:27:51 +0530 Subject: [PATCH 119/240] chore: component test refactor for intercom destination (#3236) --- .../destinations/intercom/processor/data.ts | 4156 +---------------- .../intercom/processor/groupTestData.ts | 509 ++ .../intercom/processor/identifyTestData.ts | 1025 ++++ .../intercom/processor/trackTestData.ts | 361 ++ .../intercom/processor/validationTestData.ts | 554 +++ 5 files changed, 2458 insertions(+), 4147 deletions(-) create mode 100644 test/integrations/destinations/intercom/processor/groupTestData.ts create mode 100644 test/integrations/destinations/intercom/processor/identifyTestData.ts create mode 100644 test/integrations/destinations/intercom/processor/trackTestData.ts create mode 100644 test/integrations/destinations/intercom/processor/validationTestData.ts diff --git a/test/integrations/destinations/intercom/processor/data.ts b/test/integrations/destinations/intercom/processor/data.ts index 2c562ed4e9..7bc697bc2d 100644 --- a/test/integrations/destinations/intercom/processor/data.ts +++ b/test/integrations/destinations/intercom/processor/data.ts @@ -1,4149 +1,11 @@ +import { identifyTestData } from './identifyTestData'; +import { trackTestData } from './trackTestData'; +import { groupTestData } from './groupTestData'; +import { validationTestData } from './validationTestData'; + export const data = [ - { - name: 'intercom', - description: 'No message type', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - traits: { - age: 23, - email: 'adc@test.com', - firstname: 'Test', - birthday: '2022-05-13T12:51:01.470Z', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36', - }, - event: 'Product Searched', - originalTimestamp: '2020-09-22T14:42:44.724Z', - timestamp: '2022-09-22T20:12:44.757+05:30', - userId: 'user@1', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 1, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - jobId: 1, - }, - statusCode: 400, - error: - 'message Type is not present. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message Type is not present. Aborting', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Unsupported message type', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - traits: { - age: 23, - email: 'adc@test.com', - firstname: 'Test', - birthday: '2022-05-13T12:51:01.470Z', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36', - }, - event: 'Product Searched', - type: 'page', - originalTimestamp: '2020-09-22T14:42:44.724Z', - timestamp: '2022-09-22T20:12:44.757+05:30', - userId: 'user@1', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 2, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - jobId: 2, - }, - statusCode: 400, - error: - 'message type page is not supported: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message type page is not supported', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Missing required config', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - context: { - traits: { - age: 23, - email: 'adc@test.com', - firstName: 'Test', - }, - }, - type: 'identify', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 3, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - jobId: 3, - }, - statusCode: 400, - error: - 'Access Token is not present. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Access Token is not present. Aborting', - statTags: { - errorCategory: 'dataValidation', - errorType: 'configuration', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Create customer with email as lookup field', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - context: { - traits: { - age: 23, - email: 'test@rudderlabs.com', - phone: '+91 9999999999', - firstName: 'Test', - lastName: 'Rudderlabs', - address: 'california usa', - ownerId: '13', - lastSeenAt: '2023-11-10T14:42:44.724Z', - }, - }, - type: 'identify', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 4, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - JSON: { - email: 'test@rudderlabs.com', - external_id: 'user@1', - last_seen_at: 1699627364, - name: 'Test Rudderlabs', - owner_id: 13, - phone: '+91 9999999999', - custom_attributes: { - address: 'california usa', - age: 23, - }, - }, - XML: {}, - FORM: {}, - JSON_ARRAY: {}, - }, - endpoint: 'https://api.intercom.io/contacts', - headers: { - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - 'Intercom-Version': '2.10', - }, - userId: '', - version: '1', - type: 'REST', - method: 'POST', - files: {}, - params: {}, - }, - metadata: { jobId: 4 }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Update customer with email as lookup field', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@2', - channel: 'web', - context: { - traits: { - age: 32, - email: 'test+2@rudderlabs.com', - phone: '+91 9299999999', - firstName: 'Test', - lastName: 'RudderStack', - ownerId: '14', - }, - }, - type: 'identify', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 5, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - JSON: { - email: 'test+2@rudderlabs.com', - external_id: 'user@2', - name: 'Test RudderStack', - owner_id: 14, - phone: '+91 9299999999', - custom_attributes: { - age: 32, - }, - }, - XML: {}, - FORM: {}, - JSON_ARRAY: {}, - }, - endpoint: 'https://api.intercom.io/contacts/7070129940741e45d040', - headers: { - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - 'Intercom-Version': '2.10', - }, - userId: '', - version: '1', - type: 'REST', - method: 'PUT', - files: {}, - params: {}, - }, - metadata: { jobId: 5 }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Missing required parameters for an identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'anon@2', - channel: 'web', - context: { - traits: { - age: 32, - phone: '+91 9299999999', - firstName: 'Test', - lastName: 'RudderStack', - ownerId: '14', - role: 'user', - source: 'rudder-sdk', - }, - }, - integrations: { - INTERCOM: { - lookup: 'phone', - }, - }, - type: 'identify', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 6, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - jobId: 6, - }, - statusCode: 400, - error: - 'Either email or userId is required for Identify call: Workflow: procWorkflow, Step: identifyPayloadForLatestVersion, ChildStep: undefined, OriginalError: Either email or userId is required for Identify call', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Unauthorized error while searching contact for an identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@3', - channel: 'web', - context: { - traits: { - phone: '+91 9399999999', - email: 'test+3@rudderlabs.com', - firstName: 'Test', - lastName: 'Rudder', - ownerId: '15', - role: 'admin', - source: 'rudder-android-sdk', - }, - }, - integrations: { - INTERCOM: { - lookup: 'email', - }, - }, - type: 'identify', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'invalidApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 7, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - jobId: 7, - }, - statusCode: 401, - error: - '{"message":"{\\"message\\":\\"Unable to search contact due to : [{\\\\\\"code\\\\\\":\\\\\\"unauthorized\\\\\\",\\\\\\"message\\\\\\":\\\\\\"Access Token Invalid\\\\\\"}]: Workflow: procWorkflow, Step: searchContact, ChildStep: undefined, OriginalError: Unable to search contact due to : [{\\\\\\"code\\\\\\":\\\\\\"unauthorized\\\\\\",\\\\\\"message\\\\\\":\\\\\\"Access Token Invalid\\\\\\"}]\\",\\"destinationResponse\\":{\\"response\\":{\\"type\\":\\"error.list\\",\\"request_id\\":\\"request_1\\",\\"errors\\":[{\\"code\\":\\"unauthorized\\",\\"message\\":\\"Access Token Invalid\\"}]},\\"status\\":401}}","destinationResponse":{"response":{"type":"error.list","request_id":"request_1","errors":[{"code":"unauthorized","message":"Access Token Invalid"}]},"status":401}}', - statTags: { - errorCategory: 'network', - errorType: 'aborted', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Track call without event name', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@3', - channel: 'web', - context: { - traits: { - age: 32, - email: 'test+3@rudderlabs.com', - phone: '+91 9399999999', - firstName: 'Test', - lastName: 'RudderStack', - ownerId: '15', - }, - }, - properties: { - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - price: { - amount: 3000, - currency: 'USD', - }, - }, - type: 'track', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 8, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - jobId: 8, - }, - statusCode: 400, - error: - 'Event name is required for track call: Workflow: procWorkflow, Step: trackPayload, ChildStep: undefined, OriginalError: Event name is required for track call', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Successful track call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@2', - channel: 'web', - context: { - traits: { - age: 32, - email: 'test+2@rudderlabs.com', - phone: '+91 9299999999', - firstName: 'Test', - lastName: 'RudderStack', - ownerId: '14', - }, - }, - properties: { - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - price: { - amount: 3000, - currency: 'USD', - }, - }, - event: 'Product Viewed', - type: 'track', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 9, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - FORM: {}, - JSON: { - created_at: 1700628164, - email: 'test+2@rudderlabs.com', - event_name: 'Product Viewed', - metadata: { - price: { - amount: 3000, - currency: 'USD', - }, - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - }, - user_id: 'user@2', - }, - JSON_ARRAY: {}, - XML: {}, - }, - endpoint: 'https://api.intercom.io/events', - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - 'Intercom-Version': '2.10', - }, - method: 'POST', - type: 'REST', - userId: '', - version: '1', - params: {}, - files: {}, - }, - statusCode: 200, - metadata: { - jobId: 9, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Group call without groupId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@4', - channel: 'web', - context: { - traits: { - email: 'test+4@rudderlabs.com', - phone: '+91 9499999999', - firstName: 'John', - lastName: 'Doe', - ownerId: '16', - }, - }, - traits: { - name: 'RudderStack', - size: 500, - website: 'www.rudderstack.com', - industry: 'CDP', - plan: 'enterprise', - }, - type: 'group', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 10, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - jobId: 10, - }, - statusCode: 400, - error: - 'groupId is required for group call: Workflow: procWorkflow, Step: groupPayloadForLatestVersion, ChildStep: validateMessageAndPreparePayload, OriginalError: groupId is required for group call', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Successful group call to create or update company', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@4', - groupId: 'rudderlabs', - channel: 'web', - context: { - traits: { - email: 'test+4@rudderlabs.com', - phone: '+91 9499999999', - firstName: 'John', - lastName: 'Doe', - ownerId: '16', - }, - }, - traits: { - name: 'RudderStack', - size: 500, - website: 'www.rudderstack.com', - industry: 'CDP', - plan: 'enterprise', - }, - type: 'group', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 11, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - JSON: { - company_id: 'rudderlabs', - industry: 'CDP', - name: 'RudderStack', - plan: 'enterprise', - size: 500, - website: 'www.rudderstack.com', - }, - XML: {}, - FORM: {}, - JSON_ARRAY: {}, - }, - endpoint: 'https://api.intercom.io/companies', - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - 'Intercom-Version': '2.10', - }, - method: 'POST', - type: 'REST', - userId: '', - version: '1', - params: {}, - files: {}, - }, - statusCode: 200, - metadata: { - jobId: 11, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Successful group call to add user to company', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@5', - groupId: 'rudderlabs', - channel: 'web', - context: { - traits: { - email: 'test+5@rudderlabs.com', - phone: '+91 9599999999', - firstName: 'John', - lastName: 'Snow', - ownerId: '17', - }, - }, - traits: { - name: 'RudderStack', - size: 500, - website: 'www.rudderstack.com', - industry: 'CDP', - plan: 'enterprise', - }, - type: 'group', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'eu', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 12, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - JSON: { - id: '657264e9018c0a647s45', - }, - XML: {}, - FORM: {}, - JSON_ARRAY: {}, - }, - endpoint: 'https://api.eu.intercom.io/contacts/70701240741e45d040/companies', - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - 'Intercom-Version': '2.10', - }, - method: 'POST', - type: 'REST', - userId: '', - version: '1', - params: {}, - files: {}, - }, - statusCode: 200, - metadata: { - jobId: 12, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Identify rEtl test', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - externalId: [ - { - id: 'user@1', - type: 'INTERCOM-customer', - identifierType: 'user_id', - }, - ], - mappedToDestination: 'true', - }, - traits: { - email: 'test@rudderlabs.com', - phone: '+91 9999999999', - name: 'Test Rudderlabs', - owner_id: 13, - }, - type: 'identify', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 13, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - JSON: { - email: 'test@rudderlabs.com', - name: 'Test Rudderlabs', - phone: '+91 9999999999', - owner_id: 13, - user_id: 'user@1', - }, - XML: {}, - FORM: {}, - JSON_ARRAY: {}, - }, - endpoint: 'https://api.intercom.io/contacts', - headers: { - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - 'Intercom-Version': '2.10', - }, - userId: '', - version: '1', - type: 'REST', - method: 'POST', - files: {}, - params: {}, - }, - metadata: { jobId: 13 }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Track rEtl test', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - context: { - mappedToDestination: 'true', - }, - traits: { - event_name: 'Product Viewed', - user_id: 'user@1', - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - price: { - amount: 3000, - currency: 'USD', - }, - }, - event: 'Product Viewed', - type: 'track', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 14, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - JSON: { - event_name: 'Product Viewed', - price: { - amount: 3000, - currency: 'USD', - }, - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - user_id: 'user@1', - }, - XML: {}, - FORM: {}, - JSON_ARRAY: {}, - }, - endpoint: 'https://api.intercom.io/events', - headers: { - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - 'Intercom-Version': '2.10', - }, - userId: '', - version: '1', - type: 'REST', - method: 'POST', - files: {}, - params: {}, - }, - metadata: { jobId: 14 }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - userId: 'test_user_id_1', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - address: { - city: 'Kolkata', - state: 'West Bengal', - }, - originalArray: [ - { - nested_field: 'nested value', - tags: ['tag_1', 'tag_2', 'tag_3'], - }, - { - nested_field: 'nested value', - tags: ['tag_1'], - }, - { - nested_field: 'nested value', - }, - ], - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 15, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - user_id: 'test_user_id_1', - email: 'test_1@test.com', - phone: '9876543210', - name: 'Test Name', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - 'address.city': 'Kolkata', - 'address.state': 'West Bengal', - 'originalArray[0].nested_field': 'nested value', - 'originalArray[0].tags[0]': 'tag_1', - 'originalArray[0].tags[1]': 'tag_2', - 'originalArray[0].tags[2]': 'tag_3', - 'originalArray[1].nested_field': 'nested value', - 'originalArray[1].tags[0]': 'tag_1', - 'originalArray[2].nested_field': 'nested value', - }, - update_last_request_at: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 15, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 16, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: true, - name: 'Test Name', - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 16, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 17, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: true, - name: 'Name', - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 17, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - firstName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 18, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: true, - name: 'Name', - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 18, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old Version: Identify call without email and userId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - firstName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 19, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: - 'Either of `email` or `userId` is required for Identify call: Workflow: procWorkflow, Step: identifyPayloadForOlderVersion, ChildStep: undefined, OriginalError: Either of `email` or `userId` is required for Identify call', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - metadata: { - jobId: 19, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - company: { - name: 'Test Comp', - id: 'company_id', - industry: 'test industry', - key1: 'value1', - key2: { - a: 'a', - }, - key3: [1, 2, 3], - }, - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 20, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: true, - name: 'Name', - companies: [ - { - company_id: 'company_id', - custom_attributes: { - key1: 'value1', - key2: '{"a":"a"}', - key3: '[1,2,3]', - }, - name: 'Test Comp', - industry: 'test industry', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 20, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - company: { - name: 'Test Comp', - industry: 'test industry', - key1: 'value1', - key2: null, - key3: ['value1', 'value2'], - key4: { - foo: 'bar', - }, - }, - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - updateLastRequestAt: false, - }, - }, - metadata: { - jobId: 21, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: false, - name: 'Name', - companies: [ - { - company_id: 'c0277b5c814453e5135f515f943d085a', - custom_attributes: { - key1: 'value1', - key3: '["value1","value2"]', - key4: '{"foo":"bar"}', - }, - name: 'Test Comp', - industry: 'test industry', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 21, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - company: { - industry: 'test industry', - key1: 'value1', - key2: null, - }, - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 22, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: true, - name: 'Name', - companies: [], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 22, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful track call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - userId: 'test_user_id_1', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - properties: { - property1: 1, - property2: 'test', - property3: true, - property4: '2020-10-05T09:09:03.731Z', - property5: { - property1: 1, - property2: 'test', - property3: { - subProp1: { - a: 'a', - b: 'b', - }, - subProp2: ['a', 'b'], - }, - }, - properties6: null, - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - price: { - amount: 3000, - currency: 'USD', - }, - article: { - url: 'https://example.org/ab1de.html', - value: 'the dude abides', - }, - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'track', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 23, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/events', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - user_id: 'test_user_id_1', - email: 'test_1@test.com', - event_name: 'Test Event 2', - created: 1601493061, - metadata: { - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - price: { - amount: 3000, - currency: 'USD', - }, - article: { - url: 'https://example.org/ab1de.html', - value: 'the dude abides', - }, - property1: 1, - property2: 'test', - property3: true, - property4: '2020-10-05T09:09:03.731Z', - 'property5.property1': 1, - 'property5.property2': 'test', - 'property5.property3.subProp1.a': 'a', - 'property5.property3.subProp1.b': 'b', - 'property5.property3.subProp2[0]': 'a', - 'property5.property3.subProp2[1]': 'b', - properties6: null, - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 23, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful track call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'track', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 24, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/events', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - event_name: 'Test Event 2', - created: 1601493061, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 24, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : Track call without email or userId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'track', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 25, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: - 'Either email or userId is required for Track call: Workflow: procWorkflow, Step: trackPayload, ChildStep: undefined, OriginalError: Either email or userId is required for Track call', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - metadata: { - jobId: 25, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - externalId: [ - { - id: '10156', - type: 'INTERCOM-customer', - identifierType: 'user_id', - }, - ], - mappedToDestination: 'true', - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 26, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - phone: '9876543210', - key1: 'value1', - update_last_request_at: true, - user_id: '10156', - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 26, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - sendAnonymousId: true, - }, - }, - metadata: { - jobId: 27, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - phone: '9876543210', - name: 'Test Name', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - user_id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - update_last_request_at: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 27, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : Identify call without email or userId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 28, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: - 'Either of `email` or `userId` is required for Identify call: Workflow: procWorkflow, Step: identifyPayloadForOlderVersion, ChildStep: undefined, OriginalError: Either of `email` or `userId` is required for Identify call', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - metadata: { - jobId: 28, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : successful group call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - groupId: 'test_company_id_wdasda', - traits: { - employees: 450, - plan: 'basic', - userId: 'sdfrsdfsdfsf', - email: 'test@test.com', - name: 'rudderUpdate', - size: '50', - industry: 'IT', - monthlySpend: '2131231', - remoteCreatedAt: '1683017572', - key1: 'val1', - }, - anonymousId: 'sdfrsdfsdfsf', - integrations: { - All: true, - }, - type: 'group', - userId: 'sdfrsdfsdfsf', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'abcd=', - appId: 'asdasdasd', - apiVersion: 'v1', - collectContext: false, - }, - }, - metadata: { - jobId: 29, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/companies', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer abcd=', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - company_id: 'test_company_id_wdasda', - name: 'rudderUpdate', - plan: 'basic', - size: 50, - industry: 'IT', - monthly_spend: 2131231, - remote_created_at: 1683017572, - custom_attributes: { - employees: 450, - email: 'test@test.com', - key1: 'val1', - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: 'sdfrsdfsdfsf', - }, - statusCode: 200, - metadata: { - jobId: 29, - }, - }, - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer abcd=', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - user_id: 'sdfrsdfsdfsf', - companies: [ - { - company_id: 'test_company_id_wdasda', - name: 'rudderUpdate', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: 'sdfrsdfsdfsf', - }, - statusCode: 200, - metadata: { - jobId: 29, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : successful group call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - groupId: 'test_company_id', - traits: { - plan: 'basic', - name: 'rudderUpdate', - size: 50, - industry: 'IT', - monthlySpend: '2131231', - email: 'comanyemail@abc.com', - }, - anonymousId: '12312312', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - userAgent: 'unknown', - }, - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - type: 'group', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'abcd=', - apiVersion: 'v1', - appId: 'asdasdasd', - collectContext: false, - }, - }, - metadata: { - jobId: 30, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/companies', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer abcd=', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - company_id: 'test_company_id', - name: 'rudderUpdate', - plan: 'basic', - size: 50, - industry: 'IT', - monthly_spend: 2131231, - custom_attributes: { - email: 'comanyemail@abc.com', - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '12312312', - }, - statusCode: 200, - metadata: { - jobId: 30, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : successful group call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - groupId: 'test_company_id_wdasda', - context: { - traits: { - email: 'testUser@test.com', - }, - }, - traits: { - employees: 450, - plan: 'basic', - email: 'test@test.com', - name: 'rudderUpdate', - size: '50', - industry: 'IT', - website: 'url', - monthlySpend: '2131231', - remoteCreatedAt: '1683017572', - key1: 'val1', - }, - anonymousId: 'sdfrsdfsdfsf', - integrations: { - All: true, - }, - type: 'group', - userId: 'sdfrsdfsdfsf', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'abcd=', - apiVersion: 'v1', - appId: 'asdasdasd', - collectContext: false, - }, - }, - metadata: { - jobId: 31, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/companies', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer abcd=', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - company_id: 'test_company_id_wdasda', - name: 'rudderUpdate', - plan: 'basic', - size: 50, - website: 'url', - industry: 'IT', - monthly_spend: 2131231, - remote_created_at: 1683017572, - custom_attributes: { - employees: 450, - email: 'test@test.com', - key1: 'val1', - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: 'sdfrsdfsdfsf', - }, - statusCode: 200, - metadata: { - jobId: 31, - }, - }, - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer abcd=', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - user_id: 'sdfrsdfsdfsf', - email: 'testUser@test.com', - companies: [ - { - company_id: 'test_company_id_wdasda', - name: 'rudderUpdate', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: 'sdfrsdfsdfsf', - }, - statusCode: 200, - metadata: { - jobId: 31, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : successful group call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - groupId: 'test_company_id_wdasda', - context: { - traits: { - email: 'testUser@test.com', - }, - }, - traits: { - employees: 450, - plan: 'basic', - email: 'test@test.com', - name: 'rudderUpdate', - size: '50', - industry: 'IT', - website: 'url', - monthlySpend: '2131231', - remoteCreatedAt: '1683017572', - key1: 'val1', - key2: { - a: 'a', - b: 'b', - }, - key3: [1, 2, 3], - key4: null, - }, - anonymousId: 'anonId', - integrations: { - All: true, - }, - type: 'group', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'abcd=', - appId: 'asdasdasd', - apiVersion: 'v1', - collectContext: false, - sendAnonymousId: true, - }, - }, - metadata: { - jobId: 32, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/companies', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer abcd=', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - company_id: 'test_company_id_wdasda', - name: 'rudderUpdate', - plan: 'basic', - size: 50, - website: 'url', - industry: 'IT', - monthly_spend: 2131231, - remote_created_at: 1683017572, - custom_attributes: { - employees: 450, - email: 'test@test.com', - key1: 'val1', - 'key2.a': 'a', - 'key2.b': 'b', - 'key3[0]': 1, - 'key3[1]': 2, - 'key3[2]': 3, - key4: null, - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: 'anonId', - }, - statusCode: 200, - metadata: { - jobId: 32, - }, - }, - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer abcd=', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - user_id: 'anonId', - email: 'testUser@test.com', - companies: [ - { - company_id: 'test_company_id_wdasda', - name: 'rudderUpdate', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: 'anonId', - }, - statusCode: 200, - metadata: { - jobId: 32, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Test 0', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - userId: 'test_user_id_1', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - address: { - city: 'Kolkata', - state: 'West Bengal', - }, - originalArray: [ - { - nested_field: 'nested value', - tags: ['tag_1', 'tag_2', 'tag_3'], - }, - { - nested_field: 'nested value', - tags: ['tag_1'], - }, - { - nested_field: 'nested value', - }, - ], - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - Config: { - apiKey: 'intercomApiKey', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - user_id: 'test_user_id_1', - email: 'test_1@test.com', - phone: '9876543210', - name: 'Test Name', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - 'address.city': 'Kolkata', - 'address.state': 'West Bengal', - 'originalArray[0].nested_field': 'nested value', - 'originalArray[0].tags[0]': 'tag_1', - 'originalArray[0].tags[1]': 'tag_2', - 'originalArray[0].tags[2]': 'tag_3', - 'originalArray[1].nested_field': 'nested value', - 'originalArray[1].tags[0]': 'tag_1', - 'originalArray[2].nested_field': 'nested value', - }, - update_last_request_at: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Test 1', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - Config: { - apiKey: 'intercomApiKey', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: true, - name: 'Test Name', - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Test 2', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - Config: { - apiKey: 'intercomApiKey', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: true, - name: 'Name', - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - }, - ], - }, - }, - }, + ...identifyTestData, + ...trackTestData, + ...groupTestData, + ...validationTestData, ]; diff --git a/test/integrations/destinations/intercom/processor/groupTestData.ts b/test/integrations/destinations/intercom/processor/groupTestData.ts new file mode 100644 index 0000000000..cb81df7bd2 --- /dev/null +++ b/test/integrations/destinations/intercom/processor/groupTestData.ts @@ -0,0 +1,509 @@ +import { Destination } from '../../../../../src/types'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedGroupPayload, +} from '../../../testUtils'; + +const v1Config = { + apiKey: 'abcd=', + appId: 'asdasdasd', + apiVersion: 'v1', + collectContext: false, +}; + +const v2Config = { + apiKey: 'testApiKey', + apiVersion: 'v2', + apiServer: 'standard', + sendAnonymousId: false, +}; + +const v1Headers = { + 'Content-Type': 'application/json', + Authorization: 'Bearer abcd=', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const v2Headers = { + Accept: 'application/json', + Authorization: 'Bearer testApiKey', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.10', +}; + +const destination: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: {}, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const v1Destination = { ...destination, Config: v1Config }; +const v2Destination = { ...destination, Config: v2Config }; + +const userTraits = { + ownerId: '17', + firstName: 'John', + lastName: 'Snow', + phone: '+91 9599999999', + email: 'test+5@rudderlabs.com', +}; + +const group1Traits = { + size: 500, + industry: 'CDP', + plan: 'enterprise', + name: 'RudderStack', + website: 'www.rudderstack.com', +}; + +const group2Traits = { + size: '50', + key1: 'val1', + plan: 'basic', + industry: 'IT', + employees: 450, + monthlySpend: '2131231', + userId: 'sdfrsdfsdfsf', + email: 'test@test.com', + name: 'rudderUpdate', + remoteCreatedAt: '1683017572', +}; + +const timestamp = '2023-11-22T10:12:44.757+05:30'; +const originalTimestamp = '2023-11-10T14:42:44.724Z'; + +const endpoint = 'https://api.intercom.io/companies'; + +export const groupTestData = [ + { + id: 'intercom-group-test-1', + name: 'intercom', + description: 'V2 version : Successful group call to create or update company', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create or update company payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: generateSimplifiedGroupPayload({ + userId: 'user@4', + groupId: 'rudderlabs', + context: { + traits: { email: 'test+4@rudderlabs.com' }, + }, + traits: group1Traits, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint, + headers: v2Headers, + JSON: { company_id: 'rudderlabs', ...group1Traits }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-group-test-2', + name: 'intercom', + description: 'V2 version : Successful group call to add user to company', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain add user to company payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...v2Destination, Config: { ...v2Destination.Config, apiServer: 'eu' } }, + message: generateSimplifiedGroupPayload({ + userId: 'user@5', + groupId: 'rudderlabs', + context: { + traits: userTraits, + }, + traits: group1Traits, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint: 'https://api.eu.intercom.io/contacts/70701240741e45d040/companies', + headers: v2Headers, + JSON: { id: '657264e9018c0a647s45' }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-group-test-3', + name: 'intercom', + description: + 'V1 version : successful group call to create company and add user to company based on userId', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create company and add user to company payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: generateSimplifiedGroupPayload({ + userId: 'sdfrsdfsdfsf', + anonymousId: 'sdfrsdfsdfsf', + groupId: 'test_company_id_wdasda', + context: { + traits: {}, + }, + traits: group2Traits, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: 'sdfrsdfsdfsf', + endpoint, + headers: v1Headers, + JSON: { + company_id: 'test_company_id_wdasda', + custom_attributes: { + email: 'test@test.com', + employees: 450, + key1: 'val1', + }, + industry: 'IT', + monthly_spend: 2131231, + name: 'rudderUpdate', + plan: 'basic', + remote_created_at: 1683017572, + size: 50, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + { + output: transformResultBuilder({ + userId: 'sdfrsdfsdfsf', + endpoint: 'https://api.intercom.io/users', + headers: v1Headers, + JSON: { + companies: [ + { + name: 'rudderUpdate', + company_id: 'test_company_id_wdasda', + }, + ], + user_id: 'sdfrsdfsdfsf', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-group-test-4', + name: 'intercom', + description: + 'V1 version : successful group call to create company and add user to company based on userId', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create company and add user to company payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: generateSimplifiedGroupPayload({ + userId: 'sdfrsdfsdfsf', + anonymousId: 'sdfrsdfsdfsf', + groupId: 'test_company_id_wdasda', + context: { + traits: { + email: 'testUser@test.com', + }, + }, + traits: { ...group2Traits, website: 'url' }, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: 'sdfrsdfsdfsf', + endpoint, + headers: v1Headers, + JSON: { + company_id: 'test_company_id_wdasda', + custom_attributes: { + email: 'test@test.com', + employees: 450, + key1: 'val1', + }, + industry: 'IT', + monthly_spend: 2131231, + name: 'rudderUpdate', + plan: 'basic', + remote_created_at: 1683017572, + size: 50, + website: 'url', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + { + output: transformResultBuilder({ + userId: 'sdfrsdfsdfsf', + endpoint: 'https://api.intercom.io/users', + headers: v1Headers, + JSON: { + companies: [ + { + name: 'rudderUpdate', + company_id: 'test_company_id_wdasda', + }, + ], + email: 'testUser@test.com', + user_id: 'sdfrsdfsdfsf', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-group-test-5', + name: 'intercom', + description: + 'V1 version : successful group call without userId (anonId will be considered as userId)', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create company and add user to company payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...v1Destination, Config: v1Destination.Config, sendAnonymousId: true }, + message: { + anonymousId: 'anonId', + groupId: 'test_company_id_wdasda', + context: { + traits: { + email: 'testUser@test.com', + }, + }, + traits: { + ...group2Traits, + website: 'url', + key1: 'val1', + key2: { + a: 'a', + b: 'b', + }, + key3: [1, 2, 3], + key4: null, + }, + type: 'group', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: 'anonId', + endpoint, + headers: v1Headers, + JSON: { + company_id: 'test_company_id_wdasda', + custom_attributes: { + email: 'test@test.com', + employees: 450, + key1: 'val1', + 'key2.a': 'a', + 'key2.b': 'b', + 'key3[0]': 1, + 'key3[1]': 2, + 'key3[2]': 3, + key4: null, + }, + industry: 'IT', + monthly_spend: 2131231, + name: 'rudderUpdate', + plan: 'basic', + remote_created_at: 1683017572, + size: 50, + website: 'url', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + { + output: transformResultBuilder({ + userId: 'anonId', + endpoint: 'https://api.intercom.io/users', + headers: v1Headers, + JSON: { + companies: [ + { + name: 'rudderUpdate', + company_id: 'test_company_id_wdasda', + }, + ], + email: 'testUser@test.com', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-group-test-6', + name: 'intercom', + description: 'V1 version : successful group call with email as custom attribute', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create company payload with email as custom attribute', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '12312312', + groupId: 'test_company_id', + context: { + traits: {}, + }, + traits: { ...group1Traits, monthlySpend: '2131231', email: 'comanyemail@abc.com' }, + type: 'group', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '12312312', + endpoint, + headers: v1Headers, + JSON: { + ...group1Traits, + custom_attributes: { + email: 'comanyemail@abc.com', + }, + monthly_spend: 2131231, + company_id: 'test_company_id', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/intercom/processor/identifyTestData.ts b/test/integrations/destinations/intercom/processor/identifyTestData.ts new file mode 100644 index 0000000000..d88b7cf7f5 --- /dev/null +++ b/test/integrations/destinations/intercom/processor/identifyTestData.ts @@ -0,0 +1,1025 @@ +import { Destination } from '../../../../../src/types'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedIdentifyPayload, +} from '../../../testUtils'; + +const v1Config = { + apiKey: 'intercomApiKey', + apiVersion: 'v1', + appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', + collectContext: false, +}; + +const v2Config = { + apiKey: 'testApiKey', + apiVersion: 'v2', + apiServer: 'standard', + sendAnonymousId: false, +}; + +const v2Headers = { + Accept: 'application/json', + Authorization: 'Bearer testApiKey', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.10', +}; + +const v1Headers = { + 'Content-Type': 'application/json', + Authorization: 'Bearer intercomApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const destination: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: {}, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const v1Destination = { ...destination, Config: v1Config }; +const v2Destination = { ...destination, Config: v2Config }; + +const user1Traits = { + age: 23, + ownerId: '13', + firstName: 'Test', + lastName: 'Rudderlabs', + phone: '+91 9999999999', + address: 'california usa', + email: 'test@rudderlabs.com', + lastSeenAt: '2023-11-10T14:42:44.724Z', +}; + +const user2Traits = { + age: 32, + ownerId: '14', + firstName: 'Test', + lastName: 'RudderStack', + phone: '+91 9299999999', + email: 'test+2@rudderlabs.com', +}; + +const user3Traits = { + owner_id: 13, + name: 'Test Rudderlabs', + phone: '+91 9999999999', + email: 'test@rudderlabs.com', +}; + +const user4Traits = { + key1: 'value1', + name: 'Test Name', + firstName: 'Test', + lastName: 'Name', + phone: '9876543210', + userId: 'test_user_id_1', + email: 'test_1@test.com', + createdAt: '2020-09-30T19:11:00.337Z', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + address: { + city: 'Kolkata', + state: 'West Bengal', + }, + originalArray: [ + { + nested_field: 'nested value', + tags: ['tag_1', 'tag_2', 'tag_3'], + }, + { + nested_field: 'nested value', + tags: ['tag_1'], + }, + { + nested_field: 'nested value', + }, + ], +}; + +const user5Traits = { + firstName: 'Test', + lastName: 'Name', + key1: 'value1', + phone: '9876543210', + email: 'test_1@test.com', + createdAt: '2020-09-30T19:11:00.337Z', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', +}; + +const user6Traits = { + lastName: 'Name', + key1: 'value1', + phone: '9876543210', + email: 'test_1@test.com', + createdAt: '2020-09-30T19:11:00.337Z', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + company: { + key1: 'value1', + name: 'Test Comp', + id: 'company_id', + industry: 'test industry', + key2: { + a: 'a', + }, + key3: [1, 2, 3], + }, +}; + +const expectedUser1Traits = { + owner_id: 13, + external_id: 'user@1', + last_seen_at: 1699627364, + name: 'Test Rudderlabs', + phone: '+91 9999999999', + email: 'test@rudderlabs.com', + custom_attributes: { + age: 23, + address: 'california usa', + }, +}; + +const expectedUser2Traits = { + owner_id: 14, + external_id: 'user@2', + name: 'Test RudderStack', + phone: '+91 9299999999', + email: 'test+2@rudderlabs.com', + custom_attributes: { + age: 32, + }, +}; + +const expectedUser3Traits = { + owner_id: 13, + user_id: 'user@1', + name: 'Test Rudderlabs', + phone: '+91 9999999999', + email: 'test@rudderlabs.com', +}; + +const expectedUser4Traits = { + name: 'Test Name', + phone: '9876543210', + email: 'test_1@test.com', + signed_up_at: 1601493060, + user_id: 'test_user_id_1', + last_seen_user_agent: 'unknown', + custom_attributes: { + key1: 'value1', + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + 'originalArray[0].nested_field': 'nested value', + 'originalArray[0].tags[0]': 'tag_1', + 'originalArray[0].tags[1]': 'tag_2', + 'originalArray[0].tags[2]': 'tag_3', + 'originalArray[1].nested_field': 'nested value', + 'originalArray[1].tags[0]': 'tag_1', + 'originalArray[2].nested_field': 'nested value', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + }, + update_last_request_at: true, +}; + +const expectedUser5Traits = { + name: 'Test Name', + phone: '9876543210', + email: 'test_1@test.com', + signed_up_at: 1601493060, + update_last_request_at: true, + last_seen_user_agent: 'unknown', + custom_attributes: { + key1: 'value1', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + }, +}; + +const expectedUser6Traits = { + name: 'Name', + phone: '9876543210', + email: 'test_1@test.com', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + custom_attributes: { + key1: 'value1', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + }, + companies: [ + { + name: 'Test Comp', + industry: 'test industry', + company_id: 'company_id', + custom_attributes: { + key1: 'value1', + key2: '{"a":"a"}', + key3: '[1,2,3]', + }, + }, + ], +}; + +const timestamp = '2023-11-22T10:12:44.757+05:30'; +const originalTimestamp = '2023-11-10T14:42:44.724Z'; + +const v2Endpoint = 'https://api.intercom.io/contacts'; +const v1Endpoint = 'https://api.intercom.io/users'; + +export const identifyTestData = [ + { + id: 'intercom-identify-test-1', + name: 'intercom', + description: 'V2 version : Create customer with email as lookup field', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: generateSimplifiedIdentifyPayload({ + userId: 'user@1', + context: { + traits: user1Traits, + }, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint: v2Endpoint, + headers: v2Headers, + JSON: expectedUser1Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-2', + name: 'intercom', + description: 'V2 version : Update customer with email as lookup field', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain update user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: generateSimplifiedIdentifyPayload({ + userId: 'user@2', + context: { + traits: user2Traits, + }, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint: `${v2Endpoint}/7070129940741e45d040`, + headers: v2Headers, + method: 'PUT', + JSON: expectedUser2Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-3', + name: 'intercom', + description: 'V2 version : Identify rEtl test', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + context: { + externalId: [ + { + id: 'user@1', + type: 'INTERCOM-customer', + identifierType: 'user_id', + }, + ], + mappedToDestination: 'true', + }, + traits: user3Traits, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint: v2Endpoint, + headers: v2Headers, + method: 'POST', + JSON: expectedUser3Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-4', + name: 'intercom', + description: 'V1 version : successful identify call to create user', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: user4Traits, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: expectedUser4Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-5', + name: 'intercom', + description: 'V1 version : successful identify call to create user', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: user5Traits, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: expectedUser5Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-6', + name: 'intercom', + description: 'V1 version : successful identify call to update user', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain update user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: { ...user5Traits, firstName: undefined }, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: { ...expectedUser5Traits, name: 'Name' }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-7', + name: 'intercom', + description: 'V1 version : successful identify call to update user', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain update user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: { ...user5Traits, lastName: undefined }, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: { ...expectedUser5Traits, name: 'Test' }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-8', + name: 'intercom', + description: 'V1 version : successful identify call to create user', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: user6Traits, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: expectedUser6Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-9', + name: 'intercom', + description: 'V1 version : successful identify call to create user', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: { + ...user6Traits, + company: { + name: 'Test Comp', + industry: 'test industry', + key1: 'value1', + key2: null, + key3: ['value1', 'value2'], + key4: { + foo: 'bar', + }, + }, + }, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: { + ...expectedUser6Traits, + companies: [ + { + ...expectedUser6Traits.companies[0], + custom_attributes: { + key1: 'value1', + key3: '["value1","value2"]', + key4: '{"foo":"bar"}', + }, + company_id: 'c0277b5c814453e5135f515f943d085a', + }, + ], + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-10', + name: 'intercom', + description: 'V1 version : successful identify call to update user', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain update user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: { + ...user5Traits, + firstName: undefined, + company: { + industry: 'test industry', + key1: 'value1', + key2: null, + }, + }, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: { ...expectedUser5Traits, companies: [], name: 'Name' }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-11', + name: 'intercom', + description: + 'No Version : Successful identify call to create user without giving apiVersion in configuration', + scenario: 'Business', + successCriteria: + 'Response should take v1 apiVersion by default and response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...v1Destination, apiVersion: undefined }, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: user4Traits, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: expectedUser4Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-12', + name: 'intercom', + description: + 'No Version : Successful identify call to create user without giving apiVersion in configuration', + scenario: 'Business', + successCriteria: + 'Response should take v1 apiVersion by default and response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...v1Destination, apiVersion: undefined }, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: user5Traits, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: expectedUser5Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-13', + name: 'intercom', + description: + 'No Version : Successful identify call to update user without giving apiVersion in configuration', + scenario: 'Business', + successCriteria: + 'Response should take v1 apiVersion by default and response status code should be 200 and response should contain update user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...v1Destination, apiVersion: undefined }, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: { ...user5Traits, firstName: undefined }, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: { ...expectedUser5Traits, name: 'Name' }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-14', + name: 'intercom', + description: + 'V1 version : Successful identify call to update user with sendAnonymousId configuration set to true', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain update user payload with all traits and userId should be equal to anonymousId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: user5Traits, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: expectedUser5Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-15', + name: 'intercom', + description: 'V1 version : Identify rEtl test', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + context: { + externalId: [ + { + id: '10156', + type: 'INTERCOM-customer', + identifierType: 'user_id', + }, + ], + mappedToDestination: 'true', + traits: user5Traits, + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: { + ...user5Traits, + user_id: '10156', + name: 'Test Name', + update_last_request_at: true, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/intercom/processor/trackTestData.ts b/test/integrations/destinations/intercom/processor/trackTestData.ts new file mode 100644 index 0000000000..15bed25d68 --- /dev/null +++ b/test/integrations/destinations/intercom/processor/trackTestData.ts @@ -0,0 +1,361 @@ +import { Destination } from '../../../../../src/types'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedTrackPayload, +} from '../../../testUtils'; + +const v1Config = { + apiKey: 'intercomApiKey', + apiVersion: 'v1', + appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', + collectContext: false, +}; + +const v2Config = { + apiKey: 'testApiKey', + apiVersion: 'v2', + apiServer: 'standard', + sendAnonymousId: false, +}; + +const v1Headers = { + 'Content-Type': 'application/json', + Authorization: 'Bearer intercomApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const v2Headers = { + Accept: 'application/json', + Authorization: 'Bearer testApiKey', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.10', +}; + +const destination: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: {}, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const v1Destination = { ...destination, Config: v1Config }; +const v2Destination = { ...destination, Config: v2Config }; + +const userTraits = { + age: 23, + ownerId: '13', + firstName: 'Test', + lastName: 'Rudderlabs', + phone: '+91 9999999999', + address: 'california usa', + email: 'test@rudderlabs.com', + lastSeenAt: '2023-11-10T14:42:44.724Z', +}; + +const properties = { + revenue: { + amount: 1232, + currency: 'inr', + test: 123, + }, + price: { + amount: 3000, + currency: 'USD', + }, +}; + +const nestedProperties = { + property1: 1, + property2: 'test', + property3: true, + property4: '2020-10-05T09:09:03.731Z', + property5: { + property1: 1, + property2: 'test', + property3: { + subProp1: { + a: 'a', + b: 'b', + }, + subProp2: ['a', 'b'], + }, + }, + properties6: null, + revenue: { + amount: 1232, + currency: 'inr', + test: 123, + }, + price: { + amount: 3000, + currency: 'USD', + }, + article: { + url: 'https://example.org/ab1de.html', + value: 'the dude abides', + }, +}; + +const expectedNestedProperties = { + revenue: { + amount: 1232, + currency: 'inr', + test: 123, + }, + price: { + amount: 3000, + currency: 'USD', + }, + article: { + url: 'https://example.org/ab1de.html', + value: 'the dude abides', + }, + property1: 1, + property2: 'test', + property3: true, + property4: '2020-10-05T09:09:03.731Z', + 'property5.property1': 1, + 'property5.property2': 'test', + 'property5.property3.subProp1.a': 'a', + 'property5.property3.subProp1.b': 'b', + 'property5.property3.subProp2[0]': 'a', + 'property5.property3.subProp2[1]': 'b', + properties6: null, +}; + +const expectedOutput = { + user_id: 'user@2', + created: 1699627364, + event_name: 'Test Event 2', + email: 'test@rudderlabs.com', +}; + +const timestamp = '2023-11-22T10:12:44.757+05:30'; +const originalTimestamp = '2023-11-10T14:42:44.724Z'; + +const endpoint = 'https://api.intercom.io/events'; + +export const trackTestData = [ + { + id: 'intercom-track-test-1', + name: 'intercom', + description: 'V2 version : Successful track call', + scenario: 'Business', + successCriteria: + "Response status code should be 200 and response should contain event name and it's properties", + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: generateSimplifiedTrackPayload({ + userId: 'user@2', + event: 'Product Viewed', + context: { + traits: userTraits, + }, + properties, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint, + headers: v2Headers, + JSON: { + user_id: 'user@2', + metadata: properties, + created_at: 1699627364, + event_name: 'Product Viewed', + email: 'test@rudderlabs.com', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-track-test-2', + name: 'intercom', + description: 'V2 version : Track rEtl test', + scenario: 'Business', + successCriteria: + "Response status code should be 200 and response should contain event name and it's properties", + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + userId: 'user@2', + event: 'Product Viewed', + context: { + mappedToDestination: 'true', + }, + traits: { + ...properties, + user_id: 'user@1', + event_name: 'Product Viewed', + }, + type: 'track', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint, + headers: v2Headers, + JSON: { + ...properties, + user_id: 'user@1', + event_name: 'Product Viewed', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-track-test-3', + name: 'intercom', + description: 'V1 version : successful track call with nested properties', + scenario: 'Business', + successCriteria: + "Response status code should be 200 and response should contain event name and it's properties", + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: generateSimplifiedTrackPayload({ + userId: 'user@2', + event: 'Test Event 2', + context: { + traits: userTraits, + }, + properties: nestedProperties, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: 'default-anonymousId', + endpoint, + headers: v1Headers, + JSON: { + ...expectedOutput, + metadata: { + ...expectedNestedProperties, + }, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-track-test-4', + name: 'intercom', + description: 'V1 version : successful track call without properties', + scenario: 'Business', + successCriteria: 'Response status code should be 200 and response should contain event name', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: generateSimplifiedTrackPayload({ + userId: 'user@2', + event: 'Test Event 2', + context: { + traits: userTraits, + }, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + endpoint, + headers: v1Headers, + JSON: expectedOutput, + userId: 'default-anonymousId', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/intercom/processor/validationTestData.ts b/test/integrations/destinations/intercom/processor/validationTestData.ts new file mode 100644 index 0000000000..45fe3c1b9e --- /dev/null +++ b/test/integrations/destinations/intercom/processor/validationTestData.ts @@ -0,0 +1,554 @@ +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata } from '../../../testUtils'; + +const v1Config = { + apiKey: 'intercomApiKey', + apiVersion: 'v1', + appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', + collectContext: false, +}; + +const v2Config = { + apiKey: 'testApiKey', + apiVersion: 'v2', + apiServer: 'standard', + sendAnonymousId: false, +}; + +const destination: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + apiKey: 'testApiKey', + apiVersion: 'v2', + apiServer: 'standard', + sendAnonymousId: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const v1Destination = { ...destination, Config: v1Config }; +const v2Destination = { ...destination, Config: v2Config }; + +const userTraits = { + age: 23, + ownerId: '14', + role: 'user', + source: 'rudder-sdk', + firstname: 'Test', + lastName: 'RudderStack', + phone: '+91 9299999999', + birthday: '2022-05-13T12:51:01.470Z', +}; + +const properties = { + revenue: { + amount: 1232, + currency: 'inr', + test: 123, + }, + price: { + amount: 3000, + currency: 'USD', + }, +}; + +const groupTraits = { + name: 'RudderStack', + size: 500, + website: 'www.rudderstack.com', + industry: 'CDP', + plan: 'enterprise', +}; + +const expectedStatTags = { + destType: 'INTERCOM', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +export const validationTestData: ProcessorTestData[] = [ + { + id: 'intercom-validation-test-1', + name: 'intercom', + description: '[Error - V2 version]: Check for no message type', + scenario: 'Framework', + successCriteria: 'Response status code should be 400 with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + event: 'Product Searched', + context: { + traits: userTraits, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'message Type is not present. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message Type is not present. Aborting', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-2', + name: 'intercom', + description: '[Error - V2 version]: Check for unsupported message type', + scenario: 'Framework', + successCriteria: + 'Response should contain error message and status code should be 400, as we are sending a message type which is not supported by intercom destination and the error message should be Event type alias is not supported', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + userId: 'user@45', + type: 'page', + context: { + traits: userTraits, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'message type page is not supported: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message type page is not supported', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-3', + name: 'intercom', + description: '[Error - V2 version]: Missing required config', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw configuration error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...v2Destination, + Config: { ...v2Destination.Config, apiKey: null }, + }, + message: { + userId: 'user@1', + type: 'identify', + context: { + traits: userTraits, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Access Token is not present. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Access Token is not present. Aborting', + statTags: { ...expectedStatTags, errorType: 'configuration' }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-4', + name: 'intercom', + description: '[Error - V2 version]: Missing required parameters for an identify call', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + anonymousId: 'anon@2', + type: 'identify', + context: { + traits: userTraits, + }, + integrations: { + INTERCOM: { + lookup: 'phone', + }, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Either email or userId is required for Identify call: Workflow: procWorkflow, Step: identifyPayloadForLatestVersion, ChildStep: undefined, OriginalError: Either email or userId is required for Identify call', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-5', + name: 'intercom', + description: '[Error - V2 version]: Missing required parameters for an identify call', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + anonymousId: 'anon@2', + type: 'identify', + context: { + traits: userTraits, + }, + integrations: { + INTERCOM: { + lookup: 'phone', + }, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Either email or userId is required for Identify call: Workflow: procWorkflow, Step: identifyPayloadForLatestVersion, ChildStep: undefined, OriginalError: Either email or userId is required for Identify call', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-6', + name: 'intercom', + description: + '[Error - V2 version]: Unauthorized error while searching contact for an identify call', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw network error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...v2Destination, + Config: { ...v2Destination.Config, apiKey: 'invalidApiKey' }, + }, + message: { + userId: 'user@3', + type: 'identify', + context: { + traits: { + phone: '+91 9399999999', + email: 'test+3@rudderlabs.com', + firstName: 'Test', + lastName: 'Rudder', + ownerId: '15', + role: 'admin', + source: 'rudder-android-sdk', + }, + }, + integrations: { + INTERCOM: { + lookup: 'email', + }, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '{"message":"{\\"message\\":\\"Unable to search contact due to : [{\\\\\\"code\\\\\\":\\\\\\"unauthorized\\\\\\",\\\\\\"message\\\\\\":\\\\\\"Access Token Invalid\\\\\\"}]: Workflow: procWorkflow, Step: searchContact, ChildStep: undefined, OriginalError: Unable to search contact due to : [{\\\\\\"code\\\\\\":\\\\\\"unauthorized\\\\\\",\\\\\\"message\\\\\\":\\\\\\"Access Token Invalid\\\\\\"}]\\",\\"destinationResponse\\":{\\"response\\":{\\"type\\":\\"error.list\\",\\"request_id\\":\\"request_1\\",\\"errors\\":[{\\"code\\":\\"unauthorized\\",\\"message\\":\\"Access Token Invalid\\"}]},\\"status\\":401}}","destinationResponse":{"response":{"type":"error.list","request_id":"request_1","errors":[{"code":"unauthorized","message":"Access Token Invalid"}]},"status":401}}', + statTags: { ...expectedStatTags, errorCategory: 'network', errorType: 'aborted' }, + statusCode: 401, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-7', + name: 'intercom', + description: '[Error - V2 version]: Track call without event name', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + userId: 'user@3', + type: 'track', + context: { + traits: userTraits, + }, + properties, + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Event name is required for track call: Workflow: procWorkflow, Step: trackPayload, ChildStep: undefined, OriginalError: Event name is required for track call', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-8', + name: 'intercom', + description: '[Error - V2 version]: Group call without groupId', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + userId: 'user@4', + type: 'group', + context: { + traits: { + email: 'test+4@rudderlabs.com', + phone: '+91 9499999999', + firstName: 'John', + lastName: 'Doe', + ownerId: '16', + }, + }, + traits: groupTraits, + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'groupId is required for group call: Workflow: procWorkflow, Step: groupPayloadForLatestVersion, ChildStep: validateMessageAndPreparePayload, OriginalError: groupId is required for group call', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-9', + name: 'intercom', + description: '[Error - V1 version]: Identify call without email and userId', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + type: 'identify', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: userTraits, + }, + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Either of `email` or `userId` is required for Identify call: Workflow: procWorkflow, Step: identifyPayloadForOlderVersion, ChildStep: undefined, OriginalError: Either of `email` or `userId` is required for Identify call', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-10', + name: 'intercom', + description: '[Error - V1 version]: Track call without email or userId', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + type: 'track', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: userTraits, + }, + event: 'Test Event 2', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Either email or userId is required for Track call: Workflow: procWorkflow, Step: trackPayload, ChildStep: undefined, OriginalError: Either email or userId is required for Track call', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; From 2a63f128c562bd527491cdd653ced3f689d73a06 Mon Sep 17 00:00:00 2001 From: ItsSudip Date: Wed, 17 Apr 2024 08:42:57 +0530 Subject: [PATCH 120/240] feat: remove redundant data from traits in hubspot --- src/v0/destinations/hs/HSTransform-v2.js | 2 +- test/integrations/destinations/hs/processor/data.ts | 1 - test/integrations/destinations/hs/router/data.ts | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/v0/destinations/hs/HSTransform-v2.js b/src/v0/destinations/hs/HSTransform-v2.js index 3699e1c789..d2b26f1ab8 100644 --- a/src/v0/destinations/hs/HSTransform-v2.js +++ b/src/v0/destinations/hs/HSTransform-v2.js @@ -110,11 +110,11 @@ const processIdentify = async (message, destination, propertyMap) => { GENERIC_TRUE_VALUES.includes(mappedToDestination.toString()) && operation ) { - addExternalIdToHSTraits(message); if (!objectType) { throw new InstrumentationError('objectType not found'); } if (operation === 'createObject') { + addExternalIdToHSTraits(message); endpoint = CRM_CREATE_UPDATE_ALL_OBJECTS.replace(':objectType', objectType); } else if (operation === 'updateObject' && getHsSearchId(message)) { const { hsSearchId } = getHsSearchId(message); diff --git a/test/integrations/destinations/hs/processor/data.ts b/test/integrations/destinations/hs/processor/data.ts index 0867f2cb54..f503ae92ac 100644 --- a/test/integrations/destinations/hs/processor/data.ts +++ b/test/integrations/destinations/hs/processor/data.ts @@ -1534,7 +1534,6 @@ export const data = [ firstname: 'Test Hubspot', anonymousId: '12345', country: 'India', - email: 'testhubspot2@email.com', }, }, XML: {}, diff --git a/test/integrations/destinations/hs/router/data.ts b/test/integrations/destinations/hs/router/data.ts index e1c3e04356..ab3ca8cba8 100644 --- a/test/integrations/destinations/hs/router/data.ts +++ b/test/integrations/destinations/hs/router/data.ts @@ -1033,7 +1033,6 @@ export const data = [ firstname: 'Test Hubspot', anonymousId: '12345', country: 'India', - email: 'testhubspot2@email.com', }, id: '103605', }, From 099d2295926dadadff435e3c4f011f54e514d3e7 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 25 Apr 2024 08:08:53 +0000 Subject: [PATCH 121/240] chore(release): 1.63.0 --- CHANGELOG.md | 8 ++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87ca4738ec..fff19042bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.63.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.62.2...v1.63.0) (2024-04-25) + + +### Features + +* remove redundant data from traits in hubspot ([2a63f12](https://github.com/rudderlabs/rudder-transformer/commit/2a63f128c562bd527491cdd653ced3f689d73a06)) +* remove redundant data from traits in hubspot ([#3310](https://github.com/rudderlabs/rudder-transformer/issues/3310)) ([4b21f13](https://github.com/rudderlabs/rudder-transformer/commit/4b21f1353d3d9a431a0d5446d019f66a543b977b)) + ### [1.62.2](https://github.com/rudderlabs/rudder-transformer/compare/v1.62.1...v1.62.2) (2024-04-18) diff --git a/package-lock.json b/package-lock.json index b5b413f936..a16f7006bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.62.2", + "version": "1.63.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.62.2", + "version": "1.63.0", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index a291bbab90..6aae041dfd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.62.2", + "version": "1.63.0", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From 4a546d969280bf29f14af4244ea5dd8ad7ecfff9 Mon Sep 17 00:00:00 2001 From: ItsSudip Date: Thu, 25 Apr 2024 13:41:52 +0530 Subject: [PATCH 122/240] chore: update changelog --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fff19042bb..0d3da68413 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,6 @@ All notable changes to this project will be documented in this file. See [standa ### Features -* remove redundant data from traits in hubspot ([2a63f12](https://github.com/rudderlabs/rudder-transformer/commit/2a63f128c562bd527491cdd653ced3f689d73a06)) * remove redundant data from traits in hubspot ([#3310](https://github.com/rudderlabs/rudder-transformer/issues/3310)) ([4b21f13](https://github.com/rudderlabs/rudder-transformer/commit/4b21f1353d3d9a431a0d5446d019f66a543b977b)) ### [1.62.2](https://github.com/rudderlabs/rudder-transformer/compare/v1.62.1...v1.62.2) (2024-04-18) From f06ebde110693fe32f8e450dc395f1f4019defab Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Fri, 26 Apr 2024 13:32:58 +0530 Subject: [PATCH 123/240] fix: algolia enhancement ( adding currency, price, subType and objectData support ) (#3290) * fix: algolia enhancement * fix: bug fix * Apply suggestions from code review Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> * Revert "Apply suggestions from code review" This reverts commit fc3def40eddc86df82bde99076d6409138ddf017. * feat: review comments addressed * feat: review comments addressed --------- Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> --- .../v2/destinations/algolia/procWorkflow.yaml | 24 +- src/v0/destinations/algolia/config.js | 2 + .../algolia/data/AlgoliaTrack.json | 10 + src/v0/destinations/algolia/transform.js | 24 +- src/v0/destinations/algolia/util.js | 19 +- .../destinations/algolia/processor/data.ts | 660 ++++++++++++++++++ 6 files changed, 734 insertions(+), 5 deletions(-) diff --git a/src/cdk/v2/destinations/algolia/procWorkflow.yaml b/src/cdk/v2/destinations/algolia/procWorkflow.yaml index f9ac8e3ae6..87da64aa45 100644 --- a/src/cdk/v2/destinations/algolia/procWorkflow.yaml +++ b/src/cdk/v2/destinations/algolia/procWorkflow.yaml @@ -5,7 +5,10 @@ bindings: - path: ../../../../v0/destinations/algolia/config - name: removeUndefinedAndNullValues path: ../../../../v0/util + - name: isDefinedAndNotNull + path: ../../../../v0/util - path: ../../bindings/jsontemplate + - path: '@rudderstack/integrations-lib' steps: - name: validateInput @@ -24,6 +27,7 @@ steps: let eventTypeMap = $.eventTypeMapping(.destination.Config); let event = .message.event.trim().toLowerCase(); let eventType = .message.properties.eventType ?? eventTypeMap[event]; + let eventSubType = .message.properties.eventSubtype && eventType === 'conversion' && (.message.properties.eventSubtype in $.ALLOWED_EVENT_SUBTYPES) ? .message.properties.eventSubtype; $.assert(eventType, "eventType is mandatory for track call"); let payload = .message.().({ index: .properties.index, @@ -32,12 +36,28 @@ steps: filters: .properties.filters, objectIDs: .properties.objectIds, positions: .properties.positions, + value: $.isDefinedAndNotNull(.properties.currency) ? .properties.value, + currency: .properties.currency, userToken: {{{{$.getGenericPaths("userId", "||")}}}}, eventName: event, - eventType: eventType + eventType: eventType, + eventSubtype: eventSubType }); $.context.payload = $.genericpayloadValidator(payload); + - name: prepareObjectDataBlock + condition: $.context.payload.eventType === "conversion" && $.isDefinedAndNotNull(^.message.properties.products) && Array.isArray(^.message.properties.products) + description: | + Populate list of objectData + template: | + const products = ^.message.properties.products + products.($.removeUndefinedAndNullValues({ + "queryID" : $.isDefinedAndNotNull(.queryID) ? String(.queryID) : null, + "price": $.isDefinedAndNotNull(.price) && $.isDefinedAndNotNull(^.message.properties.currency) ? String(.price) : null, + "quantity": $.isDefinedAndNotNull(.quantity)? Number(.quantity) : null, + "discount": $.isDefinedAndNotNull(.discount) ? String(.discount) : null + }))[] + - name: populateProductsData condition: | .message.properties.products && @@ -55,11 +75,13 @@ steps: const products = .message.properties.products; const objectIDs = ~r products.objectId; $.context.payload.objectIDs = Array.isArray(objectIDs) ? objectIDs[:20]:$.context.payload.objectIDs; + $.context.payload.objectData = $.outputs.prepareObjectDataBlock - name: validateDestPayload template: | const filters = $.context.payload.filters; const objectIDs = $.context.payload.objectIDs; + const objectData = $.context.payload.objectData; $.assert(!(filters && objectIDs), "event can't have both objectIds and filters at the same time."); $.assert(filters.length || objectIDs.length, "Either filters or objectIds is required and must be non empty."); diff --git a/src/v0/destinations/algolia/config.js b/src/v0/destinations/algolia/config.js index 11b4ec99f2..4e20294dd2 100644 --- a/src/v0/destinations/algolia/config.js +++ b/src/v0/destinations/algolia/config.js @@ -5,6 +5,7 @@ const CONFIG_CATEGORIES = { TRACK: { type: 'track', name: 'AlgoliaTrack' }, }; const EVENT_TYPES = ['click', 'view', 'conversion']; +const ALLOWED_EVENT_SUBTYPES = ['addToCart', 'purchase']; const MAX_BATCH_SIZE = 1000; const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); module.exports = { @@ -12,4 +13,5 @@ module.exports = { MAX_BATCH_SIZE, EVENT_TYPES, trackMapping: MAPPING_CONFIG[CONFIG_CATEGORIES.TRACK.name], + ALLOWED_EVENT_SUBTYPES, }; diff --git a/src/v0/destinations/algolia/data/AlgoliaTrack.json b/src/v0/destinations/algolia/data/AlgoliaTrack.json index bdc3449147..41f43af9cb 100644 --- a/src/v0/destinations/algolia/data/AlgoliaTrack.json +++ b/src/v0/destinations/algolia/data/AlgoliaTrack.json @@ -34,5 +34,15 @@ "destKey": "positions", "sourceKeys": "properties.positions", "required": false + }, + { + "destKey": "value", + "sourceKeys": "properties.value", + "required": false + }, + { + "destKey": "currency", + "sourceKeys": "properties.currency", + "required": false } ] diff --git a/src/v0/destinations/algolia/transform.js b/src/v0/destinations/algolia/transform.js index 8e9cd57e8b..33ae6f2101 100644 --- a/src/v0/destinations/algolia/transform.js +++ b/src/v0/destinations/algolia/transform.js @@ -16,7 +16,7 @@ const { handleRtTfSingleEventError, } = require('../../util/index'); -const { ENDPOINT, MAX_BATCH_SIZE, trackMapping } = require('./config'); +const { ENDPOINT, MAX_BATCH_SIZE, trackMapping, ALLOWED_EVENT_SUBTYPES } = require('./config'); const { genericpayloadValidator, @@ -38,6 +38,12 @@ const trackResponseBuilder = (message, { Config }) => { const eventMapping = eventTypeMapping(Config); payload.eventName = event; payload.eventType = getValueFromMessage(message, 'properties.eventType') || eventMapping[event]; + if ( + payload.eventType === 'conversion' && + ALLOWED_EVENT_SUBTYPES.includes(getValueFromMessage(message, 'properties.eventSubtype')) + ) { + payload.eventSubtype = getValueFromMessage(message, 'properties.eventSubtype'); + } if (!payload.eventType) { throw new InstrumentationError('eventType is mandatory for track call'); @@ -47,9 +53,13 @@ const trackResponseBuilder = (message, { Config }) => { if (event === 'product list viewed' || event === 'order completed') { const products = getValueFromMessage(message, 'properties.products'); if (products) { - const { objectList, positionList } = createObjectArray(products, payload.eventType); + const { objectList, positionList, objectData } = createObjectArray( + products, + payload.eventType, + ); const objLen = objectList.length; const posLen = positionList.length; + const objDataLen = objectData.length; if (objLen > 0) { payload.objectIDs = objectList; payload.objectIDs.splice(20); @@ -58,10 +68,20 @@ const trackResponseBuilder = (message, { Config }) => { payload.positions = positionList; payload.positions.splice(20); } + + if (objDataLen > 0) { + payload.objectData = objectData; + } // making size of object list and position list equal if (posLen > 0 && objLen > 0 && posLen !== objLen) { throw new InstrumentationError('length of objectId and position should be equal'); } + + if (objDataLen > 0 && objLen > 0 && objDataLen !== objLen) { + throw new InstrumentationError( + 'length of objectId and properties.products array should be equal', + ); + } } } // for all events either filter or objectID should be there diff --git a/src/v0/destinations/algolia/util.js b/src/v0/destinations/algolia/util.js index eddb4dc16d..863c23aba7 100644 --- a/src/v0/destinations/algolia/util.js +++ b/src/v0/destinations/algolia/util.js @@ -1,4 +1,8 @@ -const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { + InstrumentationError, + isDefined, + removeUndefinedAndNullValues, +} = require('@rudderstack/integrations-lib'); const logger = require('../../../logger'); const { EVENT_TYPES } = require('./config'); @@ -66,6 +70,8 @@ const genericpayloadValidator = (payload) => { const createObjectArray = (objects, eventType) => { const objectList = []; const positionList = []; + // eslint-disable-next-line sonarjs/no-unused-collection + const objectData = []; if (objects.length > 0) { objects.forEach((object, index) => { if (object.objectId) { @@ -80,13 +86,22 @@ const createObjectArray = (objects, eventType) => { } } else { objectList.push(object.objectId); + if (eventType === 'conversion') { + const singleObjData = { + queryID: isDefined(object.queryID) ? `${object.queryID}` : null, + price: isDefined(object.price) ? `${object.price}` : null, + quantity: object.quantity, + discount: isDefined(object.discount) ? `${object.discount}` : null, + }; + objectData.push(removeUndefinedAndNullValues(singleObjData)); + } } } else { logger.error(`object at index ${index} dropped. objectId is required.`); } }); } - return { objectList, positionList }; + return { objectList, positionList, objectData }; }; const clickPayloadValidator = (payload) => { diff --git a/test/integrations/destinations/algolia/processor/data.ts b/test/integrations/destinations/algolia/processor/data.ts index 7c37c9642a..a8dd31b51a 100644 --- a/test/integrations/destinations/algolia/processor/data.ts +++ b/test/integrations/destinations/algolia/processor/data.ts @@ -1627,4 +1627,664 @@ export const data = [ }, }, }, + { + name: 'algolia', + description: + 'For conversion event including product array and subtype addToCart, object data is sent', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + page: { + path: '/destinations/ometria', + referrer: '', + search: '', + title: '', + url: 'https://docs.rudderstack.com/destinations/ometria', + category: 'destination', + initial_referrer: 'https://docs.rudderstack.com', + initial_referring_domain: 'docs.rudderstack.com', + }, + }, + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + properties: { + index: 'products', + eventSubtype: 'addToCart', + products: [ + { + objectId: 'ecommerce-sample-data-919', + position: 7, + quantity: '2', + price: 10, + queryID: '123', + discount: '10', + }, + { + objectId: '9780439784542', + position: 8, + quantity: '3', + price: 30, + queryID: '123', + discount: '10', + }, + ], + queryId: '43b15df305339e827f0ac0bdc5ebcaa7', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'product list viewed', + to: 'conversion', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + JSON: { + events: [ + { + index: 'products', + queryID: '43b15df305339e827f0ac0bdc5ebcaa7', + objectIDs: ['ecommerce-sample-data-919', '9780439784542'], + userToken: 'testuserId1', + eventName: 'product list viewed', + eventSubtype: 'addToCart', + eventType: 'conversion', + objectData: [ + { + quantity: 2, + queryID: '123', + discount: '10', + }, + { + quantity: 3, + queryID: '123', + discount: '10', + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'algolia', + description: + 'For conversion event including product array and subtype purchase, object data is sent', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + page: { + path: '/destinations/ometria', + referrer: '', + search: '', + title: '', + url: 'https://docs.rudderstack.com/destinations/ometria', + category: 'destination', + initial_referrer: 'https://docs.rudderstack.com', + initial_referring_domain: 'docs.rudderstack.com', + }, + }, + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + properties: { + index: 'products', + eventSubtype: 'purchase', + products: [ + { + objectId: 'ecommerce-sample-data-919', + position: 7, + quantity: '2', + price: 10, + queryID: '123', + discount: '10', + }, + { + objectId: '9780439784542', + position: 8, + quantity: '3', + price: 30, + queryID: '123', + discount: '10', + }, + ], + queryId: '43b15df305339e827f0ac0bdc5ebcaa7', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'product list viewed', + to: 'conversion', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + JSON: { + events: [ + { + index: 'products', + queryID: '43b15df305339e827f0ac0bdc5ebcaa7', + objectIDs: ['ecommerce-sample-data-919', '9780439784542'], + userToken: 'testuserId1', + eventName: 'product list viewed', + eventSubtype: 'purchase', + eventType: 'conversion', + objectData: [ + { + quantity: 2, + queryID: '123', + discount: '10', + }, + { + quantity: 3, + queryID: '123', + discount: '10', + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'algolia', + description: + 'For conversion event including product array and subtype wrong, object data is sent but subType is omitted', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + page: { + path: '/destinations/ometria', + referrer: '', + search: '', + title: '', + url: 'https://docs.rudderstack.com/destinations/ometria', + category: 'destination', + initial_referrer: 'https://docs.rudderstack.com', + initial_referring_domain: 'docs.rudderstack.com', + }, + }, + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + properties: { + index: 'products', + eventSubtype: 'random', + value: 10, + currency: 'USD', + products: [ + { + objectId: 'ecommerce-sample-data-919', + position: 7, + quantity: '2', + queryID: '123', + discount: '10', + price: 10, + }, + { + objectId: '9780439784542', + position: 8, + quantity: '3', + queryID: '123', + discount: '10', + price: 10, + }, + ], + queryId: '43b15df305339e827f0ac0bdc5ebcaa7', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'product list viewed', + to: 'conversion', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + JSON: { + events: [ + { + index: 'products', + queryID: '43b15df305339e827f0ac0bdc5ebcaa7', + objectIDs: ['ecommerce-sample-data-919', '9780439784542'], + userToken: 'testuserId1', + eventName: 'product list viewed', + eventType: 'conversion', + value: 10, + currency: 'USD', + objectData: [ + { + price: '10', + quantity: 2, + queryID: '123', + discount: '10', + }, + { + price: '10', + quantity: 3, + queryID: '123', + discount: '10', + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'algolia', + description: + 'For conversion event without including product array and subtype purchase, object data is not sent but subType is sent', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + page: { + path: '/destinations/ometria', + referrer: '', + search: '', + title: '', + url: 'https://docs.rudderstack.com/destinations/ometria', + category: 'destination', + initial_referrer: 'https://docs.rudderstack.com', + initial_referring_domain: 'docs.rudderstack.com', + }, + }, + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + properties: { + index: 'products', + eventSubtype: 'purchase', + filters: ['field1:hello', 'val1:val2'], + queryId: '43b15df305339e827f0ac0bdc5ebcaa7', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'product list viewed', + to: 'conversion', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + JSON: { + events: [ + { + index: 'products', + queryID: '43b15df305339e827f0ac0bdc5ebcaa7', + filters: ['field1:hello', 'val1:val2'], + userToken: 'testuserId1', + eventName: 'product list viewed', + eventType: 'conversion', + eventSubtype: 'purchase', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, ]; From 54eca3220ea48fae64c655813fe4430dd704639e Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Fri, 26 Apr 2024 18:14:15 +0530 Subject: [PATCH 124/240] fix: send content_ids as a string if there is only one value (#3317) --- .../facebook_conversions/utils.js | 2 +- .../facebook_conversions/processor/data.ts | 121 ++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/src/v0/destinations/facebook_conversions/utils.js b/src/v0/destinations/facebook_conversions/utils.js index c6e3993e33..87fb0ea606 100644 --- a/src/v0/destinations/facebook_conversions/utils.js +++ b/src/v0/destinations/facebook_conversions/utils.js @@ -134,7 +134,7 @@ const populateCustomDataBasedOnCategory = (customData, message, category, catego const { contentIds, contents } = populateContentsAndContentIDs([message.properties]); eventTypeCustomData = { ...eventTypeCustomData, - content_ids: contentIds, + content_ids: contentIds.length === 1 ? contentIds[0] : contentIds, contents, content_type: contentType, content_category: getContentCategory(contentCategory), diff --git a/test/integrations/destinations/facebook_conversions/processor/data.ts b/test/integrations/destinations/facebook_conversions/processor/data.ts index 6eb90942a7..bfa35bc22b 100644 --- a/test/integrations/destinations/facebook_conversions/processor/data.ts +++ b/test/integrations/destinations/facebook_conversions/processor/data.ts @@ -1534,4 +1534,125 @@ export const data = [ }, mockFns: defaultMockFns, }, + { + name: 'facebook_conversions', + description: 'Track event with standard event product added with content_ids', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + channel: 'web', + context: { + device: { + id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', + manufacturer: 'Xiaomi', + model: 'Redmi 6', + name: 'xiaomi', + }, + network: { + carrier: 'Banglalink', + }, + os: { + name: 'android', + version: '8.1.0', + }, + screen: { + height: '100', + density: 50, + }, + traits: { + email: ' aBc@gmail.com ', + address: { + zip: 1234, + }, + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + }, + }, + event: 'product added', + integrations: { + All: true, + }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { + revenue: 400, + additional_bet_index: 0, + id: '452345234', + quantity: 5, + }, + timestamp: '2023-11-12T15:46:51.693229+05:30', + type: 'track', + }, + destination: { + Config: { + limitedDataUsage: true, + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + datasetId: 'dummyID', + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + removeExternalId: true, + whitelistPiiProperties: [ + { + whitelistPiiProperties: '', + }, + ], + actionSource: 'website', + }, + Enabled: true, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/dummyID/events?access_token=09876', + headers: {}, + params: {}, + body: { + JSON: {}, + XML: {}, + JSON_ARRAY: {}, + FORM: { + data: [ + '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"AddToCart","event_time":1699784211,"action_source":"website","custom_data":{"revenue":400,"additional_bet_index":0,"id":"452345234","quantity":5,"content_ids":"452345234","contents":[{"id":"452345234","quantity":5}],"content_type":"product","currency":"USD","value":400}}', + ], + }, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, ]; From ea655bfcfd8343180477a21136e2da2447fbcf49 Mon Sep 17 00:00:00 2001 From: gitcommitshow Date: Sat, 27 Apr 2024 22:23:52 +0530 Subject: [PATCH 125/240] test: fix missing brackets --- test/integrations/sources/slack/data.ts | 130 ++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 test/integrations/sources/slack/data.ts diff --git a/test/integrations/sources/slack/data.ts b/test/integrations/sources/slack/data.ts new file mode 100644 index 0000000000..c565c573d1 --- /dev/null +++ b/test/integrations/sources/slack/data.ts @@ -0,0 +1,130 @@ +export const data = [ + { + name: 'slack', + description: 'Webhook url verificatin event', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + token: 'Jhj5dZrVaK7ZwHHjRyZWjbDl', + challenge: '3eZbrw1aB10FEMAGAZd4FyFQ', + type: 'url_verification', + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + outputToSource: { + body: 'eyJjaGFsbGVuZ2UiOiIzZVpicncxYUIxMEZFTUFHQVpkNEZ5RlEifQ==', + contentType: 'application/json', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'slack', + description: 'Team joined event', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + event: { + type: 'team_join', + user: { + id: 'W012CDE', + name: 'johnd', + real_name: 'John Doe', + }, + }, + type: 'event_callback', + event_id: 'Ev06TJ0NG5', + event_time: 1709441309, + token: 'REm276ggfh72Lq', + team_id: 'T0GFJL5J7', + context_team_id: 'T0GFJL5J7', + context_enterprise_id: null, + api_app_id: 'B02SJMHRR', + authorizations: [ + { + enterprise_id: null, + team_id: 'T0GFJL5J7', + user_id: 'U04G7H550', + is_bot: true, + is_enterprise_install: false, + }, + ], + is_ext_shared_channel: false, + event_context: 'eJldCI65436EUEpMSFhgfhg76joiQzAxRTRQTEIxMzUifQ', + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + integration: { + name: 'SLACK', + }, + externalId: [ + { + type: 'slackUserId', + id: 'W012CDE', + }, + ], + }, + integrations: { + SLACK: false, + }, + type: 'identify', + event: 'Team Join', + anonymousId: '2bc5ae2825a712d3d154cbdacb86ac16c278', + originalTimestamp: '2024-03-03T04:48:29.000Z', + sentAt: '2024-03-03T04:48:29.000Z', + properties: { + type: 'team_join', + user: { + id: 'W012CDE', + name: 'johnd', + real_name: 'John Doe', + }, + }, + }, + ], + }, + }, + ], + }, + }, + }, +]; From 8f79f53d30326e07fc92dd624e799015ff9f87c2 Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Mon, 29 Apr 2024 10:14:10 +0530 Subject: [PATCH 126/240] feat: onboard Yandex Metrica Offline Events Destination (#3232) * feat: onboard yandex metrica offline events destination, initial changes * chore: update id logic, add tests * chore: address commentsx1 * chore: add validations and tests * chore: address commentsx2 * chore: add validations for tiemstamp and id * chore: add null check for DateTime * chore: add null error throw for DateTime --------- Co-authored-by: Sai Sankeerth --- .../yandex_metrica_offline_events/config.js | 5 + .../procWorkflow.yaml | 36 + .../yandex_metrica_offline_events/utils.js | 51 ++ .../processor/data.ts | 746 ++++++++++++++++++ 4 files changed, 838 insertions(+) create mode 100644 src/cdk/v2/destinations/yandex_metrica_offline_events/config.js create mode 100644 src/cdk/v2/destinations/yandex_metrica_offline_events/procWorkflow.yaml create mode 100644 src/cdk/v2/destinations/yandex_metrica_offline_events/utils.js create mode 100644 test/integrations/destinations/yandex_metrica_offline_events/processor/data.ts diff --git a/src/cdk/v2/destinations/yandex_metrica_offline_events/config.js b/src/cdk/v2/destinations/yandex_metrica_offline_events/config.js new file mode 100644 index 0000000000..83513c3856 --- /dev/null +++ b/src/cdk/v2/destinations/yandex_metrica_offline_events/config.js @@ -0,0 +1,5 @@ +const YANDEX_METRICA_OFFLINE_EVENTS = 'yandex_metrica_offline_events'; + +module.exports = { + YANDEX_METRICA_OFFLINE_EVENTS, +}; diff --git a/src/cdk/v2/destinations/yandex_metrica_offline_events/procWorkflow.yaml b/src/cdk/v2/destinations/yandex_metrica_offline_events/procWorkflow.yaml new file mode 100644 index 0000000000..690bc399ee --- /dev/null +++ b/src/cdk/v2/destinations/yandex_metrica_offline_events/procWorkflow.yaml @@ -0,0 +1,36 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + exportAll: true + - path: ./config + - name: removeUndefinedAndNullValues + path: ../../../../v0/util + - name: defaultRequestConfig + path: ../../../../v0/util + - path: ./utils + +steps: + - name: validateInput + template: | + let messageType = .message.type; + $.assert(messageType, "message Type is not present. Aborting message."); + $.assert(.message.type.toLowerCase() ==='identify', "Event type " + .message.type.toLowerCase() + " is not supported. Aborting message."); + $.assert(.message.traits || .message.properties, "Message traits/properties not present. Aborting message."); + + - name: prepareData + template: | + let data = .message.traits + let identifierType = .message.context.externalId[0].identifierType; + let identifierValue = .message.context.externalId[0].id; + identifierValue = String(identifierValue); + data = $.setIdentifier(data, identifierType, identifierValue) + data = $.validateData(data) + data + + - name: buildResponseForProcessTransformation + description: build response + template: | + const response = $.defaultRequestConfig(); + response.body.JSON = $.outputs.prepareData + response diff --git a/src/cdk/v2/destinations/yandex_metrica_offline_events/utils.js b/src/cdk/v2/destinations/yandex_metrica_offline_events/utils.js new file mode 100644 index 0000000000..032b0b636d --- /dev/null +++ b/src/cdk/v2/destinations/yandex_metrica_offline_events/utils.js @@ -0,0 +1,51 @@ +/* eslint-disable no-param-reassign */ +const { InstrumentationError, isDefinedNotNullNotEmpty } = require('@rudderstack/integrations-lib'); +const moment = require('moment'); + +const setIdentifier = (data, identifierType, identifierValue) => { + const updatedData = data; + if (identifierType === 'ClientId') { + updatedData.ClientId = identifierValue; + } else if (identifierType === 'YCLID') { + updatedData.Yclid = identifierValue; + } else if (identifierType === 'UserId') { + updatedData.UserId = identifierValue; + } else { + throw new InstrumentationError( + 'Invalid identifier type passed in external Id. Valid types are ClientId, YCLID, UserId. Aborting!', + ); + } + return updatedData; +}; + +function isUnixTimestamp(datetime) { + if (moment.unix(datetime).isValid()) { + return datetime; + } + const unixTimestamp = moment(datetime).unix(); + if (moment.unix(unixTimestamp).isValid()) { + return unixTimestamp; + } + throw new InstrumentationError('Invalid timestamp. Aborting!'); +} + +const validateData = (data) => { + const { Price, DateTime } = data; + if (!isDefinedNotNullNotEmpty(data)) { + throw new InstrumentationError('No traits found in the payload. Aborting!'); + } + if (Price && typeof Price !== 'number') { + throw new InstrumentationError('Price can only be a numerical value. Aborting!'); + } + if (!isDefinedNotNullNotEmpty(DateTime)) { + throw new InstrumentationError('DateTime cannot be empty. Aborting!'); + } + data.DateTime = String(isUnixTimestamp(DateTime)); + return data; +}; + +module.exports = { + setIdentifier, + validateData, + isUnixTimestamp, +}; diff --git a/test/integrations/destinations/yandex_metrica_offline_events/processor/data.ts b/test/integrations/destinations/yandex_metrica_offline_events/processor/data.ts new file mode 100644 index 0000000000..4bb27bdf57 --- /dev/null +++ b/test/integrations/destinations/yandex_metrica_offline_events/processor/data.ts @@ -0,0 +1,746 @@ +export const data = [ + { + name: 'yandex_metrica_offline_events', + description: 'Successful identify event with YCLID identifier type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: 100, + Target: 'GOAL1', + Currency: 'RUB', + DateTime: '1481718166', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'YCLID', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + Currency: 'RUB', + DateTime: '1481718166', + Price: 100, + Target: 'GOAL1', + Yclid: '133591247640966458', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'yandex_metrica_offline_events', + description: 'Successful identify event with ClientId identifier type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: 100, + Target: 'GOAL1', + Currency: 'RUB', + DateTime: '1481718166', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'ClientId', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + Currency: 'RUB', + DateTime: '1481718166', + Price: 100, + Target: 'GOAL1', + ClientId: '133591247640966458', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'yandex_metrica_offline_events', + description: 'Successful identify event with UserId identifier type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: 100, + Target: 'GOAL1', + Currency: 'RUB', + DateTime: '1481718166', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'UserId', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + Currency: 'RUB', + DateTime: '1481718166', + Price: 100, + Target: 'GOAL1', + UserId: '133591247640966458', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'yandex_metrica_offline_events', + description: 'Failed identify event with Price passed as string', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: '100', + Target: 'GOAL1', + Currency: 'RUB', + DateTime: '1481718166', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'UserId', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'Price can only be a numerical value. Aborting!: Workflow: procWorkflow, Step: prepareData, ChildStep: undefined, OriginalError: Price can only be a numerical value. Aborting!', + statTags: { + destType: 'YANDEX_METRICA_OFFLINE_EVENTS', + destinationId: 'destId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + workspaceId: 'wspId', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'yandex_metrica_offline_events', + description: 'Failed identify event with invalid identifier type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: 100, + Target: 'GOAL1', + Currency: 'RUB', + DateTime: '1481718166', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'InvalidIdentifierType', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'Invalid identifier type passed in external Id. Valid types are ClientId, YCLID, UserId. Aborting!: Workflow: procWorkflow, Step: prepareData, ChildStep: undefined, OriginalError: Invalid identifier type passed in external Id. Valid types are ClientId, YCLID, UserId. Aborting!', + statTags: { + destType: 'YANDEX_METRICA_OFFLINE_EVENTS', + destinationId: 'destId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + workspaceId: 'wspId', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'yandex_metrica_offline_events', + description: 'Failed identify event with invalid timestamp', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: 100, + Target: 'GOAL1', + Currency: 'RUB', + DateTime: 'invalidTimestamp', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'ClientId', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'Invalid timestamp. Aborting!: Workflow: procWorkflow, Step: prepareData, ChildStep: undefined, OriginalError: Invalid timestamp. Aborting!', + statTags: { + destType: 'YANDEX_METRICA_OFFLINE_EVENTS', + destinationId: 'destId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + workspaceId: 'wspId', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'yandex_metrica_offline_events', + description: 'Successful identify event with non unix timestamp', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: 100, + Target: 'GOAL1', + Currency: 'RUB', + DateTime: '2023-08-14T05:30:30.118Z', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'YCLID', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + Currency: 'RUB', + DateTime: '1691991030', + Price: 100, + Target: 'GOAL1', + Yclid: '133591247640966458', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'yandex_metrica_offline_events', + description: 'Failed identify event with null or empty timestamp', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: 100, + Target: 'GOAL1', + Currency: 'RUB', + DateTime: '', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'ClientId', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'DateTime cannot be empty. Aborting!: Workflow: procWorkflow, Step: prepareData, ChildStep: undefined, OriginalError: DateTime cannot be empty. Aborting!', + statTags: { + destType: 'YANDEX_METRICA_OFFLINE_EVENTS', + destinationId: 'destId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + workspaceId: 'wspId', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, +]; From 7b1d11b306f3fa3a5698061c31acfbf53bc78dd1 Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:01:53 +0530 Subject: [PATCH 127/240] feat(refiner): component test refactor (#3221) --- .../destinations/refiner/processor/data.ts | 677 +----------------- .../refiner/processor/groupTestData.ts | 91 +++ .../refiner/processor/identifyTestData.ts | 114 +++ .../refiner/processor/pageTestData.ts | 99 +++ .../refiner/processor/trackTestData.ts | 109 +++ .../refiner/processor/validationTestData.ts | 227 ++++++ 6 files changed, 651 insertions(+), 666 deletions(-) create mode 100644 test/integrations/destinations/refiner/processor/groupTestData.ts create mode 100644 test/integrations/destinations/refiner/processor/identifyTestData.ts create mode 100644 test/integrations/destinations/refiner/processor/pageTestData.ts create mode 100644 test/integrations/destinations/refiner/processor/trackTestData.ts create mode 100644 test/integrations/destinations/refiner/processor/validationTestData.ts diff --git a/test/integrations/destinations/refiner/processor/data.ts b/test/integrations/destinations/refiner/processor/data.ts index c851e9d7ce..b09ea234db 100644 --- a/test/integrations/destinations/refiner/processor/data.ts +++ b/test/integrations/destinations/refiner/processor/data.ts @@ -1,668 +1,13 @@ +import { identifyTestData } from './identifyTestData'; +import { trackTestData } from './trackTestData'; +import { pageTestData } from './pageTestData'; +import { groupTestData } from './groupTestData'; +import { validationTestData } from './validationTestData'; + export const data = [ - { - name: 'refiner', - description: 'No Message type', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - sentAt: '2022-10-11T13:10:54.877+05:30', - userId: 'user@45', - context: { - traits: { - age: '30', - city: 'Banglore', - email: 'test@user.com', - phone: '9876543210', - address: { city: 'ahmedabad', state: 'india' }, - lastName: 'user', - username: 'testUser', - firstName: 'test', - userCountry: 'india', - }, - }, - rudderId: 'caae04c5-959f-467b-a293-86f6c62d59e6', - messageId: 'b6ce7f31-5d76-4240-94d2-3eea020ef791', - timestamp: '2022-10-11T13:10:52.137+05:30', - receivedAt: '2022-10-11T13:10:52.138+05:30', - request_ip: '[::1]', - originalTimestamp: '2022-10-11T13:10:54.877+05:30', - }, - destination: { - Config: { - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665474171943, - eventFilteringOption: 'disable', - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Event type is required', - statTags: { - destType: 'REFINER', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'refiner', - description: 'Unsupported Event type ', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'alias', - sentAt: '2022-10-11T13:10:54.877+05:30', - userId: 'user@45', - context: { - traits: { - age: '30', - city: 'Banglore', - email: 'test@user.com', - phone: '9876543210', - address: { city: 'ahmedabad', state: 'india' }, - lastName: 'user', - username: 'testUser', - firstName: 'test', - userCountry: 'india', - }, - }, - rudderId: 'caae04c5-959f-467b-a293-86f6c62d59e6', - messageId: 'b6ce7f31-5d76-4240-94d2-3eea020ef791', - timestamp: '2022-10-11T13:10:52.137+05:30', - receivedAt: '2022-10-11T13:10:52.138+05:30', - request_ip: '[::1]', - originalTimestamp: '2022-10-11T13:10:54.877+05:30', - }, - destination: { - Config: { - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665474171943, - eventFilteringOption: 'disable', - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Event type "alias" is not supported', - statTags: { - destType: 'REFINER', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'refiner', - description: 'userId and email is not present', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'identify', - sentAt: '2022-10-11T13:10:54.877+05:30', - context: { - traits: { - age: '30', - city: 'Banglore', - phone: '9876543210', - address: { city: 'ahmedabad', state: 'india' }, - lastName: 'user', - username: 'testUser', - firstName: 'test', - userCountry: 'india', - }, - }, - rudderId: 'caae04c5-959f-467b-a293-86f6c62d59e6', - messageId: 'b6ce7f31-5d76-4240-94d2-3eea020ef791', - timestamp: '2022-10-11T13:10:52.137+05:30', - receivedAt: '2022-10-11T13:10:52.138+05:30', - request_ip: '[::1]', - originalTimestamp: '2022-10-11T13:10:54.877+05:30', - }, - destination: { - Config: { - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665474171943, - eventFilteringOption: 'disable', - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'At least one of `userId` or `email` is required', - statTags: { - destType: 'REFINER', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'refiner', - description: 'event name is not present', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.1.2', - }, - traits: { - age: '30', - email: 'test@user.com', - phone: '9876543210', - city: 'Banglore', - userCountry: 'india', - lastName: 'user', - username: 'testUser', - firstName: 'test', - }, - library: { name: 'RudderLabs JavaScript SDK', version: '1.1.2' }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36', - locale: 'en-GB', - os: { name: '', version: '' }, - screen: { density: 2 }, - page: { - path: '/tests/html/ecomm_test.html', - referrer: 'http://0.0.0.0:1112/tests/html/', - search: '', - title: 'Fb Offline Conversion Ecommerce Test', - url: 'http://0.0.0.0:1112/tests/html/ecomm_test.html', - }, - }, - type: 'track', - messageId: '9116b734-7e6b-4497-ab51-c16744d4487e', - userId: 'user@45', - properties: { - order_id: '5241735', - coupon: 'APPARELSALE', - currency: 'IND', - products: [ - { id: 'product-bacon-jam', category: 'Merch', brand: '' }, - { id: 'product-t-shirt', category: 'Merch', brand: 'Levis' }, - { id: 'offer-t-shirt', category: 'Merch', brand: 'Levis' }, - ], - }, - }, - destination: { - Config: { - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665474171943, - eventFilteringOption: 'disable', - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Event name is required', - statTags: { - destType: 'REFINER', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'refiner', - description: 'successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'identify', - sentAt: '2022-10-11T13:10:54.877+05:30', - userId: 'user@45', - context: { - traits: { - age: '30', - city: 'Banglore', - email: 'test@user.com', - phone: '9876543210', - address: { city: 'ahmedabad', state: 'india' }, - lastName: 'user', - username: 'testUser', - firstName: 'test', - userCountry: 'india', - }, - }, - rudderId: 'caae04c5-959f-467b-a293-86f6c62d59e6', - messageId: 'b6ce7f31-5d76-4240-94d2-3eea020ef791', - timestamp: '2022-10-11T13:10:52.137+05:30', - receivedAt: '2022-10-11T13:10:52.138+05:30', - request_ip: '[::1]', - originalTimestamp: '2022-10-11T13:10:54.877+05:30', - }, - destination: { - Config: { - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665475307930, - eventFilteringOption: 'disable', - userAttributesMapping: [{ from: 'address', to: 'userAddress' }], - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.refiner.io/v1/identify-user', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Authorization: 'Bearer dummyApiKey', - }, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - age: '30', - city: 'Banglore', - email: 'test@user.com', - phone: '9876543210', - userId: 'user@45', - lastName: 'user', - username: 'testUser', - firstName: 'test', - userAddress: { city: 'ahmedabad', state: 'india' }, - userCountry: 'india', - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'refiner', - description: 'successful track call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'track', - event: 'Product Searched', - sentAt: '2022-10-11T13:38:31.827+05:30', - userId: 'user@45', - channel: 'web', - context: { - os: { name: '', version: '' }, - app: { - name: 'RudderLabs JavaScript SDK', - build: '1.0.0', - version: '1.1.2', - namespace: 'com.rudderlabs.javascript', - }, - page: { - url: 'http://0.0.0.0:1112/tests/html/ecomm_test.html', - path: '/tests/html/ecomm_test.html', - title: 'Fb Offline Conversion Ecommerce Test', - search: '', - referrer: 'http://0.0.0.0:1112/tests/html/', - }, - locale: 'en-GB', - screen: { density: 2 }, - traits: { - age: '30', - city: 'Banglore', - email: 'test@user.com', - phone: '9876543210', - lastName: 'user', - username: 'testUser', - firstName: 'test', - userCountry: 'india', - }, - library: { name: 'RudderLabs JavaScript SDK', version: '1.1.2' }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36', - }, - rudderId: 'caae04c5-959f-467b-a293-86f6c62d59e6', - messageId: '9116b734-7e6b-4497-ab51-c16744d4487e', - timestamp: '2022-10-11T13:38:29.177+05:30', - properties: { - coupon: 'APPARELSALE', - currency: 'IND', - order_id: '5241735', - products: [ - { id: 'product-bacon-jam', category: 'Merch', brand: '' }, - { id: 'product-t-shirt', category: 'Merch', brand: 'Levis' }, - { id: 'offer-t-shirt', category: 'Merch', brand: 'Levis' }, - ], - }, - receivedAt: '2022-10-11T13:38:29.178+05:30', - request_ip: '[::1]', - originalTimestamp: '2022-10-11T13:38:31.827+05:30', - }, - destination: { - Config: { - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665475307930, - eventFilteringOption: 'disable', - userAttributesMapping: [{ from: 'address', to: 'userAddress' }], - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: { id: 'user@45', email: 'test@user.com', event: 'Product Searched' }, - JSON: {}, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: {}, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Authorization: 'Bearer dummyApiKey', - }, - version: '1', - endpoint: 'https://api.refiner.io/v1/track', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'refiner', - description: 'successful group call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'group', - sentAt: '2015-02-23T22:28:55.111Z', - traits: { name: 'rudder ventures', email: 'business@rudderstack.com' }, - userId: 'test@12', - channel: 'browser', - context: { - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36', - }, - groupId: 'group@123', - rudderId: 'd944be7a-c870-41ba-9fa5-f3c9dbf5f7e0', - messageId: '022bb90c-bbac-11e4-8dfc-aa07a5b093db', - request_ip: '[::1]', - integrations: { All: true }, - originalTimestamp: '2022-10-11T13:51:00.906+05:30', - }, - destination: { - Config: { - accountAttributesMapping: [{ from: 'email', to: 'businessEmail' }], - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665476456112, - eventFilteringOption: 'disable', - userAttributesMapping: [{ from: 'address', to: 'userAddress' }], - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.refiner.io/v1/identify-user', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Authorization: 'Bearer dummyApiKey', - }, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - id: 'test@12', - 'account[businessEmail]': 'business@rudderstack.com', - 'account[id]': 'group@123', - 'account[name]': 'rudder ventures', - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'refiner', - description: 'Refiner page call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { name: '', version: '' }, - screen: { density: 2 }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - timestamp: '2019-09-01T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: 'xyz', - search: 'def', - title: 'ghi', - url: 'jkl', - }, - integrations: { All: true }, - name: 'pageviewed', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - accountAttributesMapping: [{ from: 'email', to: 'businessEmail' }], - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665476456112, - eventFilteringOption: 'disable', - userAttributesMapping: [{ from: 'address', to: 'userAddress' }], - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: { id: '12345', event: 'Viewed pageviewed Page' }, - JSON: {}, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: {}, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Authorization: 'Bearer dummyApiKey', - }, - version: '1', - endpoint: 'https://api.refiner.io/v1/track', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, + ...identifyTestData, + ...trackTestData, + ...pageTestData, + ...groupTestData, + ...validationTestData, ]; diff --git a/test/integrations/destinations/refiner/processor/groupTestData.ts b/test/integrations/destinations/refiner/processor/groupTestData.ts new file mode 100644 index 0000000000..e3a05357f5 --- /dev/null +++ b/test/integrations/destinations/refiner/processor/groupTestData.ts @@ -0,0 +1,91 @@ +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedGroupPayload, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'refiner', + DestinationDefinition: { + ID: '123', + Name: 'refiner', + DisplayName: 'Refiner', + Config: {}, + }, + Config: { + apiKey: 'dummyApiKey', + blacklistedEvents: [{ eventName: '' }], + eventDelivery: true, + eventDeliveryTS: 1665474171943, + eventFilteringOption: 'disable', + whitelistedEvents: [{ eventName: '' }], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const headers = { + Authorization: 'Bearer dummyApiKey', + 'Content-Type': 'application/x-www-form-urlencoded', +}; + +const endpoint = 'https://api.refiner.io/v1/identify-user'; + +export const groupTestData: ProcessorTestData[] = [ + { + id: 'refiner-group-test-1', + name: 'refiner', + description: 'Successful group call to create account and add user to account', + scenario: 'Framework', + successCriteria: 'Response status code should be 200 and it should contain all account traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedGroupPayload({ + userId: 'test@12', + groupId: 'group@123', + traits: { name: 'rudder ventures', email: 'business@rudderstack.com' }, + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36', + }, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + FORM: { + id: 'test@12', + 'account[email]': 'business@rudderstack.com', + 'account[id]': 'group@123', + 'account[name]': 'rudder ventures', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/refiner/processor/identifyTestData.ts b/test/integrations/destinations/refiner/processor/identifyTestData.ts new file mode 100644 index 0000000000..cc3704bb45 --- /dev/null +++ b/test/integrations/destinations/refiner/processor/identifyTestData.ts @@ -0,0 +1,114 @@ +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + generateSimplifiedIdentifyPayload, + transformResultBuilder, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'refiner', + DestinationDefinition: { + ID: '123', + Name: 'refiner', + DisplayName: 'Refiner', + Config: {}, + }, + Config: { + apiKey: 'dummyApiKey', + blacklistedEvents: [{ eventName: '' }], + eventDelivery: true, + eventDeliveryTS: 1665474171943, + eventFilteringOption: 'disable', + whitelistedEvents: [{ eventName: '' }], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const userTraits = { + age: '30', + city: 'Banglore', + email: 'test@user.com', + phone: '9876543210', + address: { city: 'ahmedabad', state: 'india' }, + lastName: 'user', + username: 'testUser', + firstName: 'test', + userCountry: 'india', +}; + +const expectedOutputUserTraits = { + address: { + city: 'ahmedabad', + state: 'india', + }, + age: '30', + city: 'Banglore', + email: 'test@user.com', + firstName: 'test', + lastName: 'user', + phone: '9876543210', + userCountry: 'india', + userId: 'user@45', + username: 'testUser', +}; + +const headers = { + Authorization: 'Bearer dummyApiKey', + 'Content-Type': 'application/x-www-form-urlencoded', +}; + +const endpoint = 'https://api.refiner.io/v1/identify-user'; + +export const identifyTestData: ProcessorTestData[] = [ + { + id: 'refiner-identify-test-1', + name: 'refiner', + description: 'Successful identify call to create user', + scenario: 'Framework', + successCriteria: + 'Response status code should be 200 and it should contain all user standard and custom traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedIdentifyPayload({ + userId: 'user@45', + context: { + traits: userTraits, + }, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + FORM: { + ...expectedOutputUserTraits, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/refiner/processor/pageTestData.ts b/test/integrations/destinations/refiner/processor/pageTestData.ts new file mode 100644 index 0000000000..b6c782202e --- /dev/null +++ b/test/integrations/destinations/refiner/processor/pageTestData.ts @@ -0,0 +1,99 @@ +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedPageOrScreenPayload, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'refiner', + DestinationDefinition: { + ID: '123', + Name: 'refiner', + DisplayName: 'Refiner', + Config: {}, + }, + Config: { + apiKey: 'dummyApiKey', + blacklistedEvents: [{ eventName: '' }], + eventDelivery: true, + eventDeliveryTS: 1665474171943, + eventFilteringOption: 'disable', + userAttributesMapping: [{ from: 'address', to: 'userAddress' }], + whitelistedEvents: [{ eventName: '' }], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const properties = { + order_id: '5241735', + coupon: 'APPARELSALE', + currency: 'IND', + products: [ + { id: 'product-bacon-jam', category: 'Merch', brand: '' }, + { id: 'product-t-shirt', category: 'Merch', brand: 'Levis' }, + { id: 'offer-t-shirt', category: 'Merch', brand: 'Levis' }, + ], +}; + +const headers = { + Authorization: 'Bearer dummyApiKey', + 'Content-Type': 'application/x-www-form-urlencoded', +}; + +const endpoint = 'https://api.refiner.io/v1/track'; + +export const pageTestData: ProcessorTestData[] = [ + { + id: 'refiner-track-test-1', + name: 'refiner', + description: 'Successful page call', + scenario: 'Framework', + successCriteria: + 'Response status code should be 200 and response should contain respective page name', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedPageOrScreenPayload({ + type: 'page', + userId: '12345', + context: { + traits: {}, + }, + name: 'pageviewed', + properties, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + FORM: { id: '12345', event: 'Viewed pageviewed Page' }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/refiner/processor/trackTestData.ts b/test/integrations/destinations/refiner/processor/trackTestData.ts new file mode 100644 index 0000000000..e10ac6f0f5 --- /dev/null +++ b/test/integrations/destinations/refiner/processor/trackTestData.ts @@ -0,0 +1,109 @@ +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedTrackPayload, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'refiner', + DestinationDefinition: { + ID: '123', + Name: 'refiner', + DisplayName: 'Refiner', + Config: {}, + }, + Config: { + apiKey: 'dummyApiKey', + blacklistedEvents: [{ eventName: '' }], + eventDelivery: true, + eventDeliveryTS: 1665474171943, + eventFilteringOption: 'disable', + userAttributesMapping: [{ from: 'address', to: 'userAddress' }], + whitelistedEvents: [{ eventName: '' }], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const userTraits = { + age: '30', + city: 'Banglore', + email: 'test@user.com', + phone: '9876543210', + address: { city: 'ahmedabad', state: 'india' }, + lastName: 'user', + username: 'testUser', + firstName: 'test', + userCountry: 'india', +}; + +const properties = { + order_id: '5241735', + coupon: 'APPARELSALE', + currency: 'IND', + products: [ + { id: 'product-bacon-jam', category: 'Merch', brand: '' }, + { id: 'product-t-shirt', category: 'Merch', brand: 'Levis' }, + { id: 'offer-t-shirt', category: 'Merch', brand: 'Levis' }, + ], +}; + +const headers = { + Authorization: 'Bearer dummyApiKey', + 'Content-Type': 'application/x-www-form-urlencoded', +}; + +const endpoint = 'https://api.refiner.io/v1/track'; + +export const trackTestData: ProcessorTestData[] = [ + { + id: 'refiner-track-test-1', + name: 'refiner', + description: 'Track event call for Product Searched event', + scenario: 'Framework', + successCriteria: 'Response status code should be 200 and event name should be Product Searched', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + userId: 'user@45', + event: 'Product Searched', + context: { + traits: userTraits, + }, + properties, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + FORM: { id: 'user@45', email: 'test@user.com', event: 'Product Searched' }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/refiner/processor/validationTestData.ts b/test/integrations/destinations/refiner/processor/validationTestData.ts new file mode 100644 index 0000000000..20a9694fdf --- /dev/null +++ b/test/integrations/destinations/refiner/processor/validationTestData.ts @@ -0,0 +1,227 @@ +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'refiner', + DestinationDefinition: { + ID: '123', + Name: 'refiner', + DisplayName: 'Refiner', + Config: {}, + }, + Config: { + apiKey: 'dummyApiKey', + blacklistedEvents: [{ eventName: '' }], + eventDelivery: true, + eventDeliveryTS: 1665474171943, + eventFilteringOption: 'disable', + whitelistedEvents: [{ eventName: '' }], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const userTraits = { + age: '30', + city: 'Banglore', + email: 'test@user.com', + phone: '9876543210', + address: { city: 'ahmedabad', state: 'india' }, + lastName: 'user', + username: 'testUser', + firstName: 'test', + userCountry: 'india', +}; + +const properties = { + order_id: '5241735', + coupon: 'APPARELSALE', + currency: 'IND', + products: [ + { id: 'product-bacon-jam', category: 'Merch', brand: '' }, + { id: 'product-t-shirt', category: 'Merch', brand: 'Levis' }, + { id: 'offer-t-shirt', category: 'Merch', brand: 'Levis' }, + ], +}; + +const expectedStatTags = { + destType: 'REFINER', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +export const validationTestData: ProcessorTestData[] = [ + { + id: 'refiner-validation-test-1', + name: 'refiner', + description: '[Error]: Check for no message type', + scenario: 'Framework', + successCriteria: 'Response status code should be 400 with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + userId: 'user@45', + context: { + traits: userTraits, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Event type is required', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'refiner-validation-test-2', + name: 'refiner', + description: 'Error]: Check for unsupported message type', + scenario: 'Framework', + successCriteria: + 'Response should contain error message and status code should be 400, as we are sending a message type which is not supported by Klaviyo destination and the error message should be Event type alias is not supported', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + userId: 'user@45', + type: 'alias', + context: { + traits: userTraits, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Event type "alias" is not supported', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'refiner-validation-test-3', + name: 'refiner', + description: 'Error]: userId and email is not present in payload', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400, it should throw instrumetation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + context: { + traits: {}, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'At least one of `userId` or `email` is required', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'refiner-validation-test-4', + name: 'refiner', + description: 'Error]: event name is not present in payload', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400, it should throw instrumetation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + context: { + traits: userTraits, + }, + properties, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Event name is required', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; From a16d6387873e107a3004038236f5531380137936 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 29 Apr 2024 06:55:25 +0000 Subject: [PATCH 128/240] chore(release): 1.64.0 --- CHANGELOG.md | 19 +++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d3da68413..6907317e82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,25 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.64.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.63.0...v1.64.0) (2024-04-29) + + +### Features + +* adding custom properties support to bluecore ([#3282](https://github.com/rudderlabs/rudder-transformer/issues/3282)) ([8592e66](https://github.com/rudderlabs/rudder-transformer/commit/8592e664eb568e70a00261e275ab2faed8f6f618)) +* **iterable:** component test refactor ([#3250](https://github.com/rudderlabs/rudder-transformer/issues/3250)) ([650ea9c](https://github.com/rudderlabs/rudder-transformer/commit/650ea9ca9ca6351aadc51bfb5038b8b2507990ec)) +* onboard Yandex Metrica Offline Events Destination ([#3232](https://github.com/rudderlabs/rudder-transformer/issues/3232)) ([8f79f53](https://github.com/rudderlabs/rudder-transformer/commit/8f79f53d30326e07fc92dd624e799015ff9f87c2)) +* **refiner:** component test refactor ([#3221](https://github.com/rudderlabs/rudder-transformer/issues/3221)) ([7b1d11b](https://github.com/rudderlabs/rudder-transformer/commit/7b1d11b306f3fa3a5698061c31acfbf53bc78dd1)) + + +### Bug Fixes + +* algolia enhancement ( adding currency, price, subType and objectData support ) ([#3290](https://github.com/rudderlabs/rudder-transformer/issues/3290)) ([f06ebde](https://github.com/rudderlabs/rudder-transformer/commit/f06ebde110693fe32f8e450dc395f1f4019defab)) +* **delighted:** replace myAxios utility with handleHttpRequest utility ([#3237](https://github.com/rudderlabs/rudder-transformer/issues/3237)) ([bac3cc5](https://github.com/rudderlabs/rudder-transformer/commit/bac3cc5670c149454a6063a55a4b901043b0ff02)) +* handle empty userId ([5402b21](https://github.com/rudderlabs/rudder-transformer/commit/5402b219ccdeaafb710c8c2828e983e9864a415f)) +* handle empty userId (movable ink, bloomreach) ([#3300](https://github.com/rudderlabs/rudder-transformer/issues/3300)) ([038c1aa](https://github.com/rudderlabs/rudder-transformer/commit/038c1aa04049aaa1caa1bf82cf6c69611b5d3fd9)) +* send content_ids as a string if there is only one value ([#3317](https://github.com/rudderlabs/rudder-transformer/issues/3317)) ([54eca32](https://github.com/rudderlabs/rudder-transformer/commit/54eca3220ea48fae64c655813fe4430dd704639e)) + ## [1.63.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.62.2...v1.63.0) (2024-04-25) diff --git a/package-lock.json b/package-lock.json index a16f7006bc..ee4f0bd087 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.63.0", + "version": "1.64.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.63.0", + "version": "1.64.0", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index 6aae041dfd..1a246d143b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.63.0", + "version": "1.64.0", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From e57d7e0e0b50c9cdb42ed7e9225c65bb873288aa Mon Sep 17 00:00:00 2001 From: Sai Sankeerth Date: Mon, 29 Apr 2024 13:32:17 +0530 Subject: [PATCH 129/240] chore: update changelog --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6907317e82..ea8468723f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,7 @@ All notable changes to this project will be documented in this file. See [standa ### Features * adding custom properties support to bluecore ([#3282](https://github.com/rudderlabs/rudder-transformer/issues/3282)) ([8592e66](https://github.com/rudderlabs/rudder-transformer/commit/8592e664eb568e70a00261e275ab2faed8f6f618)) -* **iterable:** component test refactor ([#3250](https://github.com/rudderlabs/rudder-transformer/issues/3250)) ([650ea9c](https://github.com/rudderlabs/rudder-transformer/commit/650ea9ca9ca6351aadc51bfb5038b8b2507990ec)) * onboard Yandex Metrica Offline Events Destination ([#3232](https://github.com/rudderlabs/rudder-transformer/issues/3232)) ([8f79f53](https://github.com/rudderlabs/rudder-transformer/commit/8f79f53d30326e07fc92dd624e799015ff9f87c2)) -* **refiner:** component test refactor ([#3221](https://github.com/rudderlabs/rudder-transformer/issues/3221)) ([7b1d11b](https://github.com/rudderlabs/rudder-transformer/commit/7b1d11b306f3fa3a5698061c31acfbf53bc78dd1)) ### Bug Fixes From fdecaf36d91db7540d6f68a013e4f7fb2a36ebaa Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Tue, 30 Apr 2024 10:32:14 +0530 Subject: [PATCH 130/240] feat: custom property support AWIN (#3325) * feat: custom property support AWIN * feat: adding awin property pattern check logic --- src/v0/destinations/awin/transform.js | 9 +- src/v0/destinations/awin/utils.js | 19 ++ src/v0/destinations/awin/utils.test.js | 31 +- test/integrations/destinations/awin/data.ts | 317 ++++++++++++++++++++ 4 files changed, 373 insertions(+), 3 deletions(-) diff --git a/src/v0/destinations/awin/transform.js b/src/v0/destinations/awin/transform.js index 0d7fd95c33..68dd9d62e1 100644 --- a/src/v0/destinations/awin/transform.js +++ b/src/v0/destinations/awin/transform.js @@ -2,10 +2,10 @@ const { InstrumentationError, ConfigurationError } = require('@rudderstack/integ const { BASE_URL, ConfigCategory, mappingConfig } = require('./config'); const { defaultRequestConfig, constructPayload, simpleProcessRouterDest } = require('../../util'); -const { getParams, trackProduct } = require('./utils'); +const { getParams, trackProduct, populateCustomTransactionProperties } = require('./utils'); const responseBuilder = (message, { Config }) => { - const { advertiserId, eventsToTrack } = Config; + const { advertiserId, eventsToTrack, customFieldMap } = Config; const { event, properties } = message; let finalParams = {}; @@ -22,10 +22,15 @@ const responseBuilder = (message, { Config }) => { if (eventsList.includes(event)) { params = getParams(payload.params, advertiserId); const productTrackObject = trackProduct(properties, advertiserId, params.parts); + const customTransactionProperties = populateCustomTransactionProperties( + properties, + customFieldMap, + ); finalParams = { ...params, ...productTrackObject, + ...customTransactionProperties, }; } else { throw new InstrumentationError( diff --git a/src/v0/destinations/awin/utils.js b/src/v0/destinations/awin/utils.js index f0daea9b99..715fb5818d 100644 --- a/src/v0/destinations/awin/utils.js +++ b/src/v0/destinations/awin/utils.js @@ -1,3 +1,4 @@ +const { getHashFromArray } = require('@rudderstack/integrations-lib'); const lodash = require('lodash'); /** @@ -77,8 +78,26 @@ const trackProduct = (properties, advertiserId, commissionParts) => { return transformedProductInfoObj; }; +// ref: https://wiki.awin.com/index.php/Advertiser_Tracking_Guide/Product_Level_Tracking#PLT_Via_Conversion_Pixel +const populateCustomTransactionProperties = (properties, customFieldMap) => { + const customObject = {}; + const customPropertyPattern = '^\\s*p\\d+\\s*$'; + const regex = new RegExp(customPropertyPattern, 'i'); + const propertyMap = getHashFromArray(customFieldMap, 'from', 'to', false); + Object.entries(propertyMap).forEach(([rudderProperty, awinProperty]) => { + if (regex.test(awinProperty)) { + const fieldValue = properties[rudderProperty]; + if (fieldValue) { + customObject[awinProperty] = fieldValue; + } + } + }); + return customObject; +}; + module.exports = { getParams, trackProduct, buildProductPayloadString, + populateCustomTransactionProperties, }; diff --git a/src/v0/destinations/awin/utils.test.js b/src/v0/destinations/awin/utils.test.js index e60c07e96c..ca7d079b1b 100644 --- a/src/v0/destinations/awin/utils.test.js +++ b/src/v0/destinations/awin/utils.test.js @@ -1,4 +1,8 @@ -const { buildProductPayloadString, trackProduct } = require('./utils'); +const { + buildProductPayloadString, + trackProduct, + populateCustomTransactionProperties, +} = require('./utils'); describe('buildProductPayloadString', () => { // Should correctly build the payload string with all fields provided @@ -163,3 +167,28 @@ describe('trackProduct', () => { }); }); }); + +describe('populateCustomTransactionProperties', () => { + // The function should correctly map properties from the input object to the output object based on the customFieldMap. + it('should correctly map properties from the input object to the output object based on the customFieldMap', () => { + const properties = { + rudderProperty1: 'value1', + rudderProperty2: 123, + rudderProperty3: 'value3', + rudderProperty4: 234, + }; + const customFieldMap = [ + { from: 'rudderProperty1', to: 'p1' }, + { from: 'rudderProperty2', to: 'p2' }, + { from: 'rudderProperty4', to: 'anotherp2' }, + ]; + const expectedOutput = { + p1: 'value1', + p2: 123, + }; + + const result = populateCustomTransactionProperties(properties, customFieldMap); + + expect(result).toEqual(expectedOutput); + }); +}); diff --git a/test/integrations/destinations/awin/data.ts b/test/integrations/destinations/awin/data.ts index 8ca294b5fb..5a7fcfb50f 100644 --- a/test/integrations/destinations/awin/data.ts +++ b/test/integrations/destinations/awin/data.ts @@ -1158,4 +1158,321 @@ export const data = [ }, }, }, + { + name: 'awin', + description: 'Track call: with custom transaction property', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + advertiserId: '1234', + eventsToTrack: [ + { + eventName: 'abc', + }, + { + eventName: 'prop2', + }, + { + eventName: 'prop3', + }, + ], + customFieldMap: [ + { + from: 'customTransactionValue1', + to: 'p1', + }, + { + from: 'customTransactionValue2', + to: 'p2', + }, + ], + }, + }, + message: { + type: 'track', + event: 'abc', + sentAt: '2022-01-20T13:39:21.033Z', + userId: 'user123456001', + channel: 'web', + properties: { + currency: 'INR', + voucherCode: '1bcu1', + amount: 125, + customTransactionValue1: 'val1', + customTransactionValue2: 'val2', + }, + context: { + os: { + name: '', + version: '', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.2.20', + namespace: 'com.rudderlabs.javascript', + }, + page: { + url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + path: '/Testing/App_for_LaunchDarkly/ourSdk.html', + title: 'Document', + search: '', + tab_url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + referrer: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/', + initial_referrer: '$direct', + referring_domain: '127.0.0.1:7307', + initial_referring_domain: '', + }, + locale: 'en-US', + screen: { + width: 1440, + height: 900, + density: 2, + innerWidth: 536, + innerHeight: 689, + }, + traits: { + city: 'Pune', + name: 'First User', + email: 'firstUser@testmail.com', + title: 'VP', + gender: 'female', + avatar: 'https://i.pravatar.cc/300', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.2.20', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36', + }, + rudderId: '553b5522-c575-40a7-8072-9741c5f9a647', + messageId: '831f1fa5-de84-4f22-880a-4c3f23fc3f04', + anonymousId: 'bf412108-0357-4330-b119-7305e767823c', + integrations: { + All: true, + }, + originalTimestamp: '2022-01-20T13:39:21.032Z', + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.awin1.com/sread.php', + headers: {}, + params: { + amount: 125, + ch: 'aw', + cr: 'INR', + tt: 'ss', + tv: '2', + vc: '1bcu1', + merchant: '1234', + parts: 'DEFAULT:125', + testmode: '0', + p1: 'val1', + p2: 'val2', + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'awin', + description: 'Track call- with product array, along with transactional custom property', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + advertiserId: '1234', + eventsToTrack: [ + { + eventName: 'abc', + }, + { + eventName: 'prop2', + }, + { + eventName: 'prop3', + }, + ], + customFieldMap: [ + { + from: 'customTransactionValue1', + to: 'p1', + }, + { + from: 'customTransactionValue2', + to: 'p2', + }, + ], + }, + }, + message: { + type: 'track', + event: 'prop2', + sentAt: '2022-01-20T13:39:21.033Z', + userId: 'user123456001', + channel: 'web', + properties: { + currency: 'INR', + voucherCode: '1bcu1', + amount: 500, + commissionGroup: 'sales', + cks: 'new', + testMode: '1', + order_id: 'QW123', + customTransactionValue1: 'val1', + customTransactionValue2: 'val2', + products: [ + { + price: 10, + quantity: 1, + sku: undefined, + category: 'Category 1', + }, + { + product_id: '456', + name: 'Product 2', + price: 20, + quantity: 2, + sku: 'SKU456', + category: undefined, + }, + ], + }, + context: { + os: { + name: '', + version: '', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.2.20', + namespace: 'com.rudderlabs.javascript', + }, + page: { + url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + path: '/Testing/App_for_LaunchDarkly/ourSdk.html', + title: 'Document', + search: '', + tab_url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + referrer: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/', + initial_referrer: '$direct', + referring_domain: '127.0.0.1:7307', + initial_referring_domain: '', + }, + locale: 'en-US', + screen: { + width: 1440, + height: 900, + density: 2, + innerWidth: 536, + innerHeight: 689, + }, + traits: { + city: 'Pune', + name: 'First User', + email: 'firstUser@testmail.com', + title: 'VP', + gender: 'female', + avatar: 'https://i.pravatar.cc/300', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.2.20', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36', + }, + rudderId: '553b5522-c575-40a7-8072-9741c5f9a647', + messageId: '831f1fa5-de84-4f22-880a-4c3f23fc3f04', + anonymousId: 'bf412108-0357-4330-b119-7305e767823c', + integrations: { + All: true, + }, + originalTimestamp: '2022-01-20T13:39:21.032Z', + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.awin1.com/sread.php', + headers: {}, + params: { + amount: 500, + ch: 'aw', + parts: 'sales:500', + cr: 'INR', + tt: 'ss', + tv: '2', + vc: '1bcu1', + cks: 'new', + merchant: '1234', + testmode: '1', + ref: 'QW123', + 'bd[0]': 'AW:P|1234|QW123|456|Product%202|20|2|SKU456|sales%3A500|', + p1: 'val1', + p2: 'val2', + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, ]; From 17010050e01c2e02490414f5aca21e2a1c49642b Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Tue, 30 Apr 2024 13:13:03 +0530 Subject: [PATCH 131/240] chore: changelog edit for awin --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea8468723f..95fd902959 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. See [standa * adding custom properties support to bluecore ([#3282](https://github.com/rudderlabs/rudder-transformer/issues/3282)) ([8592e66](https://github.com/rudderlabs/rudder-transformer/commit/8592e664eb568e70a00261e275ab2faed8f6f618)) * onboard Yandex Metrica Offline Events Destination ([#3232](https://github.com/rudderlabs/rudder-transformer/issues/3232)) ([8f79f53](https://github.com/rudderlabs/rudder-transformer/commit/8f79f53d30326e07fc92dd624e799015ff9f87c2)) +* transactional custom property support for awin ([#3325](https://github.com/rudderlabs/rudder-transformer/issues/3325)) ([fdecaf3](https://github.com/rudderlabs/rudder-transformer/commit/fdecaf36d91db7540d6f68a013e4f7fb2a36ebaa)) ### Bug Fixes From ce9f5db861712c90d22cba0bd2ad389f31a1bf62 Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Tue, 30 Apr 2024 16:37:22 +0530 Subject: [PATCH 132/240] fix: algolia add validation for currency fields (#3329) * fix: algolia add validation for currency fields * fix: minor edit * fix: adding unit test cases for validate payload --- .../v2/destinations/algolia/procWorkflow.yaml | 1 + src/v0/destinations/algolia/util.js | 19 +++ src/v0/destinations/algolia/util.test.js | 58 +++++++ .../destinations/algolia/processor/data.ts | 142 ++++++++++++++++++ 4 files changed, 220 insertions(+) create mode 100644 src/v0/destinations/algolia/util.test.js diff --git a/src/cdk/v2/destinations/algolia/procWorkflow.yaml b/src/cdk/v2/destinations/algolia/procWorkflow.yaml index 87da64aa45..402b48dabd 100644 --- a/src/cdk/v2/destinations/algolia/procWorkflow.yaml +++ b/src/cdk/v2/destinations/algolia/procWorkflow.yaml @@ -76,6 +76,7 @@ steps: const objectIDs = ~r products.objectId; $.context.payload.objectIDs = Array.isArray(objectIDs) ? objectIDs[:20]:$.context.payload.objectIDs; $.context.payload.objectData = $.outputs.prepareObjectDataBlock + $.validatePayload($.context.payload) - name: validateDestPayload template: | diff --git a/src/v0/destinations/algolia/util.js b/src/v0/destinations/algolia/util.js index 863c23aba7..b10097bbee 100644 --- a/src/v0/destinations/algolia/util.js +++ b/src/v0/destinations/algolia/util.js @@ -143,9 +143,28 @@ const clickPayloadValidator = (payload) => { return updatedPayload; }; +// ref : https://www.algolia.com/doc/rest-api/insights/#method-param-objectdata-2:~:text=currency-,%23,currency%20as%20ISO%2D4217%20currency%20code%2C%20such%20as%20USD%20or%20EUR.,-ObjectData +function validatePayload(payload) { + if (payload.objectData && Array.isArray(payload.objectData)) { + const hasRelevantFields = payload.objectData.some( + (obj) => + obj.hasOwnProperty('price') || + obj.hasOwnProperty('quantity') || + obj.hasOwnProperty('discount'), + ); + + if (hasRelevantFields && !payload.currency) { + throw new InstrumentationError( + 'Currency missing when objectData fields has price informations.', + ); + } + } +} + module.exports = { genericpayloadValidator, createObjectArray, eventTypeMapping, clickPayloadValidator, + validatePayload, }; diff --git a/src/v0/destinations/algolia/util.test.js b/src/v0/destinations/algolia/util.test.js new file mode 100644 index 0000000000..850d93f1c6 --- /dev/null +++ b/src/v0/destinations/algolia/util.test.js @@ -0,0 +1,58 @@ +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { validatePayload } = require('./util'); + +describe('validatePayload', () => { + // When payload is valid and contains relevant fields and currency + it('should validate payload when it is valid and contains relevant fields and currency', () => { + const payload = { + objectData: [ + { price: 10, quantity: 2, discount: 0.1 }, + { price: 20, quantity: 1, discount: 0 }, + ], + currency: 'USD', + }; + + expect(() => validatePayload(payload)).not.toThrow(); + }); + + // When payload contains objects with missing relevant fields + it('should throw an error when payload contains objects with missing relevant fields', () => { + const payload = { + objectData: [ + { price: 10, quantity: 2 }, + { price: 20, discount: 0 }, + ], + }; + + expect(() => validatePayload(payload)).toThrow(InstrumentationError); + }); + + // When payload is valid and contains relevant fields but no currency + it('should throw an InstrumentationError when currency is missing', () => { + const payload = { + objectData: [ + { price: 10, quantity: 2, discount: 0.1 }, + { price: 20, quantity: 1, discount: 0 }, + ], + }; + + expect(() => validatePayload(payload)).toThrow(InstrumentationError); + }); + + // When payload is valid but does not contain relevant fields + it('should not throw an error when payload does not contain relevant fields', () => { + const payload = { + objectData: [{ position: 'Product A' }, { position: 'Product B' }], + currency: 'USD', + }; + + expect(() => validatePayload(payload)).not.toThrow(); + }); + + // When payload is empty + it('should not throw an error when payload is empty', () => { + const payload = {}; + + expect(() => validatePayload(payload)).not.toThrow(); + }); +}); diff --git a/test/integrations/destinations/algolia/processor/data.ts b/test/integrations/destinations/algolia/processor/data.ts index a8dd31b51a..d239c8de70 100644 --- a/test/integrations/destinations/algolia/processor/data.ts +++ b/test/integrations/destinations/algolia/processor/data.ts @@ -1688,6 +1688,7 @@ export const data = [ properties: { index: 'products', eventSubtype: 'addToCart', + currency: 'USD', products: [ { objectId: 'ecommerce-sample-data-919', @@ -1751,6 +1752,7 @@ export const data = [ events: [ { index: 'products', + currency: 'USD', queryID: '43b15df305339e827f0ac0bdc5ebcaa7', objectIDs: ['ecommerce-sample-data-919', '9780439784542'], userToken: 'testuserId1', @@ -1760,11 +1762,13 @@ export const data = [ objectData: [ { quantity: 2, + price: '10', queryID: '123', discount: '10', }, { quantity: 3, + price: '30', queryID: '123', discount: '10', }, @@ -1858,6 +1862,7 @@ export const data = [ userId: 'testuserId1', properties: { index: 'products', + currency: 'USD', eventSubtype: 'purchase', products: [ { @@ -1922,6 +1927,7 @@ export const data = [ events: [ { index: 'products', + currency: 'USD', queryID: '43b15df305339e827f0ac0bdc5ebcaa7', objectIDs: ['ecommerce-sample-data-919', '9780439784542'], userToken: 'testuserId1', @@ -1933,11 +1939,13 @@ export const data = [ quantity: 2, queryID: '123', discount: '10', + price: '10', }, { quantity: 3, queryID: '123', discount: '10', + price: '30', }, ], }, @@ -2287,4 +2295,138 @@ export const data = [ }, }, }, + { + name: 'algolia', + description: 'When price information is present in objectData, currency is mandatory', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + page: { + path: '/destinations/ometria', + referrer: '', + search: '', + title: '', + url: 'https://docs.rudderstack.com/destinations/ometria', + category: 'destination', + initial_referrer: 'https://docs.rudderstack.com', + initial_referring_domain: 'docs.rudderstack.com', + }, + }, + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + properties: { + index: 'products', + products: [ + { + objectId: 'ecommerce-sample-data-919', + position: 7, + quantity: '2', + price: 10, + }, + { + objectId: '9780439784542', + position: 8, + quantity: '3', + price: 30, + }, + ], + queryId: '43b15df305339e827f0ac0bdc5ebcaa7', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'product list viewed', + to: 'conversion', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Currency missing when objectData fields has price informations.: Workflow: procWorkflow, Step: populateProductsData, ChildStep: populateForClickEvent, OriginalError: Currency missing when objectData fields has price informations.', + statTags: { + destType: 'ALGOLIA', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + destinationId: 'destId', + workspaceId: 'wspId', + }, + statusCode: 400, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, ]; From 2e22075ddf792c573642fd09c5f9d31d8132525b Mon Sep 17 00:00:00 2001 From: Gauravudia Date: Thu, 2 May 2024 15:32:50 +0530 Subject: [PATCH 133/240] fix: auth0 error handling --- src/v0/sources/auth0/transform.js | 16 +-- test/__tests__/data/auth0_source.json | 136 +++++++++++++++++++++----- 2 files changed, 119 insertions(+), 33 deletions(-) diff --git a/src/v0/sources/auth0/transform.js b/src/v0/sources/auth0/transform.js index 4b78621418..b2330443bd 100644 --- a/src/v0/sources/auth0/transform.js +++ b/src/v0/sources/auth0/transform.js @@ -1,6 +1,5 @@ const path = require('path'); const fs = require('fs'); -const { InstrumentationError } = require('@rudderstack/integrations-lib'); const { removeUndefinedAndNullValues } = require('../../util'); const { getGroupId } = require('./util'); // import mapping json using JSON.parse to preserve object key order @@ -59,11 +58,10 @@ function processEvents(eventList) { } else { response = prepareTrackPayload(data); } - if (response?.userId) { - // eslint-disable-next-line camelcase - response.properties.log_id = log_id; - responses.push(removeUndefinedAndNullValues(response)); - } + + // eslint-disable-next-line camelcase + response.properties.log_id = log_id; + responses.push(removeUndefinedAndNullValues(response)); } }); return responses; @@ -74,11 +72,7 @@ function process(events) { if (!Array.isArray(events)) { eventList = events.logs || [events]; } - const responses = processEvents(eventList); - if (responses.length === 0) { - throw new InstrumentationError('UserId is not present'); - } - return responses; + return processEvents(eventList); } exports.process = process; diff --git a/test/__tests__/data/auth0_source.json b/test/__tests__/data/auth0_source.json index e3190bd683..739933dd1f 100644 --- a/test/__tests__/data/auth0_source.json +++ b/test/__tests__/data/auth0_source.json @@ -1147,18 +1147,51 @@ "log_id": "90020221031055712103169676686005480714681762668315934738" } }, - "output": { - "statusCode": 400, - "error": "UserId is not present", - "statTags": { - "errorCategory": "dataValidation", - "errorType": "instrumentation", - "module": "source", - "implementation": "native", - "destinationId": "Non determinable", - "workspaceId": "Non determinable" + "output": [ + { + "type": "identify", + "sentAt": "2022-10-31T05:57:06.859Z", + "traits": { + "connection": "Username-Password-Authentication", + "connection_id": "con_djwCjiwyID0vZy1S" + }, + "userId": "", + "context": { + "traits": { + "userId": "", + "user_name": "testRudderlabs+21@gmail.com" + }, + "library": { + "name": "unknown", + "version": "unknown" + }, + "userAgent": "unknown", + "request_ip": "35.166.202.113", + "integration": { + "name": "Auth0" + } + }, + "properties": { + "log_id": "90020221031055712103169676686005480714681762668315934738", + "details": { + "body": { + "email": "testRudderlabs+21@gmail.com", + "tenant": "dev-cu4jy2zgao6yx15x", + "password": "dummyPassword", + "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", + "connection": "Username-Password-Authentication" + } + }, + "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", + "client_name": "All Applications", + "description": "" + }, + "integrations": { + "Auth0": false + }, + "originalTimestamp": "2022-10-31T05:57:06.859Z" } - } + ] }, { "description": "UserId is missing for all the requests in a batch", @@ -1205,17 +1238,76 @@ } } ], - "output": { - "statusCode": 400, - "error": "UserId is not present", - "statTags": { - "errorCategory": "dataValidation", - "errorType": "instrumentation", - "module": "source", - "implementation": "native", - "destinationId": "Non determinable", - "workspaceId": "Non determinable" + "output": [ + { + "type": "identify", + "userId": "", + "sentAt": "2022-10-31T05:57:06.859Z", + "traits": { + "connection": "Username-Password-Authentication", + "connection_id": "con_djwCjiwyID0vZy1S" + }, + "context": { + "traits": { + "userId": "", + "user_name": "testRudderlabs+21@gmail.com" + }, + "library": { + "name": "unknown", + "version": "unknown" + }, + "userAgent": "unknown", + "request_ip": "35.166.202.113", + "integration": { + "name": "Auth0" + } + }, + "properties": { + "log_id": "90020221031055712103169676686005480714681762668315934738", + "details": { + "body": { + "email": "testRudderlabs+21@gmail.com", + "tenant": "dev-cu4jy2zgao6yx15x", + "password": "dummyPassword", + "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", + "connection": "Username-Password-Authentication" + } + }, + "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", + "client_name": "All Applications", + "description": "" + }, + "integrations": { + "Auth0": false + }, + "originalTimestamp": "2022-10-31T05:57:06.859Z" + }, + { + "type": "track", + "event": "Success API Operation", + "sentAt": "2022-10-31T05:57:06.874Z", + "context": { + "library": { + "name": "unknown", + "version": "unknown" + }, + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", + "request_ip": "35.166.202.113", + "integration": { + "name": "Auth0" + } + }, + "properties": { + "log_id": "90020221031055712103169676686007898566320991926665347090", + "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", + "client_name": "", + "description": "Create a User" + }, + "integrations": { + "Auth0": false + }, + "originalTimestamp": "2022-10-31T05:57:06.874Z" } - } + ] } ] From 447f85faf6ccca2179ab33b7fe43e281fc4f5897 Mon Sep 17 00:00:00 2001 From: Gauravudia Date: Fri, 3 May 2024 15:13:57 +0530 Subject: [PATCH 134/240] feat: generate anonymousId and move to component testcases --- src/v0/sources/auth0/transform.js | 2 + test/__tests__/auth0_source.test.js | 23 - test/__tests__/data/auth0_source.json | 1313 -------------------- test/integrations/sources/auth0/data.ts | 1493 +++++++++++++++++++++++ 4 files changed, 1495 insertions(+), 1336 deletions(-) delete mode 100644 test/__tests__/auth0_source.test.js delete mode 100644 test/__tests__/data/auth0_source.json create mode 100644 test/integrations/sources/auth0/data.ts diff --git a/src/v0/sources/auth0/transform.js b/src/v0/sources/auth0/transform.js index b2330443bd..5a1bf42e28 100644 --- a/src/v0/sources/auth0/transform.js +++ b/src/v0/sources/auth0/transform.js @@ -5,6 +5,7 @@ const { getGroupId } = require('./util'); // import mapping json using JSON.parse to preserve object key order const mapping = JSON.parse(fs.readFileSync(path.resolve(__dirname, './mapping.json'), 'utf-8')); const Message = require('../message'); +const { generateUUID } = require('../../util'); // Ref: https://auth0.com/docs/logs/references/log-event-type-codes const eventNameMap = JSON.parse( @@ -61,6 +62,7 @@ function processEvents(eventList) { // eslint-disable-next-line camelcase response.properties.log_id = log_id; + response.anonymousId = generateUUID(); responses.push(removeUndefinedAndNullValues(response)); } }); diff --git a/test/__tests__/auth0_source.test.js b/test/__tests__/auth0_source.test.js deleted file mode 100644 index 75d103d46f..0000000000 --- a/test/__tests__/auth0_source.test.js +++ /dev/null @@ -1,23 +0,0 @@ -const integration = "auth0"; - -const fs = require("fs"); -const path = require("path"); - -const transformer = require(`../../src/v0/sources/${integration}/transform`); - -const testDataFile = fs.readFileSync( - path.resolve(__dirname, `./data/${integration}_source.json`) -); - -const testData = JSON.parse(testDataFile); - -testData.forEach((data, index) => { - it(`${index}. ${integration} - ${data.description}`, () => { - try { - const output = transformer.process(data.input); - expect(output).toEqual(data.output); - } catch (error) { - expect(error.message).toEqual(data.output.error); - } - }); -}); diff --git a/test/__tests__/data/auth0_source.json b/test/__tests__/data/auth0_source.json deleted file mode 100644 index 739933dd1f..0000000000 --- a/test/__tests__/data/auth0_source.json +++ /dev/null @@ -1,1313 +0,0 @@ -[ - { - "description": "successful signup", - "input": [ - { - "log_id": "90020221031055712103169676686005480714681762668315934738", - "data": { - "date": "2022-10-31T05:57:06.859Z", - "type": "ss", - "description": "", - "connection": "Username-Password-Authentication", - "connection_id": "con_djwCjiwyID0vZy1S", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "All Applications", - "ip": "35.166.202.113", - "user_agent": "unknown", - "details": { - "body": { - "email": "testRudderlabs+21@gmail.com", - "tenant": "dev-cu4jy2zgao6yx15x", - "password": "dummyPassword", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "connection": "Username-Password-Authentication" - } - }, - "user_id": "auth0|dummyPassword", - "user_name": "testRudderlabs+21@gmail.com", - "strategy": "auth0", - "strategy_type": "database", - "log_id": "90020221031055712103169676686005480714681762668315934738" - } - }, - { - "log_id": "90020221031055712103169676686007898566320991926665347090", - "data": { - "date": "2022-10-31T05:57:06.874Z", - "type": "sapi", - "description": "Create a User", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "", - "ip": "35.166.202.113", - "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "details": { - "request": { - "ip": "35.166.202.113", - "auth": { - "user": { - "name": "rudder test", - "email": "test@rudderstack.com", - "user_id": "auth0|dummyPassword" - }, - "strategy": "jwt", - "credentials": { - "jti": "571921bf7833a97efabf08d765a0ec8f", - "scopes": [ - "create:actions", - "create:actions_log_sessions", - "create:client_credentials", - "create:client_grants", - "create:clients", - "create:connections", - "create:custom_domains", - "create:email_provider", - "create:email_templates", - "create:guardian_enrollment_tickets", - "create:integrations", - "create:log_streams", - "create:organization_connections", - "create:organization_invitations", - "create:organization_member_roles", - "create:organization_members", - "create:organizations", - "create:requested_scopes", - "create:resource_servers", - "create:roles", - "create:rules", - "create:shields", - "create:signing_keys", - "create:tenant_invitations", - "create:test_email_dispatch", - "create:users", - "delete:actions", - "delete:anomaly_blocks", - "delete:branding", - "delete:client_credentials", - "delete:client_grants", - "delete:clients", - "delete:connections", - "delete:custom_domains", - "delete:device_credentials", - "delete:email_provider", - "delete:email_templates", - "delete:grants", - "delete:guardian_enrollments", - "delete:integrations", - "delete:log_streams", - "delete:organization_connections", - "delete:organization_invitations", - "delete:organization_member_roles", - "delete:organization_members", - "delete:organizations", - "delete:owners", - "delete:requested_scopes", - "delete:resource_servers", - "delete:roles", - "delete:rules", - "delete:rules_configs", - "delete:shields", - "delete:tenant_invitations", - "delete:tenant_members", - "delete:tenants", - "delete:users", - "read:actions", - "read:anomaly_blocks", - "read:attack_protection", - "read:branding", - "read:checks", - "read:client_credentials", - "read:client_grants", - "read:client_keys", - "read:clients", - "read:connections", - "read:custom_domains", - "read:device_credentials", - "read:email_provider", - "read:email_templates", - "read:email_triggers", - "read:entity_counts", - "read:grants", - "read:guardian_factors", - "read:insights", - "read:integrations", - "read:log_streams", - "read:logs", - "read:mfa_policies", - "read:organization_connections", - "read:organization_invitations", - "read:organization_member_roles", - "read:organization_members", - "read:organizations", - "read:prompts", - "read:requested_scopes", - "read:resource_servers", - "read:roles", - "read:rules", - "read:rules_configs", - "read:shields", - "read:signing_keys", - "read:stats", - "read:tenant_invitations", - "read:tenant_members", - "read:tenant_settings", - "read:triggers", - "read:users", - "run:checks", - "update:actions", - "update:attack_protection", - "update:branding", - "update:client_credentials", - "update:client_grants", - "update:client_keys", - "update:clients", - "update:connections", - "update:custom_domains", - "update:email_provider", - "update:email_templates", - "update:email_triggers", - "update:guardian_factors", - "update:integrations", - "update:log_streams", - "update:mfa_policies", - "update:organization_connections", - "update:organizations", - "update:prompts", - "update:requested_scopes", - "update:resource_servers", - "update:roles", - "update:rules", - "update:rules_configs", - "update:shields", - "update:signing_keys", - "update:tenant_members", - "update:tenant_settings", - "update:triggers", - "update:users" - ] - } - }, - "body": { - "email": "testRudderlabs+21@gmail.com", - "password": "dummyPassword", - "connection": "Username-Password-Authentication" - }, - "path": "/api/v2/users", - "query": {}, - "method": "post", - "channel": "https://manage.auth0.com/", - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" - }, - "response": { - "body": { - "name": "testRudderlabs+21@gmail.com", - "email": "testRudderlabs+21@gmail.com", - "picture": "https://s.gravatar.com/avatar/0902f9d02b92aed9f0ac59aaf9475b60?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fbh.png", - "user_id": "auth0|dummyPassword", - "nickname": "testRudderlabs+21", - "created_at": "2022-10-31T05:57:06.864Z", - "identities": [ - { - "user_id": "auth0|dummyPassword", - "isSocial": false, - "provider": "auth0", - "connection": "Username-Password-Authentication" - } - ], - "updated_at": "2022-10-31T05:57:06.864Z", - "email_verified": false - }, - "statusCode": 201 - } - }, - "user_id": "auth0|dummyPassword", - "log_id": "90020221031055712103169676686007898566320991926665347090" - } - } - ], - "output": [ - { - "type": "identify", - "sentAt": "2022-10-31T05:57:06.859Z", - "traits": { - "connection": "Username-Password-Authentication", - "connection_id": "con_djwCjiwyID0vZy1S" - }, - "userId": "auth0|dummyPassword", - "context": { - "traits": { - "userId": "auth0|dummyPassword", - "user_name": "testRudderlabs+21@gmail.com" - }, - "library": { - "name": "unknown", - "version": "unknown" - }, - "userAgent": "unknown", - "request_ip": "35.166.202.113", - "integration": { - "name": "Auth0" - } - }, - "properties": { - "log_id": "90020221031055712103169676686005480714681762668315934738", - "details": { - "body": { - "email": "testRudderlabs+21@gmail.com", - "tenant": "dev-cu4jy2zgao6yx15x", - "password": "dummyPassword", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "connection": "Username-Password-Authentication" - } - }, - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "All Applications", - "description": "" - }, - "integrations": { - "Auth0": false - }, - "originalTimestamp": "2022-10-31T05:57:06.859Z" - }, - { - "type": "track", - "event": "Success API Operation", - "sentAt": "2022-10-31T05:57:06.874Z", - "userId": "auth0|dummyPassword", - "context": { - "library": { - "name": "unknown", - "version": "unknown" - }, - "traits": { - "userId": "auth0|dummyPassword" - }, - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "request_ip": "35.166.202.113", - "integration": { - "name": "Auth0" - } - }, - "properties": { - "log_id": "90020221031055712103169676686007898566320991926665347090", - "details": { - "request": { - "ip": "35.166.202.113", - "auth": { - "user": { - "name": "rudder test", - "email": "test@rudderstack.com", - "user_id": "auth0|dummyPassword" - }, - "strategy": "jwt", - "credentials": { - "jti": "571921bf7833a97efabf08d765a0ec8f", - "scopes": [ - "create:actions", - "create:actions_log_sessions", - "create:client_credentials", - "create:client_grants", - "create:clients", - "create:connections", - "create:custom_domains", - "create:email_provider", - "create:email_templates", - "create:guardian_enrollment_tickets", - "create:integrations", - "create:log_streams", - "create:organization_connections", - "create:organization_invitations", - "create:organization_member_roles", - "create:organization_members", - "create:organizations", - "create:requested_scopes", - "create:resource_servers", - "create:roles", - "create:rules", - "create:shields", - "create:signing_keys", - "create:tenant_invitations", - "create:test_email_dispatch", - "create:users", - "delete:actions", - "delete:anomaly_blocks", - "delete:branding", - "delete:client_credentials", - "delete:client_grants", - "delete:clients", - "delete:connections", - "delete:custom_domains", - "delete:device_credentials", - "delete:email_provider", - "delete:email_templates", - "delete:grants", - "delete:guardian_enrollments", - "delete:integrations", - "delete:log_streams", - "delete:organization_connections", - "delete:organization_invitations", - "delete:organization_member_roles", - "delete:organization_members", - "delete:organizations", - "delete:owners", - "delete:requested_scopes", - "delete:resource_servers", - "delete:roles", - "delete:rules", - "delete:rules_configs", - "delete:shields", - "delete:tenant_invitations", - "delete:tenant_members", - "delete:tenants", - "delete:users", - "read:actions", - "read:anomaly_blocks", - "read:attack_protection", - "read:branding", - "read:checks", - "read:client_credentials", - "read:client_grants", - "read:client_keys", - "read:clients", - "read:connections", - "read:custom_domains", - "read:device_credentials", - "read:email_provider", - "read:email_templates", - "read:email_triggers", - "read:entity_counts", - "read:grants", - "read:guardian_factors", - "read:insights", - "read:integrations", - "read:log_streams", - "read:logs", - "read:mfa_policies", - "read:organization_connections", - "read:organization_invitations", - "read:organization_member_roles", - "read:organization_members", - "read:organizations", - "read:prompts", - "read:requested_scopes", - "read:resource_servers", - "read:roles", - "read:rules", - "read:rules_configs", - "read:shields", - "read:signing_keys", - "read:stats", - "read:tenant_invitations", - "read:tenant_members", - "read:tenant_settings", - "read:triggers", - "read:users", - "run:checks", - "update:actions", - "update:attack_protection", - "update:branding", - "update:client_credentials", - "update:client_grants", - "update:client_keys", - "update:clients", - "update:connections", - "update:custom_domains", - "update:email_provider", - "update:email_templates", - "update:email_triggers", - "update:guardian_factors", - "update:integrations", - "update:log_streams", - "update:mfa_policies", - "update:organization_connections", - "update:organizations", - "update:prompts", - "update:requested_scopes", - "update:resource_servers", - "update:roles", - "update:rules", - "update:rules_configs", - "update:shields", - "update:signing_keys", - "update:tenant_members", - "update:tenant_settings", - "update:triggers", - "update:users" - ] - } - }, - "body": { - "email": "testRudderlabs+21@gmail.com", - "password": "dummyPassword", - "connection": "Username-Password-Authentication" - }, - "path": "/api/v2/users", - "query": {}, - "method": "post", - "channel": "https://manage.auth0.com/", - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" - }, - "response": { - "body": { - "name": "testRudderlabs+21@gmail.com", - "email": "testRudderlabs+21@gmail.com", - "picture": "https://s.gravatar.com/avatar/0902f9d02b92aed9f0ac59aaf9475b60?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fbh.png", - "user_id": "auth0|dummyPassword", - "nickname": "testRudderlabs+21", - "created_at": "2022-10-31T05:57:06.864Z", - "identities": [ - { - "user_id": "auth0|dummyPassword", - "isSocial": false, - "provider": "auth0", - "connection": "Username-Password-Authentication" - } - ], - "updated_at": "2022-10-31T05:57:06.864Z", - "email_verified": false - }, - "statusCode": 201 - } - }, - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "", - "description": "Create a User" - }, - "integrations": { - "Auth0": false - }, - "originalTimestamp": "2022-10-31T05:57:06.874Z" - } - ] - }, - { - "description": "Add member to an organization", - "input": [ - { - "log_id": "90020221031061004280169676882609459981150114445973782546", - "data": { - "date": "2022-10-31T06:09:59.135Z", - "type": "sapi", - "description": "Add members to an organization", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "", - "ip": "35.167.74.121", - "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "details": { - "request": { - "ip": "35.167.74.121", - "auth": { - "user": { - "name": "rudder test", - "email": "test@rudderstack.com", - "user_id": "google-oauth2|123456" - }, - "strategy": "jwt", - "credentials": { - "jti": "571921bf7833a97efabf08d765a0ec8f" - } - }, - "body": { - "members": ["auth0|123456"] - }, - "path": "/api/v2/organizations/org_eoe8p2atZ7furBxg/members", - "query": {}, - "method": "post", - "channel": "https://manage.auth0.com/", - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" - }, - "response": { - "body": {}, - "statusCode": 204 - } - }, - "user_id": "google-oauth2|123456", - "log_id": "90020221031061004280169676882609459981150114445973782546" - } - } - ], - "output": [ - { - "type": "group", - "sentAt": "2022-10-31T06:09:59.135Z", - "userId": "google-oauth2|123456", - "context": { - "library": { - "name": "unknown", - "version": "unknown" - }, - "traits": { - "userId": "google-oauth2|123456" - }, - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "request_ip": "35.167.74.121", - "integration": { - "name": "Auth0" - } - }, - "groupId": "org_eoe8p2atZ7furBxg", - "properties": { - "log_id": "90020221031061004280169676882609459981150114445973782546", - "details": { - "request": { - "ip": "35.167.74.121", - "auth": { - "user": { - "name": "rudder test", - "email": "test@rudderstack.com", - "user_id": "google-oauth2|123456" - }, - "strategy": "jwt", - "credentials": { - "jti": "571921bf7833a97efabf08d765a0ec8f" - } - }, - "body": { - "members": ["auth0|123456"] - }, - "path": "/api/v2/organizations/org_eoe8p2atZ7furBxg/members", - "query": {}, - "method": "post", - "channel": "https://manage.auth0.com/", - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" - }, - "response": { - "body": {}, - "statusCode": 204 - } - }, - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "", - "description": "Add members to an organization" - }, - "integrations": { - "Auth0": false - }, - "originalTimestamp": "2022-10-31T06:09:59.135Z" - } - ] - }, - { - "description": "Update tenant settings", - "input": [ - { - "log_id": "90020221031061527239169676960191065529099349299958906898", - "data": { - "date": "2022-10-31T06:15:25.201Z", - "type": "sapi", - "description": "Update tenant settings", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "", - "ip": "35.160.3.103", - "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "details": { - "request": { - "ip": "35.160.3.103", - "auth": { - "user": { - "name": "rudder test", - "email": "test@rudderstack.com", - "user_id": "google-oauth2|123456" - }, - "strategy": "jwt", - "credentials": { - "jti": "571921bf7833a97efabf08d765a0ec8f", - "scopes": [ - "create:actions", - "create:actions_log_sessions", - "create:client_credentials", - "create:client_grants", - "create:clients", - "create:connections", - "create:custom_domains", - "create:email_provider", - "create:email_templates", - "create:guardian_enrollment_tickets", - "create:integrations", - "create:log_streams", - "create:organization_connections", - "create:organization_invitations", - "create:organization_member_roles", - "create:organization_members", - "create:organizations", - "create:requested_scopes", - "create:resource_servers", - "create:roles", - "create:rules", - "create:shields", - "create:signing_keys", - "create:tenant_invitations", - "create:test_email_dispatch", - "create:users", - "delete:actions", - "delete:anomaly_blocks", - "delete:branding", - "delete:client_credentials", - "delete:client_grants", - "delete:clients", - "delete:connections", - "delete:custom_domains", - "delete:device_credentials", - "delete:email_provider", - "delete:email_templates", - "delete:grants", - "delete:guardian_enrollments", - "delete:integrations", - "delete:log_streams", - "delete:organization_connections", - "delete:organization_invitations", - "delete:organization_member_roles", - "delete:organization_members", - "delete:organizations", - "delete:owners", - "delete:requested_scopes", - "delete:resource_servers", - "delete:roles", - "delete:rules", - "delete:rules_configs", - "delete:shields", - "delete:tenant_invitations", - "delete:tenant_members", - "delete:tenants", - "delete:users", - "read:actions", - "read:anomaly_blocks", - "read:attack_protection", - "read:branding", - "read:checks", - "read:client_credentials", - "read:client_grants", - "read:client_keys", - "read:clients", - "read:connections", - "read:custom_domains", - "read:device_credentials", - "read:email_provider", - "read:email_templates", - "read:email_triggers", - "read:entity_counts", - "read:grants", - "read:guardian_factors", - "read:insights", - "read:integrations", - "read:log_streams", - "read:logs", - "read:mfa_policies", - "read:organization_connections", - "read:organization_invitations", - "read:organization_member_roles", - "read:organization_members", - "read:organizations", - "read:prompts", - "read:requested_scopes", - "read:resource_servers", - "read:roles", - "read:rules", - "read:rules_configs", - "read:shields", - "read:signing_keys", - "read:stats", - "read:tenant_invitations", - "read:tenant_members", - "read:tenant_settings", - "read:triggers", - "read:users", - "run:checks", - "update:actions", - "update:attack_protection", - "update:branding", - "update:client_credentials", - "update:client_grants", - "update:client_keys", - "update:clients", - "update:connections", - "update:custom_domains", - "update:email_provider", - "update:email_templates", - "update:email_triggers", - "update:guardian_factors", - "update:integrations", - "update:log_streams", - "update:mfa_policies", - "update:organization_connections", - "update:organizations", - "update:prompts", - "update:requested_scopes", - "update:resource_servers", - "update:roles", - "update:rules", - "update:rules_configs", - "update:shields", - "update:signing_keys", - "update:tenant_members", - "update:tenant_settings", - "update:triggers", - "update:users" - ] - } - }, - "body": { - "picture_url": "", - "support_url": "", - "friendly_name": "mecro-action", - "support_email": "support@test.com" - }, - "path": "/api/v2/tenants/settings", - "query": {}, - "method": "patch", - "channel": "https://manage.auth0.com/", - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" - }, - "response": { - "body": { - "flags": { - "enable_sso": true, - "universal_login": true, - "disable_impersonation": true, - "allow_changing_enable_sso": false, - "revoke_refresh_token_grant": false, - "disable_clickjack_protection_headers": false, - "new_universal_login_experience_enabled": true, - "enforce_client_authentication_on_passwordless_start": true, - "cannot_change_enforce_client_authentication_on_passwordless_start": true - }, - "picture_url": "", - "support_url": "", - "friendly_name": "mecro-action", - "support_email": "support@test.com", - "enabled_locales": ["en"], - "sandbox_version": "16", - "universal_login": {} - }, - "statusCode": 200 - } - }, - "user_id": "google-oauth2|123456", - "log_id": "90020221031061527239169676960191065529099349299958906898" - } - }, - { - "log_id": "90020221031061530247169676961198100736838335677367058450", - "data": { - "date": "2022-10-31T06:15:25.196Z", - "type": "gd_tenant_update", - "description": "Guardian - Updates tenant settings", - "ip": "35.160.3.103", - "details": { - "request": { - "ip": "35.160.3.103", - "auth": { - "scopes": [ - "read:authenticators", - "remove:authenticators", - "update:authenticators", - "create:authenticators", - "read:enrollments", - "delete:enrollments", - "read:factors", - "update:factors", - "update:tenant_settings", - "update:users", - "create:enrollment_tickets", - "create:users" - ], - "subject": "google-oauth2|123456", - "strategy": "jwt_api2_internal_token" - }, - "body": { - "picture_url": "[REDACTED]", - "friendly_name": "[REDACTED]" - }, - "path": "/api/tenants/settings", - "query": {}, - "method": "PATCH" - }, - "response": { - "body": { - "name": "dev-cu4jy2zgao6yx15x", - "picture_url": "[REDACTED]", - "friendly_name": "[REDACTED]", - "guardian_mfa_page": "[REDACTED]" - }, - "statusCode": 200 - } - }, - "user_id": "google-oauth2|123456", - "log_id": "90020221031061530247169676961198100736838335677367058450" - } - } - ], - "output": [ - { - "type": "track", - "event": "Success API Operation", - "sentAt": "2022-10-31T06:15:25.201Z", - "userId": "google-oauth2|123456", - "context": { - "library": { - "name": "unknown", - "version": "unknown" - }, - "traits": { - "userId": "google-oauth2|123456" - }, - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "request_ip": "35.160.3.103", - "integration": { - "name": "Auth0" - } - }, - "properties": { - "log_id": "90020221031061527239169676960191065529099349299958906898", - "details": { - "request": { - "ip": "35.160.3.103", - "auth": { - "user": { - "name": "rudder test", - "email": "test@rudderstack.com", - "user_id": "google-oauth2|123456" - }, - "strategy": "jwt", - "credentials": { - "jti": "571921bf7833a97efabf08d765a0ec8f", - "scopes": [ - "create:actions", - "create:actions_log_sessions", - "create:client_credentials", - "create:client_grants", - "create:clients", - "create:connections", - "create:custom_domains", - "create:email_provider", - "create:email_templates", - "create:guardian_enrollment_tickets", - "create:integrations", - "create:log_streams", - "create:organization_connections", - "create:organization_invitations", - "create:organization_member_roles", - "create:organization_members", - "create:organizations", - "create:requested_scopes", - "create:resource_servers", - "create:roles", - "create:rules", - "create:shields", - "create:signing_keys", - "create:tenant_invitations", - "create:test_email_dispatch", - "create:users", - "delete:actions", - "delete:anomaly_blocks", - "delete:branding", - "delete:client_credentials", - "delete:client_grants", - "delete:clients", - "delete:connections", - "delete:custom_domains", - "delete:device_credentials", - "delete:email_provider", - "delete:email_templates", - "delete:grants", - "delete:guardian_enrollments", - "delete:integrations", - "delete:log_streams", - "delete:organization_connections", - "delete:organization_invitations", - "delete:organization_member_roles", - "delete:organization_members", - "delete:organizations", - "delete:owners", - "delete:requested_scopes", - "delete:resource_servers", - "delete:roles", - "delete:rules", - "delete:rules_configs", - "delete:shields", - "delete:tenant_invitations", - "delete:tenant_members", - "delete:tenants", - "delete:users", - "read:actions", - "read:anomaly_blocks", - "read:attack_protection", - "read:branding", - "read:checks", - "read:client_credentials", - "read:client_grants", - "read:client_keys", - "read:clients", - "read:connections", - "read:custom_domains", - "read:device_credentials", - "read:email_provider", - "read:email_templates", - "read:email_triggers", - "read:entity_counts", - "read:grants", - "read:guardian_factors", - "read:insights", - "read:integrations", - "read:log_streams", - "read:logs", - "read:mfa_policies", - "read:organization_connections", - "read:organization_invitations", - "read:organization_member_roles", - "read:organization_members", - "read:organizations", - "read:prompts", - "read:requested_scopes", - "read:resource_servers", - "read:roles", - "read:rules", - "read:rules_configs", - "read:shields", - "read:signing_keys", - "read:stats", - "read:tenant_invitations", - "read:tenant_members", - "read:tenant_settings", - "read:triggers", - "read:users", - "run:checks", - "update:actions", - "update:attack_protection", - "update:branding", - "update:client_credentials", - "update:client_grants", - "update:client_keys", - "update:clients", - "update:connections", - "update:custom_domains", - "update:email_provider", - "update:email_templates", - "update:email_triggers", - "update:guardian_factors", - "update:integrations", - "update:log_streams", - "update:mfa_policies", - "update:organization_connections", - "update:organizations", - "update:prompts", - "update:requested_scopes", - "update:resource_servers", - "update:roles", - "update:rules", - "update:rules_configs", - "update:shields", - "update:signing_keys", - "update:tenant_members", - "update:tenant_settings", - "update:triggers", - "update:users" - ] - } - }, - "body": { - "picture_url": "", - "support_url": "", - "friendly_name": "mecro-action", - "support_email": "support@test.com" - }, - "path": "/api/v2/tenants/settings", - "query": {}, - "method": "patch", - "channel": "https://manage.auth0.com/", - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" - }, - "response": { - "body": { - "flags": { - "enable_sso": true, - "universal_login": true, - "disable_impersonation": true, - "allow_changing_enable_sso": false, - "revoke_refresh_token_grant": false, - "disable_clickjack_protection_headers": false, - "new_universal_login_experience_enabled": true, - "enforce_client_authentication_on_passwordless_start": true, - "cannot_change_enforce_client_authentication_on_passwordless_start": true - }, - "picture_url": "", - "support_url": "", - "friendly_name": "mecro-action", - "support_email": "support@test.com", - "enabled_locales": ["en"], - "sandbox_version": "16", - "universal_login": {} - }, - "statusCode": 200 - } - }, - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "", - "description": "Update tenant settings" - }, - "integrations": { - "Auth0": false - }, - "originalTimestamp": "2022-10-31T06:15:25.201Z" - }, - { - "type": "track", - "event": "Guardian tenant update", - "sentAt": "2022-10-31T06:15:25.196Z", - "userId": "google-oauth2|123456", - "context": { - "library": { - "name": "unknown", - "version": "unknown" - }, - "traits": { - "userId": "google-oauth2|123456" - }, - "request_ip": "35.160.3.103", - "integration": { - "name": "Auth0" - } - }, - "properties": { - "log_id": "90020221031061530247169676961198100736838335677367058450", - "details": { - "request": { - "ip": "35.160.3.103", - "auth": { - "scopes": [ - "read:authenticators", - "remove:authenticators", - "update:authenticators", - "create:authenticators", - "read:enrollments", - "delete:enrollments", - "read:factors", - "update:factors", - "update:tenant_settings", - "update:users", - "create:enrollment_tickets", - "create:users" - ], - "subject": "google-oauth2|123456", - "strategy": "jwt_api2_internal_token" - }, - "body": { - "picture_url": "[REDACTED]", - "friendly_name": "[REDACTED]" - }, - "path": "/api/tenants/settings", - "query": {}, - "method": "PATCH" - }, - "response": { - "body": { - "name": "dev-cu4jy2zgao6yx15x", - "picture_url": "[REDACTED]", - "friendly_name": "[REDACTED]", - "guardian_mfa_page": "[REDACTED]" - }, - "statusCode": 200 - } - }, - "description": "Guardian - Updates tenant settings" - }, - "integrations": { - "Auth0": false - }, - "originalTimestamp": "2022-10-31T06:15:25.196Z" - } - ] - }, - { - "description": "Missing userId", - "input": { - "log_id": "90020221031055712103169676686005480714681762668315934738", - "data": { - "date": "2022-10-31T05:57:06.859Z", - "type": "ss", - "description": "", - "connection": "Username-Password-Authentication", - "connection_id": "con_djwCjiwyID0vZy1S", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "All Applications", - "ip": "35.166.202.113", - "user_agent": "unknown", - "details": { - "body": { - "email": "testRudderlabs+21@gmail.com", - "tenant": "dev-cu4jy2zgao6yx15x", - "password": "dummyPassword", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "connection": "Username-Password-Authentication" - } - }, - "user_id": "", - "user_name": "testRudderlabs+21@gmail.com", - "strategy": "auth0", - "strategy_type": "database", - "log_id": "90020221031055712103169676686005480714681762668315934738" - } - }, - "output": [ - { - "type": "identify", - "sentAt": "2022-10-31T05:57:06.859Z", - "traits": { - "connection": "Username-Password-Authentication", - "connection_id": "con_djwCjiwyID0vZy1S" - }, - "userId": "", - "context": { - "traits": { - "userId": "", - "user_name": "testRudderlabs+21@gmail.com" - }, - "library": { - "name": "unknown", - "version": "unknown" - }, - "userAgent": "unknown", - "request_ip": "35.166.202.113", - "integration": { - "name": "Auth0" - } - }, - "properties": { - "log_id": "90020221031055712103169676686005480714681762668315934738", - "details": { - "body": { - "email": "testRudderlabs+21@gmail.com", - "tenant": "dev-cu4jy2zgao6yx15x", - "password": "dummyPassword", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "connection": "Username-Password-Authentication" - } - }, - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "All Applications", - "description": "" - }, - "integrations": { - "Auth0": false - }, - "originalTimestamp": "2022-10-31T05:57:06.859Z" - } - ] - }, - { - "description": "UserId is missing for all the requests in a batch", - "input": [ - { - "log_id": "90020221031055712103169676686005480714681762668315934738", - "data": { - "date": "2022-10-31T05:57:06.859Z", - "type": "ss", - "description": "", - "connection": "Username-Password-Authentication", - "connection_id": "con_djwCjiwyID0vZy1S", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "All Applications", - "ip": "35.166.202.113", - "user_agent": "unknown", - "details": { - "body": { - "email": "testRudderlabs+21@gmail.com", - "tenant": "dev-cu4jy2zgao6yx15x", - "password": "dummyPassword", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "connection": "Username-Password-Authentication" - } - }, - "user_id": "", - "user_name": "testRudderlabs+21@gmail.com", - "strategy": "auth0", - "strategy_type": "database", - "log_id": "90020221031055712103169676686005480714681762668315934738" - } - }, - { - "log_id": "90020221031055712103169676686007898566320991926665347090", - "data": { - "date": "2022-10-31T05:57:06.874Z", - "type": "sapi", - "description": "Create a User", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "", - "ip": "35.166.202.113", - "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "log_id": "90020221031055712103169676686007898566320991926665347090" - } - } - ], - "output": [ - { - "type": "identify", - "userId": "", - "sentAt": "2022-10-31T05:57:06.859Z", - "traits": { - "connection": "Username-Password-Authentication", - "connection_id": "con_djwCjiwyID0vZy1S" - }, - "context": { - "traits": { - "userId": "", - "user_name": "testRudderlabs+21@gmail.com" - }, - "library": { - "name": "unknown", - "version": "unknown" - }, - "userAgent": "unknown", - "request_ip": "35.166.202.113", - "integration": { - "name": "Auth0" - } - }, - "properties": { - "log_id": "90020221031055712103169676686005480714681762668315934738", - "details": { - "body": { - "email": "testRudderlabs+21@gmail.com", - "tenant": "dev-cu4jy2zgao6yx15x", - "password": "dummyPassword", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "connection": "Username-Password-Authentication" - } - }, - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "All Applications", - "description": "" - }, - "integrations": { - "Auth0": false - }, - "originalTimestamp": "2022-10-31T05:57:06.859Z" - }, - { - "type": "track", - "event": "Success API Operation", - "sentAt": "2022-10-31T05:57:06.874Z", - "context": { - "library": { - "name": "unknown", - "version": "unknown" - }, - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "request_ip": "35.166.202.113", - "integration": { - "name": "Auth0" - } - }, - "properties": { - "log_id": "90020221031055712103169676686007898566320991926665347090", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "", - "description": "Create a User" - }, - "integrations": { - "Auth0": false - }, - "originalTimestamp": "2022-10-31T05:57:06.874Z" - } - ] - } -] diff --git a/test/integrations/sources/auth0/data.ts b/test/integrations/sources/auth0/data.ts new file mode 100644 index 0000000000..66aab00182 --- /dev/null +++ b/test/integrations/sources/auth0/data.ts @@ -0,0 +1,1493 @@ +import utils from '../../../../src/v0/util'; + +const defaultMockFns = () => { + jest.spyOn(utils, 'generateUUID').mockReturnValue('97fcd7b2-cc24-47d7-b776-057b7b199513'); +}; + +export const data = [ + { + name: 'auth0', + description: 'successful signup', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + log_id: '90020221031055712103169676686005480714681762668315934738', + data: { + date: '2022-10-31T05:57:06.859Z', + type: 'ss', + description: '', + connection: 'Username-Password-Authentication', + connection_id: 'con_djwCjiwyID0vZy1S', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: 'All Applications', + ip: '35.166.202.113', + user_agent: 'unknown', + details: { + body: { + email: 'testRudderlabs+21@gmail.com', + tenant: 'dev-cu4jy2zgao6yx15x', + password: 'dummyPassword', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + connection: 'Username-Password-Authentication', + }, + }, + user_id: 'auth0|dummyPassword', + user_name: 'testRudderlabs+21@gmail.com', + strategy: 'auth0', + strategy_type: 'database', + log_id: '90020221031055712103169676686005480714681762668315934738', + }, + }, + { + log_id: '90020221031055712103169676686007898566320991926665347090', + data: { + date: '2022-10-31T05:57:06.874Z', + type: 'sapi', + description: 'Create a User', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + ip: '35.166.202.113', + user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + details: { + request: { + ip: '35.166.202.113', + auth: { + user: { + name: 'rudder test', + email: 'test@rudderstack.com', + user_id: 'auth0|dummyPassword', + }, + strategy: 'jwt', + credentials: { + jti: '571921bf7833a97efabf08d765a0ec8f', + scopes: [ + 'create:actions', + 'create:actions_log_sessions', + 'create:client_credentials', + 'create:client_grants', + 'create:clients', + 'create:connections', + 'create:custom_domains', + 'create:email_provider', + 'create:email_templates', + 'create:guardian_enrollment_tickets', + 'create:integrations', + 'create:log_streams', + 'create:organization_connections', + 'create:organization_invitations', + 'create:organization_member_roles', + 'create:organization_members', + 'create:organizations', + 'create:requested_scopes', + 'create:resource_servers', + 'create:roles', + 'create:rules', + 'create:shields', + 'create:signing_keys', + 'create:tenant_invitations', + 'create:test_email_dispatch', + 'create:users', + 'delete:actions', + 'delete:anomaly_blocks', + 'delete:branding', + 'delete:client_credentials', + 'delete:client_grants', + 'delete:clients', + 'delete:connections', + 'delete:custom_domains', + 'delete:device_credentials', + 'delete:email_provider', + 'delete:email_templates', + 'delete:grants', + 'delete:guardian_enrollments', + 'delete:integrations', + 'delete:log_streams', + 'delete:organization_connections', + 'delete:organization_invitations', + 'delete:organization_member_roles', + 'delete:organization_members', + 'delete:organizations', + 'delete:owners', + 'delete:requested_scopes', + 'delete:resource_servers', + 'delete:roles', + 'delete:rules', + 'delete:rules_configs', + 'delete:shields', + 'delete:tenant_invitations', + 'delete:tenant_members', + 'delete:tenants', + 'delete:users', + 'read:actions', + 'read:anomaly_blocks', + 'read:attack_protection', + 'read:branding', + 'read:checks', + 'read:client_credentials', + 'read:client_grants', + 'read:client_keys', + 'read:clients', + 'read:connections', + 'read:custom_domains', + 'read:device_credentials', + 'read:email_provider', + 'read:email_templates', + 'read:email_triggers', + 'read:entity_counts', + 'read:grants', + 'read:guardian_factors', + 'read:insights', + 'read:integrations', + 'read:log_streams', + 'read:logs', + 'read:mfa_policies', + 'read:organization_connections', + 'read:organization_invitations', + 'read:organization_member_roles', + 'read:organization_members', + 'read:organizations', + 'read:prompts', + 'read:requested_scopes', + 'read:resource_servers', + 'read:roles', + 'read:rules', + 'read:rules_configs', + 'read:shields', + 'read:signing_keys', + 'read:stats', + 'read:tenant_invitations', + 'read:tenant_members', + 'read:tenant_settings', + 'read:triggers', + 'read:users', + 'run:checks', + 'update:actions', + 'update:attack_protection', + 'update:branding', + 'update:client_credentials', + 'update:client_grants', + 'update:client_keys', + 'update:clients', + 'update:connections', + 'update:custom_domains', + 'update:email_provider', + 'update:email_templates', + 'update:email_triggers', + 'update:guardian_factors', + 'update:integrations', + 'update:log_streams', + 'update:mfa_policies', + 'update:organization_connections', + 'update:organizations', + 'update:prompts', + 'update:requested_scopes', + 'update:resource_servers', + 'update:roles', + 'update:rules', + 'update:rules_configs', + 'update:shields', + 'update:signing_keys', + 'update:tenant_members', + 'update:tenant_settings', + 'update:triggers', + 'update:users', + ], + }, + }, + body: { + email: 'testRudderlabs+21@gmail.com', + password: 'dummyPassword', + connection: 'Username-Password-Authentication', + }, + path: '/api/v2/users', + query: {}, + method: 'post', + channel: 'https://manage.auth0.com/', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + }, + response: { + body: { + name: 'testRudderlabs+21@gmail.com', + email: 'testRudderlabs+21@gmail.com', + picture: + 'https://s.gravatar.com/avatar/0902f9d02b92aed9f0ac59aaf9475b60?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fbh.png', + user_id: 'auth0|dummyPassword', + nickname: 'testRudderlabs+21', + created_at: '2022-10-31T05:57:06.864Z', + identities: [ + { + user_id: 'auth0|dummyPassword', + isSocial: false, + provider: 'auth0', + connection: 'Username-Password-Authentication', + }, + ], + updated_at: '2022-10-31T05:57:06.864Z', + email_verified: false, + }, + statusCode: 201, + }, + }, + user_id: 'auth0|dummyPassword', + log_id: '90020221031055712103169676686007898566320991926665347090', + }, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + type: 'identify', + sentAt: '2022-10-31T05:57:06.859Z', + traits: { + connection: 'Username-Password-Authentication', + connection_id: 'con_djwCjiwyID0vZy1S', + }, + userId: 'auth0|dummyPassword', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + traits: { + userId: 'auth0|dummyPassword', + user_name: 'testRudderlabs+21@gmail.com', + }, + library: { + name: 'unknown', + version: 'unknown', + }, + userAgent: 'unknown', + request_ip: '35.166.202.113', + integration: { + name: 'Auth0', + }, + }, + properties: { + log_id: '90020221031055712103169676686005480714681762668315934738', + details: { + body: { + email: 'testRudderlabs+21@gmail.com', + tenant: 'dev-cu4jy2zgao6yx15x', + password: 'dummyPassword', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + connection: 'Username-Password-Authentication', + }, + }, + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: 'All Applications', + description: '', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T05:57:06.859Z', + }, + ], + }, + }, + { + output: { + batch: [ + { + type: 'track', + event: 'Success API Operation', + sentAt: '2022-10-31T05:57:06.874Z', + userId: 'auth0|dummyPassword', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + traits: { + userId: 'auth0|dummyPassword', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + request_ip: '35.166.202.113', + integration: { + name: 'Auth0', + }, + }, + properties: { + log_id: '90020221031055712103169676686007898566320991926665347090', + details: { + request: { + ip: '35.166.202.113', + auth: { + user: { + name: 'rudder test', + email: 'test@rudderstack.com', + user_id: 'auth0|dummyPassword', + }, + strategy: 'jwt', + credentials: { + jti: '571921bf7833a97efabf08d765a0ec8f', + scopes: [ + 'create:actions', + 'create:actions_log_sessions', + 'create:client_credentials', + 'create:client_grants', + 'create:clients', + 'create:connections', + 'create:custom_domains', + 'create:email_provider', + 'create:email_templates', + 'create:guardian_enrollment_tickets', + 'create:integrations', + 'create:log_streams', + 'create:organization_connections', + 'create:organization_invitations', + 'create:organization_member_roles', + 'create:organization_members', + 'create:organizations', + 'create:requested_scopes', + 'create:resource_servers', + 'create:roles', + 'create:rules', + 'create:shields', + 'create:signing_keys', + 'create:tenant_invitations', + 'create:test_email_dispatch', + 'create:users', + 'delete:actions', + 'delete:anomaly_blocks', + 'delete:branding', + 'delete:client_credentials', + 'delete:client_grants', + 'delete:clients', + 'delete:connections', + 'delete:custom_domains', + 'delete:device_credentials', + 'delete:email_provider', + 'delete:email_templates', + 'delete:grants', + 'delete:guardian_enrollments', + 'delete:integrations', + 'delete:log_streams', + 'delete:organization_connections', + 'delete:organization_invitations', + 'delete:organization_member_roles', + 'delete:organization_members', + 'delete:organizations', + 'delete:owners', + 'delete:requested_scopes', + 'delete:resource_servers', + 'delete:roles', + 'delete:rules', + 'delete:rules_configs', + 'delete:shields', + 'delete:tenant_invitations', + 'delete:tenant_members', + 'delete:tenants', + 'delete:users', + 'read:actions', + 'read:anomaly_blocks', + 'read:attack_protection', + 'read:branding', + 'read:checks', + 'read:client_credentials', + 'read:client_grants', + 'read:client_keys', + 'read:clients', + 'read:connections', + 'read:custom_domains', + 'read:device_credentials', + 'read:email_provider', + 'read:email_templates', + 'read:email_triggers', + 'read:entity_counts', + 'read:grants', + 'read:guardian_factors', + 'read:insights', + 'read:integrations', + 'read:log_streams', + 'read:logs', + 'read:mfa_policies', + 'read:organization_connections', + 'read:organization_invitations', + 'read:organization_member_roles', + 'read:organization_members', + 'read:organizations', + 'read:prompts', + 'read:requested_scopes', + 'read:resource_servers', + 'read:roles', + 'read:rules', + 'read:rules_configs', + 'read:shields', + 'read:signing_keys', + 'read:stats', + 'read:tenant_invitations', + 'read:tenant_members', + 'read:tenant_settings', + 'read:triggers', + 'read:users', + 'run:checks', + 'update:actions', + 'update:attack_protection', + 'update:branding', + 'update:client_credentials', + 'update:client_grants', + 'update:client_keys', + 'update:clients', + 'update:connections', + 'update:custom_domains', + 'update:email_provider', + 'update:email_templates', + 'update:email_triggers', + 'update:guardian_factors', + 'update:integrations', + 'update:log_streams', + 'update:mfa_policies', + 'update:organization_connections', + 'update:organizations', + 'update:prompts', + 'update:requested_scopes', + 'update:resource_servers', + 'update:roles', + 'update:rules', + 'update:rules_configs', + 'update:shields', + 'update:signing_keys', + 'update:tenant_members', + 'update:tenant_settings', + 'update:triggers', + 'update:users', + ], + }, + }, + body: { + email: 'testRudderlabs+21@gmail.com', + password: 'dummyPassword', + connection: 'Username-Password-Authentication', + }, + path: '/api/v2/users', + query: {}, + method: 'post', + channel: 'https://manage.auth0.com/', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + }, + response: { + body: { + name: 'testRudderlabs+21@gmail.com', + email: 'testRudderlabs+21@gmail.com', + picture: + 'https://s.gravatar.com/avatar/0902f9d02b92aed9f0ac59aaf9475b60?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fbh.png', + user_id: 'auth0|dummyPassword', + nickname: 'testRudderlabs+21', + created_at: '2022-10-31T05:57:06.864Z', + identities: [ + { + user_id: 'auth0|dummyPassword', + isSocial: false, + provider: 'auth0', + connection: 'Username-Password-Authentication', + }, + ], + updated_at: '2022-10-31T05:57:06.864Z', + email_verified: false, + }, + statusCode: 201, + }, + }, + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + description: 'Create a User', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T05:57:06.874Z', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'auth0', + description: 'add member to an organization', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + log_id: '90020221031061004280169676882609459981150114445973782546', + data: { + date: '2022-10-31T06:09:59.135Z', + type: 'sapi', + description: 'Add members to an organization', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + ip: '35.167.74.121', + user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + details: { + request: { + ip: '35.167.74.121', + auth: { + user: { + name: 'rudder test', + email: 'test@rudderstack.com', + user_id: 'google-oauth2|123456', + }, + strategy: 'jwt', + credentials: { + jti: '571921bf7833a97efabf08d765a0ec8f', + }, + }, + body: { + members: ['auth0|123456'], + }, + path: '/api/v2/organizations/org_eoe8p2atZ7furBxg/members', + query: {}, + method: 'post', + channel: 'https://manage.auth0.com/', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + }, + response: { + body: {}, + statusCode: 204, + }, + }, + user_id: 'google-oauth2|123456', + log_id: '90020221031061004280169676882609459981150114445973782546', + }, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + type: 'group', + sentAt: '2022-10-31T06:09:59.135Z', + userId: 'google-oauth2|123456', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + traits: { + userId: 'google-oauth2|123456', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + request_ip: '35.167.74.121', + integration: { + name: 'Auth0', + }, + }, + groupId: 'org_eoe8p2atZ7furBxg', + properties: { + log_id: '90020221031061004280169676882609459981150114445973782546', + details: { + request: { + ip: '35.167.74.121', + auth: { + user: { + name: 'rudder test', + email: 'test@rudderstack.com', + user_id: 'google-oauth2|123456', + }, + strategy: 'jwt', + credentials: { + jti: '571921bf7833a97efabf08d765a0ec8f', + }, + }, + body: { + members: ['auth0|123456'], + }, + path: '/api/v2/organizations/org_eoe8p2atZ7furBxg/members', + query: {}, + method: 'post', + channel: 'https://manage.auth0.com/', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + }, + response: { + body: {}, + statusCode: 204, + }, + }, + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + description: 'Add members to an organization', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T06:09:59.135Z', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'auth0', + description: 'update tenant settings', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + log_id: '90020221031061527239169676960191065529099349299958906898', + data: { + date: '2022-10-31T06:15:25.201Z', + type: 'sapi', + description: 'Update tenant settings', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + ip: '35.160.3.103', + user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + details: { + request: { + ip: '35.160.3.103', + auth: { + user: { + name: 'rudder test', + email: 'test@rudderstack.com', + user_id: 'google-oauth2|123456', + }, + strategy: 'jwt', + credentials: { + jti: '571921bf7833a97efabf08d765a0ec8f', + scopes: [ + 'create:actions', + 'create:actions_log_sessions', + 'create:client_credentials', + 'create:client_grants', + 'create:clients', + 'create:connections', + 'create:custom_domains', + 'create:email_provider', + 'create:email_templates', + 'create:guardian_enrollment_tickets', + 'create:integrations', + 'create:log_streams', + 'create:organization_connections', + 'create:organization_invitations', + 'create:organization_member_roles', + 'create:organization_members', + 'create:organizations', + 'create:requested_scopes', + 'create:resource_servers', + 'create:roles', + 'create:rules', + 'create:shields', + 'create:signing_keys', + 'create:tenant_invitations', + 'create:test_email_dispatch', + 'create:users', + 'delete:actions', + 'delete:anomaly_blocks', + 'delete:branding', + 'delete:client_credentials', + 'delete:client_grants', + 'delete:clients', + 'delete:connections', + 'delete:custom_domains', + 'delete:device_credentials', + 'delete:email_provider', + 'delete:email_templates', + 'delete:grants', + 'delete:guardian_enrollments', + 'delete:integrations', + 'delete:log_streams', + 'delete:organization_connections', + 'delete:organization_invitations', + 'delete:organization_member_roles', + 'delete:organization_members', + 'delete:organizations', + 'delete:owners', + 'delete:requested_scopes', + 'delete:resource_servers', + 'delete:roles', + 'delete:rules', + 'delete:rules_configs', + 'delete:shields', + 'delete:tenant_invitations', + 'delete:tenant_members', + 'delete:tenants', + 'delete:users', + 'read:actions', + 'read:anomaly_blocks', + 'read:attack_protection', + 'read:branding', + 'read:checks', + 'read:client_credentials', + 'read:client_grants', + 'read:client_keys', + 'read:clients', + 'read:connections', + 'read:custom_domains', + 'read:device_credentials', + 'read:email_provider', + 'read:email_templates', + 'read:email_triggers', + 'read:entity_counts', + 'read:grants', + 'read:guardian_factors', + 'read:insights', + 'read:integrations', + 'read:log_streams', + 'read:logs', + 'read:mfa_policies', + 'read:organization_connections', + 'read:organization_invitations', + 'read:organization_member_roles', + 'read:organization_members', + 'read:organizations', + 'read:prompts', + 'read:requested_scopes', + 'read:resource_servers', + 'read:roles', + 'read:rules', + 'read:rules_configs', + 'read:shields', + 'read:signing_keys', + 'read:stats', + 'read:tenant_invitations', + 'read:tenant_members', + 'read:tenant_settings', + 'read:triggers', + 'read:users', + 'run:checks', + 'update:actions', + 'update:attack_protection', + 'update:branding', + 'update:client_credentials', + 'update:client_grants', + 'update:client_keys', + 'update:clients', + 'update:connections', + 'update:custom_domains', + 'update:email_provider', + 'update:email_templates', + 'update:email_triggers', + 'update:guardian_factors', + 'update:integrations', + 'update:log_streams', + 'update:mfa_policies', + 'update:organization_connections', + 'update:organizations', + 'update:prompts', + 'update:requested_scopes', + 'update:resource_servers', + 'update:roles', + 'update:rules', + 'update:rules_configs', + 'update:shields', + 'update:signing_keys', + 'update:tenant_members', + 'update:tenant_settings', + 'update:triggers', + 'update:users', + ], + }, + }, + body: { + picture_url: '', + support_url: '', + friendly_name: 'mecro-action', + support_email: 'support@test.com', + }, + path: '/api/v2/tenants/settings', + query: {}, + method: 'patch', + channel: 'https://manage.auth0.com/', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + }, + response: { + body: { + flags: { + enable_sso: true, + universal_login: true, + disable_impersonation: true, + allow_changing_enable_sso: false, + revoke_refresh_token_grant: false, + disable_clickjack_protection_headers: false, + new_universal_login_experience_enabled: true, + enforce_client_authentication_on_passwordless_start: true, + cannot_change_enforce_client_authentication_on_passwordless_start: true, + }, + picture_url: '', + support_url: '', + friendly_name: 'mecro-action', + support_email: 'support@test.com', + enabled_locales: ['en'], + sandbox_version: '16', + universal_login: {}, + }, + statusCode: 200, + }, + }, + user_id: 'google-oauth2|123456', + log_id: '90020221031061527239169676960191065529099349299958906898', + }, + }, + { + log_id: '90020221031061530247169676961198100736838335677367058450', + data: { + date: '2022-10-31T06:15:25.196Z', + type: 'gd_tenant_update', + description: 'Guardian - Updates tenant settings', + ip: '35.160.3.103', + details: { + request: { + ip: '35.160.3.103', + auth: { + scopes: [ + 'read:authenticators', + 'remove:authenticators', + 'update:authenticators', + 'create:authenticators', + 'read:enrollments', + 'delete:enrollments', + 'read:factors', + 'update:factors', + 'update:tenant_settings', + 'update:users', + 'create:enrollment_tickets', + 'create:users', + ], + subject: 'google-oauth2|123456', + strategy: 'jwt_api2_internal_token', + }, + body: { + picture_url: '[REDACTED]', + friendly_name: '[REDACTED]', + }, + path: '/api/tenants/settings', + query: {}, + method: 'PATCH', + }, + response: { + body: { + name: 'dev-cu4jy2zgao6yx15x', + picture_url: '[REDACTED]', + friendly_name: '[REDACTED]', + guardian_mfa_page: '[REDACTED]', + }, + statusCode: 200, + }, + }, + user_id: 'google-oauth2|123456', + log_id: '90020221031061530247169676961198100736838335677367058450', + }, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + type: 'track', + event: 'Success API Operation', + sentAt: '2022-10-31T06:15:25.201Z', + userId: 'google-oauth2|123456', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + traits: { + userId: 'google-oauth2|123456', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + request_ip: '35.160.3.103', + integration: { + name: 'Auth0', + }, + }, + properties: { + log_id: '90020221031061527239169676960191065529099349299958906898', + details: { + request: { + ip: '35.160.3.103', + auth: { + user: { + name: 'rudder test', + email: 'test@rudderstack.com', + user_id: 'google-oauth2|123456', + }, + strategy: 'jwt', + credentials: { + jti: '571921bf7833a97efabf08d765a0ec8f', + scopes: [ + 'create:actions', + 'create:actions_log_sessions', + 'create:client_credentials', + 'create:client_grants', + 'create:clients', + 'create:connections', + 'create:custom_domains', + 'create:email_provider', + 'create:email_templates', + 'create:guardian_enrollment_tickets', + 'create:integrations', + 'create:log_streams', + 'create:organization_connections', + 'create:organization_invitations', + 'create:organization_member_roles', + 'create:organization_members', + 'create:organizations', + 'create:requested_scopes', + 'create:resource_servers', + 'create:roles', + 'create:rules', + 'create:shields', + 'create:signing_keys', + 'create:tenant_invitations', + 'create:test_email_dispatch', + 'create:users', + 'delete:actions', + 'delete:anomaly_blocks', + 'delete:branding', + 'delete:client_credentials', + 'delete:client_grants', + 'delete:clients', + 'delete:connections', + 'delete:custom_domains', + 'delete:device_credentials', + 'delete:email_provider', + 'delete:email_templates', + 'delete:grants', + 'delete:guardian_enrollments', + 'delete:integrations', + 'delete:log_streams', + 'delete:organization_connections', + 'delete:organization_invitations', + 'delete:organization_member_roles', + 'delete:organization_members', + 'delete:organizations', + 'delete:owners', + 'delete:requested_scopes', + 'delete:resource_servers', + 'delete:roles', + 'delete:rules', + 'delete:rules_configs', + 'delete:shields', + 'delete:tenant_invitations', + 'delete:tenant_members', + 'delete:tenants', + 'delete:users', + 'read:actions', + 'read:anomaly_blocks', + 'read:attack_protection', + 'read:branding', + 'read:checks', + 'read:client_credentials', + 'read:client_grants', + 'read:client_keys', + 'read:clients', + 'read:connections', + 'read:custom_domains', + 'read:device_credentials', + 'read:email_provider', + 'read:email_templates', + 'read:email_triggers', + 'read:entity_counts', + 'read:grants', + 'read:guardian_factors', + 'read:insights', + 'read:integrations', + 'read:log_streams', + 'read:logs', + 'read:mfa_policies', + 'read:organization_connections', + 'read:organization_invitations', + 'read:organization_member_roles', + 'read:organization_members', + 'read:organizations', + 'read:prompts', + 'read:requested_scopes', + 'read:resource_servers', + 'read:roles', + 'read:rules', + 'read:rules_configs', + 'read:shields', + 'read:signing_keys', + 'read:stats', + 'read:tenant_invitations', + 'read:tenant_members', + 'read:tenant_settings', + 'read:triggers', + 'read:users', + 'run:checks', + 'update:actions', + 'update:attack_protection', + 'update:branding', + 'update:client_credentials', + 'update:client_grants', + 'update:client_keys', + 'update:clients', + 'update:connections', + 'update:custom_domains', + 'update:email_provider', + 'update:email_templates', + 'update:email_triggers', + 'update:guardian_factors', + 'update:integrations', + 'update:log_streams', + 'update:mfa_policies', + 'update:organization_connections', + 'update:organizations', + 'update:prompts', + 'update:requested_scopes', + 'update:resource_servers', + 'update:roles', + 'update:rules', + 'update:rules_configs', + 'update:shields', + 'update:signing_keys', + 'update:tenant_members', + 'update:tenant_settings', + 'update:triggers', + 'update:users', + ], + }, + }, + body: { + picture_url: '', + support_url: '', + friendly_name: 'mecro-action', + support_email: 'support@test.com', + }, + path: '/api/v2/tenants/settings', + query: {}, + method: 'patch', + channel: 'https://manage.auth0.com/', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + }, + response: { + body: { + flags: { + enable_sso: true, + universal_login: true, + disable_impersonation: true, + allow_changing_enable_sso: false, + revoke_refresh_token_grant: false, + disable_clickjack_protection_headers: false, + new_universal_login_experience_enabled: true, + enforce_client_authentication_on_passwordless_start: true, + cannot_change_enforce_client_authentication_on_passwordless_start: true, + }, + picture_url: '', + support_url: '', + friendly_name: 'mecro-action', + support_email: 'support@test.com', + enabled_locales: ['en'], + sandbox_version: '16', + universal_login: {}, + }, + statusCode: 200, + }, + }, + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + description: 'Update tenant settings', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T06:15:25.201Z', + }, + ], + }, + }, + { + output: { + batch: [ + { + type: 'track', + event: 'Guardian tenant update', + sentAt: '2022-10-31T06:15:25.196Z', + userId: 'google-oauth2|123456', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + traits: { + userId: 'google-oauth2|123456', + }, + request_ip: '35.160.3.103', + integration: { + name: 'Auth0', + }, + }, + properties: { + log_id: '90020221031061530247169676961198100736838335677367058450', + details: { + request: { + ip: '35.160.3.103', + auth: { + scopes: [ + 'read:authenticators', + 'remove:authenticators', + 'update:authenticators', + 'create:authenticators', + 'read:enrollments', + 'delete:enrollments', + 'read:factors', + 'update:factors', + 'update:tenant_settings', + 'update:users', + 'create:enrollment_tickets', + 'create:users', + ], + subject: 'google-oauth2|123456', + strategy: 'jwt_api2_internal_token', + }, + body: { + picture_url: '[REDACTED]', + friendly_name: '[REDACTED]', + }, + path: '/api/tenants/settings', + query: {}, + method: 'PATCH', + }, + response: { + body: { + name: 'dev-cu4jy2zgao6yx15x', + picture_url: '[REDACTED]', + friendly_name: '[REDACTED]', + guardian_mfa_page: '[REDACTED]', + }, + statusCode: 200, + }, + }, + description: 'Guardian - Updates tenant settings', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T06:15:25.196Z', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'auth0', + description: 'missing userId', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + log_id: '90020221031055712103169676686005480714681762668315934738', + data: { + date: '2022-10-31T05:57:06.859Z', + type: 'ss', + description: '', + connection: 'Username-Password-Authentication', + connection_id: 'con_djwCjiwyID0vZy1S', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: 'All Applications', + ip: '35.166.202.113', + user_agent: 'unknown', + details: { + body: { + email: 'testRudderlabs+21@gmail.com', + tenant: 'dev-cu4jy2zgao6yx15x', + password: 'dummyPassword', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + connection: 'Username-Password-Authentication', + }, + }, + user_id: '', + user_name: 'testRudderlabs+21@gmail.com', + strategy: 'auth0', + strategy_type: 'database', + log_id: '90020221031055712103169676686005480714681762668315934738', + }, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + type: 'identify', + sentAt: '2022-10-31T05:57:06.859Z', + traits: { + connection: 'Username-Password-Authentication', + connection_id: 'con_djwCjiwyID0vZy1S', + }, + userId: '', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + traits: { + userId: '', + user_name: 'testRudderlabs+21@gmail.com', + }, + library: { + name: 'unknown', + version: 'unknown', + }, + userAgent: 'unknown', + request_ip: '35.166.202.113', + integration: { + name: 'Auth0', + }, + }, + properties: { + log_id: '90020221031055712103169676686005480714681762668315934738', + details: { + body: { + email: 'testRudderlabs+21@gmail.com', + tenant: 'dev-cu4jy2zgao6yx15x', + password: 'dummyPassword', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + connection: 'Username-Password-Authentication', + }, + }, + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: 'All Applications', + description: '', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T05:57:06.859Z', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'auth0', + description: 'missing userId for all the requests in a batch', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + log_id: '90020221031055712103169676686005480714681762668315934738', + data: { + date: '2022-10-31T05:57:06.859Z', + type: 'ss', + description: '', + connection: 'Username-Password-Authentication', + connection_id: 'con_djwCjiwyID0vZy1S', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: 'All Applications', + ip: '35.166.202.113', + user_agent: 'unknown', + details: { + body: { + email: 'testRudderlabs+21@gmail.com', + tenant: 'dev-cu4jy2zgao6yx15x', + password: 'dummyPassword', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + connection: 'Username-Password-Authentication', + }, + }, + user_id: '', + user_name: 'testRudderlabs+21@gmail.com', + strategy: 'auth0', + strategy_type: 'database', + log_id: '90020221031055712103169676686005480714681762668315934738', + }, + }, + { + log_id: '90020221031055712103169676686007898566320991926665347090', + data: { + date: '2022-10-31T05:57:06.874Z', + type: 'sapi', + description: 'Create a User', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + ip: '35.166.202.113', + user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + log_id: '90020221031055712103169676686007898566320991926665347090', + }, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + type: 'identify', + userId: '', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + sentAt: '2022-10-31T05:57:06.859Z', + traits: { + connection: 'Username-Password-Authentication', + connection_id: 'con_djwCjiwyID0vZy1S', + }, + context: { + traits: { + userId: '', + user_name: 'testRudderlabs+21@gmail.com', + }, + library: { + name: 'unknown', + version: 'unknown', + }, + userAgent: 'unknown', + request_ip: '35.166.202.113', + integration: { + name: 'Auth0', + }, + }, + properties: { + log_id: '90020221031055712103169676686005480714681762668315934738', + details: { + body: { + email: 'testRudderlabs+21@gmail.com', + tenant: 'dev-cu4jy2zgao6yx15x', + password: 'dummyPassword', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + connection: 'Username-Password-Authentication', + }, + }, + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: 'All Applications', + description: '', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T05:57:06.859Z', + }, + ], + }, + }, + { + output: { + batch: [ + { + type: 'track', + event: 'Success API Operation', + sentAt: '2022-10-31T05:57:06.874Z', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + request_ip: '35.166.202.113', + integration: { + name: 'Auth0', + }, + }, + properties: { + log_id: '90020221031055712103169676686007898566320991926665347090', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + description: 'Create a User', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T05:57:06.874Z', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, +]; From 0a9a2e9e2a3029ad7b4834820ba2132d3f57ce98 Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Mon, 6 May 2024 10:45:42 +0530 Subject: [PATCH 135/240] fix: multiple event mappings in ortto (#3341) --- .../v2/destinations/ortto/procWorkflow.yaml | 5 +- .../destinations/ortto/processor/data.ts | 205 ++++++++++++++++++ 2 files changed, 206 insertions(+), 4 deletions(-) diff --git a/src/cdk/v2/destinations/ortto/procWorkflow.yaml b/src/cdk/v2/destinations/ortto/procWorkflow.yaml index dfd7118c41..a8fb9b2984 100644 --- a/src/cdk/v2/destinations/ortto/procWorkflow.yaml +++ b/src/cdk/v2/destinations/ortto/procWorkflow.yaml @@ -80,11 +80,8 @@ steps: steps: - name: getTrimmedEvent template: | - let customEvent = ""; const event = .message.event; - .destination.Config.orttoEventsMapping@order.( - customEvent = event === .rsEventName ? .orttoEventName : null; - ) + const customEvent = .destination.Config.orttoEventsMapping.{.rsEventName === event}.orttoEventName; $.assert(customEvent, "Event names is not mapped"); "act:cm:"+customEvent.trim().toLowerCase().replace(new RegExp('\\s+', 'g'),'-'); diff --git a/test/integrations/destinations/ortto/processor/data.ts b/test/integrations/destinations/ortto/processor/data.ts index 715262e447..0afdb05318 100644 --- a/test/integrations/destinations/ortto/processor/data.ts +++ b/test/integrations/destinations/ortto/processor/data.ts @@ -1452,4 +1452,209 @@ export const data = [ }, }, }, + { + name: 'ortto', + description: 'Track call for testing multiple event mappings filter logic', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ORTTO', + Config: { + privateApiKey: 'pvKey', + instanceRegion: 'other', + orttoEventsMapping: [ + { + rsEventName: 'player.set.loved', + orttoEventName: 'Loved a set', + eventProperties: [ + { + rudderProperty: 'set.id', + orttoProperty: 'Set ID', + type: 'text', + }, + { + rudderProperty: 'set.title', + orttoProperty: 'Set title', + type: 'text', + }, + { + rudderProperty: 'set.artist', + orttoProperty: 'Artist name', + type: 'text', + }, + ], + }, + { + rsEventName: 'player.set.liked', + orttoEventName: 'Liked a set', + eventProperties: [ + { + rudderProperty: 'set.id', + orttoProperty: 'Set ID', + type: 'text', + }, + { + rudderProperty: 'set.title', + orttoProperty: 'Set title', + type: 'text', + }, + { + rudderProperty: 'set.artist', + orttoProperty: 'Artist name', + type: 'text', + }, + ], + }, + { + rsEventName: 'player.set.played', + orttoEventName: 'Played a set', + eventProperties: [ + { + rudderProperty: 'set.id', + orttoProperty: 'Set ID', + type: 'text', + }, + { + rudderProperty: 'set.title', + orttoProperty: 'Set title', + type: 'text', + }, + { + rudderProperty: 'set.artist', + orttoProperty: 'Artist name', + type: 'text', + }, + ], + }, + ], + orttoPersonAttributes: [ + { + rudderTraits: 'id', + orttoAttribute: 'External ID', + type: 'text', + }, + ], + }, + Enabled: true, + Transformations: [], + }, + metadata: { destintionId: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', jobId: 2 }, + message: { + type: 'track', + event: 'player.set.liked', + sentAt: '2024-04-29T06:45:59.576Z', + userId: 'XxXxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx', + channel: 'mobile', + context: { + locale: 'en-MU', + traits: { + email: 'dummy-test@email.oop', + userId: 'XxXxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx', + anonymousId: 'dd14b53d-bfb8-48a9-a46f-e3d1351d1dd8', + }, + library: { + name: 'rudder-ios-library', + version: '1.26.3', + }, + network: { + wifi: true, + cellular: false, + }, + timezone: 'Africa/Johannesburg', + }, + rudderId: '83cb2d47-9c08-4ce3-86cc-44d8486b337a', + messageId: '2d9525a7-2a55-4d31-9c34-78788a5cbb59', + timestamp: '2024-04-29T06:45:52.611Z', + properties: { + email: 'dummy-test@email.oop', + 'set.id': '537d8682-4b37-4f12-8452-3f57091c2a54', + userId: 'XxXxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx', + 'set.title': 'Burn Man_Master', + 'set.artist': 'John Doe', + environment: 'test', + 'set.artists.0.name': 'John Doe', + }, + receivedAt: '2024-04-29T06:45:59.779Z', + request_ip: '206.0.75.23', + anonymousId: 'dd14b53d-bfb8-48a9-a46f-e3d1351d1dd8', + integrations: { + All: true, + }, + originalTimestamp: '2024-04-29T06:45:52.408Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + metadata: { + destintionId: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + jobId: 2, + }, + output: { + body: { + FORM: {}, + JSON_ARRAY: {}, + XML: {}, + JSON: { + activities: [ + { + fields: { + 'str::email': 'dummy-test@email.oop', + 'geo::city': {}, + 'geo::country': {}, + 'geo::region': {}, + 'str::ei': 'XxXxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx', + 'str::language': 'en-MU', + 'bol::gdpr': true, + 'bol::p': false, + 'bol::sp': false, + }, + activity_id: 'act:cm:liked-a-set', + attributes: { + 'str:cm:set-id': '537d8682-4b37-4f12-8452-3f57091c2a54', + 'str:cm:set-title': 'Burn Man_Master', + 'str:cm:artist-name': 'John Doe', + }, + location: {}, + }, + ], + merge_by: ['str::ei', 'str::email'], + }, + }, + endpoint: 'https://api.ap3api.com/v1/activities/create', + files: {}, + headers: { + 'Content-Type': 'application/json', + 'X-Api-Key': 'pvKey', + }, + method: 'POST', + params: {}, + type: 'REST', + userId: '', + version: '1', + }, + statusCode: 200, + }, + ], + }, + }, + }, ]; From ac59fdc37d6fe08847d79d249d166de7cc358fd6 Mon Sep 17 00:00:00 2001 From: Utsab Chowdhury Date: Mon, 6 May 2024 14:12:36 +0530 Subject: [PATCH 136/240] fix: braze dedup for non-billable attributes (#3320) * fix: braze dedup for non-billable attributes * fix: braze dedup update test description * fix: braze handle null for non billable attributes --- src/v0/destinations/braze/braze.util.test.js | 4 +- src/v0/destinations/braze/util.js | 22 ++++--- .../destinations/braze/router/data.ts | 60 ++++++++++++++++++- 3 files changed, 71 insertions(+), 15 deletions(-) diff --git a/src/v0/destinations/braze/braze.util.test.js b/src/v0/destinations/braze/braze.util.test.js index 7b6a93d359..cc50ae921e 100644 --- a/src/v0/destinations/braze/braze.util.test.js +++ b/src/v0/destinations/braze/braze.util.test.js @@ -739,7 +739,7 @@ describe('dedup utility tests', () => { }); }); - test('returns null if all keys are in BRAZE_NON_BILLABLE_ATTRIBUTES', () => { + test('returns only non-billable attribute if there is key of BRAZE_NON_BILLABLE_ATTRIBUTES', () => { const userData = { external_id: '123', country: 'US', @@ -757,7 +757,7 @@ describe('dedup utility tests', () => { }; store.set('123', storeData); const result = BrazeDedupUtility.deduplicate(userData, store); - expect(result).toBeNull(); + expect(result).toEqual({ country: 'US', external_id: '123', language: 'en' }); }); test('returns null if all keys have $add, $update, or $remove properties', () => { diff --git a/src/v0/destinations/braze/util.js b/src/v0/destinations/braze/util.js index 5f1f1e6205..ce83ebc244 100644 --- a/src/v0/destinations/braze/util.js +++ b/src/v0/destinations/braze/util.js @@ -26,7 +26,7 @@ const { const { JSON_MIME_TYPE, HTTP_STATUS_CODES } = require('../../util/constant'); const { isObject } = require('../../util'); const { removeUndefinedValues, getIntegrationsObj } = require('../../util'); -const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { InstrumentationError, isDefined } = require('@rudderstack/integrations-lib'); const getEndpointFromConfig = (destination) => { // Init -- mostly for test cases @@ -284,12 +284,17 @@ const BrazeDedupUtility = { return true; }); - if (keys.length === 0) { - return null; + if (keys.length > 0) { + keys.forEach((key) => { + if (!_.isEqual(userData[key], storedUserData[key])) { + deduplicatedUserData[key] = userData[key]; + } + }); } - keys.forEach((key) => { - if (!_.isEqual(userData[key], storedUserData[key])) { + // add non billable attributes back to the deduplicated user object + BRAZE_NON_BILLABLE_ATTRIBUTES.forEach((key) => { + if (isDefined(userData[key])) { deduplicatedUserData[key] = userData[key]; } }); @@ -305,13 +310,6 @@ const BrazeDedupUtility = { const identifier = external_id || user_alias?.alias_name; store.set(identifier, { ...storedUserData, ...deduplicatedUserData }); - // add non billable attributes back to the deduplicated user object - BRAZE_NON_BILLABLE_ATTRIBUTES.forEach((key) => { - if (isDefinedAndNotNull(userData[key])) { - deduplicatedUserData[key] = userData[key]; - } - }); - return removeUndefinedValues(deduplicatedUserData); }, }; diff --git a/test/integrations/destinations/braze/router/data.ts b/test/integrations/destinations/braze/router/data.ts index 76201bda41..6803742e86 100644 --- a/test/integrations/destinations/braze/router/data.ts +++ b/test/integrations/destinations/braze/router/data.ts @@ -640,6 +640,59 @@ export const data = [ userId: 'user@50', }, }, + { + destination: { + ID: '2N9UakqKF0D35wfzSeofIxPdL8X', + Name: 'Braze-Test', + Config: { + appKey: '0e5440c3-226b-45d0-91b5-c64da56cde16', + blacklistedEvents: [], + dataCenter: 'US-03', + enableNestedArrayOperations: false, + enableSubscriptionGroupInGroupCall: false, + eventFilteringOption: 'disable', + oneTrustCookieCategories: [], + restApiKey: 'dummyApiKey', + supportDedup: true, + trackAnonymousUser: true, + whitelistedEvents: [], + }, + Enabled: true, + WorkspaceID: '27O0bhB6p5ehfOWeeZlOSsSDTLg', + Transformations: [], + IsProcessorEnabled: true, + RevisionID: '2N9Uaf2tWq2QRmatBWQm03Rz6qX', + }, + metadata: { jobId: 5, userId: 'u1' }, + message: { + anonymousId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.5', + }, + ip: '0.0.0.0', + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.5' }, + locale: 'en-GB', + os: { name: '', version: '' }, + screen: { density: 2 }, + traits: { + city: 'Disney', + email: 'mickey@disney.com', + firstName: 'Mickey', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + }, + integrations: { All: true }, + messageId: '2536eda4-d638-4c93-8014-8ffe3f083214', + type: 'identify', + userId: 'user@50', + }, + }, ], destType: 'braze', }, @@ -688,6 +741,10 @@ export const data = [ external_id: 'user@50', first_name: 'Mickey', }, + { + country: 'USA', + external_id: 'user@50', + }, ], events: [ { @@ -733,6 +790,7 @@ export const data = [ { jobId: 1, userId: 'u1' }, { jobId: 2, userId: 'u1' }, { jobId: 3, userId: 'u1' }, + { jobId: 4, userId: 'u1' }, ], batched: true, statusCode: 200, @@ -771,7 +829,7 @@ export const data = [ }, statusCode: 400, batched: false, - metadata: [{ jobId: 4, userId: 'u1' }], + metadata: [{ jobId: 5, userId: 'u1' }], destination: { ID: '2N9UakqKF0D35wfzSeofIxPdL8X', Name: 'Braze-Test', From 5d58abd1d7068b38d739fcf37947064ed4072812 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 6 May 2024 09:41:56 +0000 Subject: [PATCH 137/240] chore(release): 1.65.0 --- CHANGELOG.md | 15 +++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95fd902959..2b4ee4f176 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.65.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.64.0...v1.65.0) (2024-05-06) + + +### Features + +* generate anonymousId and move to component testcases ([447f85f](https://github.com/rudderlabs/rudder-transformer/commit/447f85faf6ccca2179ab33b7fe43e281fc4f5897)) + + +### Bug Fixes + +* auth0 error handling ([2e22075](https://github.com/rudderlabs/rudder-transformer/commit/2e22075ddf792c573642fd09c5f9d31d8132525b)) +* auth0 error handling for missing userId ([#3334](https://github.com/rudderlabs/rudder-transformer/issues/3334)) ([d2cce77](https://github.com/rudderlabs/rudder-transformer/commit/d2cce772d6f0485ff8ee51af261006eb2066c3a2)) +* braze dedup for non-billable attributes ([#3320](https://github.com/rudderlabs/rudder-transformer/issues/3320)) ([ac59fdc](https://github.com/rudderlabs/rudder-transformer/commit/ac59fdc37d6fe08847d79d249d166de7cc358fd6)) +* multiple event mappings in ortto ([#3341](https://github.com/rudderlabs/rudder-transformer/issues/3341)) ([0a9a2e9](https://github.com/rudderlabs/rudder-transformer/commit/0a9a2e9e2a3029ad7b4834820ba2132d3f57ce98)) + ## [1.64.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.63.0...v1.64.0) (2024-04-29) diff --git a/package-lock.json b/package-lock.json index ee4f0bd087..8e2400e049 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.64.0", + "version": "1.65.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.64.0", + "version": "1.65.0", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index 1a246d143b..e2fb25df41 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.64.0", + "version": "1.65.0", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From db11c3e3d70d13a67d21c5b0043e74ce801bcb4a Mon Sep 17 00:00:00 2001 From: gitcommitshow Date: Thu, 9 May 2024 13:54:34 +0530 Subject: [PATCH 138/240] fix: default event name normalization to undefined if it cannot be parsed --- src/v0/sources/slack/util.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/v0/sources/slack/util.js b/src/v0/sources/slack/util.js index c4b3bdfa4e..b9c39db223 100644 --- a/src/v0/sources/slack/util.js +++ b/src/v0/sources/slack/util.js @@ -49,10 +49,14 @@ function tsToISODate(slackTs) { * console.log(normalizedEventName); // Output: "Member Joined Channel" */ function normalizeEventName(evtName) { - return evtName - .split('_') - .map((s) => s.charAt(0).toUpperCase() + s.slice(1)) - .join(' '); + try { + return evtName + .split('_') + .map((s) => s.charAt(0).toUpperCase() + s.slice(1)) + .join(' '); + } catch (e) { + return 'undefined'; + } } module.exports = { mapping, tsToISODate, normalizeEventName }; From e1b4b9e1f11fccf23f6875b6a1b786922f6d42c6 Mon Sep 17 00:00:00 2001 From: gitcommitshow Date: Thu, 9 May 2024 14:00:45 +0530 Subject: [PATCH 139/240] test: unit tests for slack source --- src/v0/sources/slack/util.test.js | 51 +++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/v0/sources/slack/util.test.js diff --git a/src/v0/sources/slack/util.test.js b/src/v0/sources/slack/util.test.js new file mode 100644 index 0000000000..b83f22f058 --- /dev/null +++ b/src/v0/sources/slack/util.test.js @@ -0,0 +1,51 @@ +const { tsToISODate, normalizeEventName } = require('./util.js'); + +describe('Unit test cases for tsToISODate', () => { + it('should return a valid iso date string for a valid slack timestamp input', () => { + const result = tsToISODate('1609459200.123000'); + expect(result).toBe('2021-01-01T00:00:00.123Z'); + }); + + it('should return iso date string of today when slack timestamp argument is not provided', () => { + const result = tsToISODate(); + expect(result).not.toBeNull(); + expect(typeof result).toBe('string'); + expect(result).not.toHaveLength(0); + // Check if the result is a valid date + const dateObject = new Date(result); + const resultTime = dateObject.getTime(); + expect(resultTime).not.toBeNaN(); + // Check if the result is close to the current time with precision tolerance of upto a minute + const nowTime = new Date().getTime(); + const TOLERANCE = 60000; // In ms + const timeDiff = Math.abs(nowTime - resultTime); + expect(timeDiff).toBeLessThanOrEqual(TOLERANCE); + }); + + it('should return null if the slack timestamp argument is invalid', () => { + const result = tsToISODate('invalid.slack.timestamp'); + expect(result).toBeNull(); + }); +}); + +describe('Unit test cases for normalizeEventName', () => { + it('should normalize a valid snake case string "member_joined_channel" to RudderStack format "Member Joined Channel"', () => { + const result = normalizeEventName('member_joined_channel'); + expect(result).toBe('Member Joined Channel'); + }); + + it('should return undefined string when event name is undefined', () => { + const result = normalizeEventName(undefined); + expect(result).toBe('undefined'); + }); + + it('should return undefined string when event name is null', () => { + const result = normalizeEventName(null); + expect(result).toBe('undefined'); + }); + + it('should return undefined string when event name argument cannot be parsed to string', () => { + const result = normalizeEventName({}); + expect(result).toBe('undefined'); + }); +}); From ce94a8b0ffef27980bc4d0ebc89c738ea26cf798 Mon Sep 17 00:00:00 2001 From: gitcommitshow Date: Thu, 9 May 2024 14:37:09 +0530 Subject: [PATCH 140/240] test: add one test for track event --- test/integrations/sources/slack/data.ts | 152 ++++++++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/test/integrations/sources/slack/data.ts b/test/integrations/sources/slack/data.ts index c565c573d1..def8a63408 100644 --- a/test/integrations/sources/slack/data.ts +++ b/test/integrations/sources/slack/data.ts @@ -127,4 +127,156 @@ export const data = [ }, }, }, + { + name: 'slack', + description: 'Message event', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + event: { + user: 'U04G7H550', + type: 'message', + ts: '1709441309.308399', + client_msg_id: '834r664e-ec75-445d-t5c6-b873a07y9c81', + text: 'What is the pricing of product X', + team: 'T0GFJL5J7', + thread_ts: '1709407304.839329', + parent_user_id: 'U06P6LQTPV', + blocks: [ + { + type: 'rich_text', + block_id: 'xGKJl', + elements: [ + { + type: 'rich_text_section', + elements: [ + { + type: 'text', + text: 'What is the pricing of product X', + }, + { + type: 'channel', + channel_id: 'C03CDQTPI65', + }, + { + type: 'text', + text: ' to do this', + }, + ], + }, + ], + }, + ], + channel: 'C03CDQTPI65', + event_ts: '1709441309.308399', + channel_type: 'channel', + }, + type: 'event_callback', + event_id: 'EvY5JTJ0NG5', + event_time: 1709441309, + token: 'REm2987dqtpi72Lq', + team_id: 'T0GFJL5J7', + context_team_id: 'T01gqtPIL5J7', + context_enterprise_id: null, + api_app_id: 'A04QTPIHRR', + authorizations: [ + { + enterprise_id: null, + team_id: 'T0GFJL5J7', + user_id: 'W012CDE', + is_bot: true, + is_enterprise_install: false, + }, + ], + is_ext_shared_channel: false, + event_context: '4-wd6joiQfdgTRQTpIzdfifQ', + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + integration: { + name: 'SLACK', + }, + externalId: [ + { + type: 'slackUserId', + id: 'U04G7H550', + }, + ], + }, + integrations: { + SLACK: false, + }, + type: 'track', + event: 'Message', + anonymousId: '7509c04f547b05afb6838aa742f4910263d6', + originalTimestamp: '2024-03-03T04:48:29.308Z', + sentAt: '2024-03-03T04:48:29.000Z', + properties: { + user: 'U04G7H550', + type: 'message', + ts: '1709441309.308399', + client_msg_id: '834r664e-ec75-445d-t5c6-b873a07y9c81', + text: 'What is the pricing of product X', + team: 'T0GFJL5J7', + thread_ts: '1709407304.839329', + parent_user_id: 'U06P6LQTPV', + blocks: [ + { + type: 'rich_text', + block_id: 'xGKJl', + elements: [ + { + type: 'rich_text_section', + elements: [ + { + type: 'text', + text: 'What is the pricing of product X', + }, + { + type: 'channel', + channel_id: 'C03CDQTPI65', + }, + { + type: 'text', + text: ' to do this', + }, + ], + }, + ], + }, + ], + channel: 'C03CDQTPI65', + event_ts: '1709441309.308399', + channel_type: 'channel', + }, + }, + ], + }, + }, + ], + }, + }, + }, ]; From 13be5cfb85363cf5c0527247787979467d63caaa Mon Sep 17 00:00:00 2001 From: gitcommitshow Date: Thu, 9 May 2024 21:16:01 +0530 Subject: [PATCH 141/240] fix: use optional chain parameter --- src/v0/sources/slack/transform.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v0/sources/slack/transform.js b/src/v0/sources/slack/transform.js index d67d1bb017..98324a7b65 100644 --- a/src/v0/sources/slack/transform.js +++ b/src/v0/sources/slack/transform.js @@ -14,7 +14,7 @@ const { EventType } = require('../../../constants'); */ function processNormalEvent(slackPayload) { const message = new Message(`SLACK`); - if (!slackPayload || !slackPayload.event) { + if (!slackPayload?.event) { throw new TransformationError('Missing the required event data'); } switch (slackPayload.event.type) { From 88b2d5709da00445ffae54f5a36de855cb5f8479 Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Fri, 10 May 2024 09:47:22 +0530 Subject: [PATCH 142/240] feat: onboard koddi destination --- src/cdk/v2/destinations/koddi/config.js | 20 +++++ .../destinations/koddi/data/ClicksConfig.json | 35 ++++++++ .../koddi/data/ConversionsConfig.json | 53 ++++++++++++ .../koddi/data/ImpressionsConfig.json | 22 +++++ .../v2/destinations/koddi/procWorkflow.yaml | 31 +++++++ src/cdk/v2/destinations/koddi/utils.js | 84 +++++++++++++++++++ 6 files changed, 245 insertions(+) create mode 100644 src/cdk/v2/destinations/koddi/config.js create mode 100644 src/cdk/v2/destinations/koddi/data/ClicksConfig.json create mode 100644 src/cdk/v2/destinations/koddi/data/ConversionsConfig.json create mode 100644 src/cdk/v2/destinations/koddi/data/ImpressionsConfig.json create mode 100644 src/cdk/v2/destinations/koddi/procWorkflow.yaml create mode 100644 src/cdk/v2/destinations/koddi/utils.js diff --git a/src/cdk/v2/destinations/koddi/config.js b/src/cdk/v2/destinations/koddi/config.js new file mode 100644 index 0000000000..c7af5c0b57 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/config.js @@ -0,0 +1,20 @@ +const { getMappingConfig } = require('../../../../v0/util'); + +const ConfigCategories = { + IMPRESSIONS: { + type: 'track', + name: 'impressionsMapping', + }, + CLICKS: { + type: 'track', + name: 'clicksMapping', + }, + CONVERSIONS: { + type: 'track', + name: 'conversionsMapping', + }, +}; + +const mappingConfig = getMappingConfig(ConfigCategories, __dirname); + +module.exports = { ConfigCategories, mappingConfig }; diff --git a/src/cdk/v2/destinations/koddi/data/ClicksConfig.json b/src/cdk/v2/destinations/koddi/data/ClicksConfig.json new file mode 100644 index 0000000000..96ab27b2ae --- /dev/null +++ b/src/cdk/v2/destinations/koddi/data/ClicksConfig.json @@ -0,0 +1,35 @@ +[ + { + "sourceKeys": "properties.tracking_data", + "required": true, + "destKey": "trackingData" + }, + { + "sourceKeys": "properties.rank", + "required": true, + "destKey": "rank" + }, + { + "sourceKeys": "properties.beacon_issued", + "required": true, + "destKey": "beaconIssued" + }, + { + "sourceKeys": "userId", + "sourceFromGenericMap": true, + "required": true, + "destKey": "userGuid" + }, + { + "sourceKeys": "properties.test_version_override", + "destKey": "testVersionOverride" + }, + { + "sourceKeys": "properties.destination_url", + "destKey": "destinationUrl" + }, + { + "sourceKeys": "properties.overrides", + "destKey": "overrides" + } +] diff --git a/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json b/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json new file mode 100644 index 0000000000..0d49e39c32 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json @@ -0,0 +1,53 @@ +[ + { + "sourceKeys": "context.page.referring_domain", + "destKey": "domain" + }, + { + "sourceKeys": "context.locale", + "required": true, + "destKey": "culture" + }, + { + "sourceKeys": "properties.currency", + "required": true, + "destKey": "currency" + }, + { + "sourceKeys": ["context.ip", "request_ip"], + "destKey": "user_ip" + }, + { + "sourceKeys": "context.userAgent", + "destKey": "user_agent" + }, + { + "sourceKeys": "userId", + "sourceFromGenericMap": true, + "required": true, + "destKey": "userGuid" + }, + { + "sourceKeys": "context.device.type", + "destKey": "device_type" + }, + { + "sourceKeys": ["properties.order_id", "properties.transaction_id"], + "required": true, + "destKey": "transaction_id" + }, + { + "sourceKeys": "properties.conversion_source", + "destKey": "conversion_source" + }, + { + "sourceKeys": "timestamp", + "sourceFromGenericMap": true, + "destKey": "unixtime" + }, + { + "sourceKeys": "properties.bidders", + "required": true, + "destKey": "bidders" + } +] diff --git a/src/cdk/v2/destinations/koddi/data/ImpressionsConfig.json b/src/cdk/v2/destinations/koddi/data/ImpressionsConfig.json new file mode 100644 index 0000000000..de53703b32 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/data/ImpressionsConfig.json @@ -0,0 +1,22 @@ +[ + { + "sourceKeys": "properties.tracking_data", + "required": true, + "destKey": "trackingData" + }, + { + "sourceKeys": "properties.rank", + "required": true, + "destKey": "rank" + }, + { + "sourceKeys": "properties.beacon_issued", + "required": true, + "destKey": "beaconIssued" + }, + { + "sourceKeys": "timestamp", + "sourceFromGenericMap": true, + "destKey": "ts" + } +] diff --git a/src/cdk/v2/destinations/koddi/procWorkflow.yaml b/src/cdk/v2/destinations/koddi/procWorkflow.yaml new file mode 100644 index 0000000000..d632b73273 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/procWorkflow.yaml @@ -0,0 +1,31 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + - name: defaultRequestConfig + path: ../../../../v0/util + - name: removeUndefinedAndNullValues + path: ../../../../v0/util + - path: ./utils + +steps: + - name: messageType + template: | + .message.type.toLowerCase(); + - name: validateInput + template: | + let messageType = $.outputs.messageType; + $.assert(messageType, "message Type is not present. Aborting message."); + $.assert(messageType in {{$.EventType.([.TRACK])}}, "message type " + messageType + " is not supported"); + $.assert(.message.event, "Event name is not present. Aborting"); + $.assert(typeof .message.event === "string", "event name should be a string"); + $.assertConfig(.destination.Config.apiBaseUrl, "API Base URL is not present. Aborting"); + $.assertConfig(.destination.Config.clientName, "Client Name is not present. Aborting"); + - name: preparePayload + template: | + const payload = $.constructFullPayload(.message, .destination.Config); + $.context.payload = $.removeUndefinedAndNullValues(payload); + - name: buildResponse + template: | + const response = $.constructResponse(.context.payload, .destination.Config, .message); + response diff --git a/src/cdk/v2/destinations/koddi/utils.js b/src/cdk/v2/destinations/koddi/utils.js new file mode 100644 index 0000000000..2936d519a8 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/utils.js @@ -0,0 +1,84 @@ +const config = require('./config'); +const { constructPayload, defaultRequestConfig } = require('../../../../v0/util'); + +/** + * + * @param message + * @param Config + * @returns {{}} + */ +const constructFullPayload = (message, Config) => { + let payload; + switch (message.event) { + case 'Impressions': + payload = constructPayload( + message, + config.mappingConfig[config.ConfigCategories.IMPRESSIONS.name], + ); + payload.clientName = Config.clientName; + break; + case 'Clicks': + payload = constructPayload( + message, + config.mappingConfig[config.ConfigCategories.CLICKS.name], + ); + payload.clientName = Config.clientName; + if (Config.testVersionOverride === false) { + payload.properties.test_version_override = null; + } + if (Config.overrides === false) { + payload.properties.overrides = null; + } + break; + case 'Conversions': + payload = constructPayload( + message, + config.mappingConfig[config.ConfigCategories.CONVERSIONS.name], + ); + payload.client_name = Config.clientName; + break; + default: + break; + } + return payload; +}; + +const getEndpoint = (Config, message) => { + let endpoint = Config.apiBaseUrl; + switch (message.event) { + case 'Impressions': + endpoint += '?action=impression'; + break; + case 'Clicks': + endpoint += '?action=click'; + break; + case 'Conversions': + endpoint += '/conversion'; + break; + default: + break; + } + return endpoint; +}; + +const constructResponse = (payload, Config, message) => { + const response = defaultRequestConfig(); + response.endpoint = getEndpoint(Config, message); + response.headers = { + accept: 'application/json', + }; + if (message.event === 'Conversions') { + response.body.JSON = payload; + response.method = 'POST'; + response.headers = { + ...response.headers, + 'content-type': 'application/json', + }; + } else { + response.params = payload; + response.method = 'GET'; + } + return response; +}; + +module.exports = { constructFullPayload, getEndpoint, constructResponse }; From cb10aa7707518b52edcf7fb1081c6969bcb5f8f8 Mon Sep 17 00:00:00 2001 From: Sudip Paul <67197965+ItsSudip@users.noreply.github.com> Date: Fri, 10 May 2024 10:06:26 +0530 Subject: [PATCH 143/240] fix: update regex which was discarding firstname and lastname (#3360) --- .../destinations/fb_custom_audience/util.js | 4 +- .../fb_custom_audience/processor/data.ts | 44 ++++++++++++++++ .../fb_custom_audience/router/data.ts | 50 +++++++++++++++---- 3 files changed, 86 insertions(+), 12 deletions(-) diff --git a/src/v0/destinations/fb_custom_audience/util.js b/src/v0/destinations/fb_custom_audience/util.js index 6c53ed2814..1f096215f3 100644 --- a/src/v0/destinations/fb_custom_audience/util.js +++ b/src/v0/destinations/fb_custom_audience/util.js @@ -92,11 +92,11 @@ const ensureApplicableFormat = (userProperty, userInformation) => { case 'FN': case 'FI': if (userProperty !== 'FI') { - updatedProperty = stringifiedUserInformation.toLowerCase().replace(/[!#$%&@A-Za-z]/g, ''); + updatedProperty = stringifiedUserInformation.toLowerCase().replace(/[^#$%&'*+/a-z]/g, ''); } else { updatedProperty = stringifiedUserInformation .toLowerCase() - .replace(/[^!#$%&,.?@A-Za-z]/g, ''); + .replace(/[^!"#$%&'()*+,-./a-z]/g, ''); } break; case 'MADID': diff --git a/test/integrations/destinations/fb_custom_audience/processor/data.ts b/test/integrations/destinations/fb_custom_audience/processor/data.ts index 267b966865..602f8a7fb6 100644 --- a/test/integrations/destinations/fb_custom_audience/processor/data.ts +++ b/test/integrations/destinations/fb_custom_audience/processor/data.ts @@ -1261,6 +1261,23 @@ export const data = [ DOBY: '2013', PHONE: '@09432457768', GEN: 'f', + FN: 'test', + LN: 'user', + FI: 'Ms.', + MADID: 'ABC', + ZIP: 'ZIP ', + ST: '123abc ', + COUNTRY: 'IN', + }, + { + EMAIL: 'testuser2@abc.com', + DOBM: '2', + DOBD: '13', + DOBY: '2013', + PHONE: '@09432457768', + GEN: 'f', + FN: '1234', + LN: true, FI: 'Ms.', MADID: 'ABC', ZIP: 'ZIP ', @@ -1276,6 +1293,8 @@ export const data = [ DOBY: '2013', PHONE: '@09432457768', GEN: 'f', + FN: 'test', + LN: 'user', FI: 'Ms.', MADID: 'ABC', ZIP: 'ZIP ', @@ -1303,6 +1322,8 @@ export const data = [ 'DOBY', 'PHONE', 'GEN', + 'FN', + 'LN', 'FI', 'MADID', 'ZIP', @@ -1355,6 +1376,8 @@ export const data = [ 'DOBY', 'PHONE', 'GEN', + 'FN', + 'LN', 'FI', 'MADID', 'ZIP', @@ -1369,6 +1392,8 @@ export const data = [ '7931aa2a1bed855457d1ddf6bc06ab4406a9fba0579045a4d6ff78f9c07c440f', '0dcd4be87427e008a16adbdc2b2c15a14accf485dd451314dcecfb902c51c686', '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + '04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb', 'db0683221aebc02cc034b65ebcf7d1bddd1eb199e33fd23a31931947d13a11bc', 'abc', '4a70fe9aa6436e02c2dea340fbd1e352e4ef2d8ce6ca52ad25d4b95471fc8bf2', @@ -1409,6 +1434,8 @@ export const data = [ 'DOBY', 'PHONE', 'GEN', + 'FN', + 'LN', 'FI', 'MADID', 'ZIP', @@ -1423,6 +1450,23 @@ export const data = [ '7931aa2a1bed855457d1ddf6bc06ab4406a9fba0579045a4d6ff78f9c07c440f', '0dcd4be87427e008a16adbdc2b2c15a14accf485dd451314dcecfb902c51c686', '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + '04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb', + 'db0683221aebc02cc034b65ebcf7d1bddd1eb199e33fd23a31931947d13a11bc', + 'abc', + '4a70fe9aa6436e02c2dea340fbd1e352e4ef2d8ce6ca52ad25d4b95471fc8bf2', + 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', + '582967534d0f909d196b97f9e6921342777aea87b46fa52df165389db1fb8ccf', + ], + [ + '0059c0c78dac479648069fc1ed4298cd53bd857857aac57ac597537bedc6e043', + 'a953f09a1b6b6725b81956e9ad0b1eb49e3ad40004c04307ef8af6246a054116', + '3fdba35f04dc8c462986c992bcf875546257113072a909c162f7e470e581e278', + '7931aa2a1bed855457d1ddf6bc06ab4406a9fba0579045a4d6ff78f9c07c440f', + '0dcd4be87427e008a16adbdc2b2c15a14accf485dd451314dcecfb902c51c686', + '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + '', + 'b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b', 'db0683221aebc02cc034b65ebcf7d1bddd1eb199e33fd23a31931947d13a11bc', 'abc', '4a70fe9aa6436e02c2dea340fbd1e352e4ef2d8ce6ca52ad25d4b95471fc8bf2', diff --git a/test/integrations/destinations/fb_custom_audience/router/data.ts b/test/integrations/destinations/fb_custom_audience/router/data.ts index 492b625b2d..1240939dbd 100644 --- a/test/integrations/destinations/fb_custom_audience/router/data.ts +++ b/test/integrations/destinations/fb_custom_audience/router/data.ts @@ -26626,16 +26626,46 @@ export const data = [ payload: { schema: ['EMAIL', 'FN'], data: [ - ['7625cab24612c37df6d2f724721bb38a25095d0295e29b807238ee188b8aca43', ''], - ['b2b4abadd72190af54305c0d3abf1977fec4935016bb13ff28040d5712318dfd', ''], - ['c4b007d1c3c9a5d31bd4082237a913e8e0db1767225c2a5ef33be2716df005fa', ''], - ['94639be1bd9f17c05820164e9d71ef78558f117a9e8bfab43cf8015e08aa0b27', ''], - ['39b456cfb4bb07f9e6bb18698aa173171ca49c731fccc4790e9ecea808d24ae6', ''], - ['769f73387add781a481ca08300008a08fb2f1816aaed196137efc2e05976d711', ''], - ['48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', ''], - ['da2d431121cd10578fd81f8f80344b06db59ea2d05a7b5d27536c8789ddae8f0', ''], - ['b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', ''], - ['0c1d1b0ba547a742013366d6fbc8f71dd77f566d94e41ed9f828a74b96928161', ''], + [ + '7625cab24612c37df6d2f724721bb38a25095d0295e29b807238ee188b8aca43', + 'e328a0d90d4b5132b2655cf7079b160040d2c1a83d70d4cad9cf1f69310635b3', + ], + [ + 'b2b4abadd72190af54305c0d3abf1977fec4935016bb13ff28040d5712318dfd', + 'f8147eb72c9bb356c362fdb0796b54971ebc983cb60b3cc3ff29582ce2052bad', + ], + [ + 'c4b007d1c3c9a5d31bd4082237a913e8e0db1767225c2a5ef33be2716df005fa', + 'd8bb13b95eaed7f9b6a8af276aa6122e8015e0c466c1a84e49ff7c69ad6ac911', + ], + [ + '94639be1bd9f17c05820164e9d71ef78558f117a9e8bfab43cf8015e08aa0b27', + 'b1661f97721dede0f876dcbf603289ee339f641b9c310deba53c76940f472698', + ], + [ + '39b456cfb4bb07f9e6bb18698aa173171ca49c731fccc4790e9ecea808d24ae6', + '6c882abd6d0aff713cdd6a4a31ee28c9140612fb2627a611f6f9f539bac44f81', + ], + [ + '769f73387add781a481ca08300008a08fb2f1816aaed196137efc2e05976d711', + '2222cb73346f7a01a1d4d3db28b58fd41045782bb66152b92aade379192544c5', + ], + [ + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + 'abc12f8d666517c35280bf220f5390b1f0ef4bdbbc794ac59c95bba0381bf91b', + ], + [ + 'da2d431121cd10578fd81f8f80344b06db59ea2d05a7b5d27536c8789ddae8f0', + 'abc12f8d666517c35280bf220f5390b1f0ef4bdbbc794ac59c95bba0381bf91b', + ], + [ + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '62a2fed3d6e08c44835fce71f02210b1ddabfb066e39edf1e6c261988f824dd3', + ], + [ + '0c1d1b0ba547a742013366d6fbc8f71dd77f566d94e41ed9f828a74b96928161', + '62a2fed3d6e08c44835fce71f02210b1ddabfb066e39edf1e6c261988f824dd3', + ], ], }, }, From 8148e9bf3db13f3e3f124c15543188ffac648d5b Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 10 May 2024 05:31:32 +0000 Subject: [PATCH 144/240] chore(release): 1.65.1 --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b4ee4f176..a63e10b793 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [1.65.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.65.0...v1.65.1) (2024-05-10) + + +### Bug Fixes + +* update regex which was discarding firstname and lastname ([#3360](https://github.com/rudderlabs/rudder-transformer/issues/3360)) ([cb10aa7](https://github.com/rudderlabs/rudder-transformer/commit/cb10aa7707518b52edcf7fb1081c6969bcb5f8f8)) + ## [1.65.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.64.0...v1.65.0) (2024-05-06) diff --git a/package-lock.json b/package-lock.json index 8e2400e049..23f524d0ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.65.0", + "version": "1.65.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.65.0", + "version": "1.65.1", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index e2fb25df41..00dad8305f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.65.0", + "version": "1.65.1", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From 847e3e04d3ef67b9a7b5e35127251f3fc34ba3bf Mon Sep 17 00:00:00 2001 From: Gauravudia Date: Fri, 10 May 2024 16:56:03 +0530 Subject: [PATCH 145/240] fix: config --- src/cdk/v2/destinations/koddi/config.js | 18 ++++++++++++------ src/cdk/v2/destinations/koddi/utils.js | 17 ++++------------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/cdk/v2/destinations/koddi/config.js b/src/cdk/v2/destinations/koddi/config.js index c7af5c0b57..dfae60a257 100644 --- a/src/cdk/v2/destinations/koddi/config.js +++ b/src/cdk/v2/destinations/koddi/config.js @@ -1,20 +1,26 @@ const { getMappingConfig } = require('../../../../v0/util'); -const ConfigCategories = { +const CONFIG_CATEGORIES = { IMPRESSIONS: { type: 'track', - name: 'impressionsMapping', + name: 'ImpressionsConfig', }, CLICKS: { type: 'track', - name: 'clicksMapping', + name: 'ClicksConfig', }, CONVERSIONS: { type: 'track', - name: 'conversionsMapping', + name: 'ConversionsConfig', }, }; -const mappingConfig = getMappingConfig(ConfigCategories, __dirname); +const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); -module.exports = { ConfigCategories, mappingConfig }; +module.exports = { + CONFIG_CATEGORIES, + MAPPING_CONFIG, + IMPRESSIONS_CONFIG: MAPPING_CONFIG[CONFIG_CATEGORIES.IMPRESSIONS.name], + CLICKS_CONFIG: MAPPING_CONFIG[CONFIG_CATEGORIES.CLICKS.name], + CONVERSIONS_CONFIG: MAPPING_CONFIG[CONFIG_CATEGORIES.CONVERSIONS.name], +}; diff --git a/src/cdk/v2/destinations/koddi/utils.js b/src/cdk/v2/destinations/koddi/utils.js index 2936d519a8..e9f2c6a412 100644 --- a/src/cdk/v2/destinations/koddi/utils.js +++ b/src/cdk/v2/destinations/koddi/utils.js @@ -1,4 +1,4 @@ -const config = require('./config'); +const { IMPRESSIONS_CONFIG, CLICKS_CONFIG, CONVERSIONS_CONFIG } = require('./config'); const { constructPayload, defaultRequestConfig } = require('../../../../v0/util'); /** @@ -11,17 +11,11 @@ const constructFullPayload = (message, Config) => { let payload; switch (message.event) { case 'Impressions': - payload = constructPayload( - message, - config.mappingConfig[config.ConfigCategories.IMPRESSIONS.name], - ); + payload = constructPayload(message, IMPRESSIONS_CONFIG); payload.clientName = Config.clientName; break; case 'Clicks': - payload = constructPayload( - message, - config.mappingConfig[config.ConfigCategories.CLICKS.name], - ); + payload = constructPayload(message, CLICKS_CONFIG); payload.clientName = Config.clientName; if (Config.testVersionOverride === false) { payload.properties.test_version_override = null; @@ -31,10 +25,7 @@ const constructFullPayload = (message, Config) => { } break; case 'Conversions': - payload = constructPayload( - message, - config.mappingConfig[config.ConfigCategories.CONVERSIONS.name], - ); + payload = constructPayload(message, CONVERSIONS_CONFIG); payload.client_name = Config.clientName; break; default: From ddf8d46fed980204c561f95daa12fc740302e6e3 Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Sun, 12 May 2024 20:59:12 +0530 Subject: [PATCH 146/240] fix: added conversions bidders validation and improved implementation --- src/cdk/v2/destinations/koddi/config.js | 7 ++ .../v2/destinations/koddi/procWorkflow.yaml | 12 ++- src/cdk/v2/destinations/koddi/rtWorkflow.yaml | 35 ++++++++ src/cdk/v2/destinations/koddi/utils.js | 81 +++++++++++++------ 4 files changed, 107 insertions(+), 28 deletions(-) create mode 100644 src/cdk/v2/destinations/koddi/rtWorkflow.yaml diff --git a/src/cdk/v2/destinations/koddi/config.js b/src/cdk/v2/destinations/koddi/config.js index dfae60a257..fa595dc627 100644 --- a/src/cdk/v2/destinations/koddi/config.js +++ b/src/cdk/v2/destinations/koddi/config.js @@ -1,5 +1,11 @@ const { getMappingConfig } = require('../../../../v0/util'); +const EVENT_NAMES = { + IMPRESSIONS: 'impressions', + CLICKS: 'clicks', + CONVERSIONS: 'conversions', +}; + const CONFIG_CATEGORIES = { IMPRESSIONS: { type: 'track', @@ -18,6 +24,7 @@ const CONFIG_CATEGORIES = { const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); module.exports = { + EVENT_NAMES, CONFIG_CATEGORIES, MAPPING_CONFIG, IMPRESSIONS_CONFIG: MAPPING_CONFIG[CONFIG_CATEGORIES.IMPRESSIONS.name], diff --git a/src/cdk/v2/destinations/koddi/procWorkflow.yaml b/src/cdk/v2/destinations/koddi/procWorkflow.yaml index d632b73273..28d062d4a4 100644 --- a/src/cdk/v2/destinations/koddi/procWorkflow.yaml +++ b/src/cdk/v2/destinations/koddi/procWorkflow.yaml @@ -2,30 +2,34 @@ bindings: - name: EventType path: ../../../../constants - path: ../../bindings/jsontemplate - - name: defaultRequestConfig - path: ../../../../v0/util - name: removeUndefinedAndNullValues path: ../../../../v0/util - path: ./utils + - path: ./config steps: - name: messageType template: | .message.type.toLowerCase(); + - name: eventName + template: | + .message.integrations.koddi.eventName.toLowerCase(); - name: validateInput template: | let messageType = $.outputs.messageType; + let eventName = $.outputs.eventName; $.assert(messageType, "message Type is not present. Aborting message."); $.assert(messageType in {{$.EventType.([.TRACK])}}, "message type " + messageType + " is not supported"); + $.assert(eventName in {{$.EVENT_NAMES.([.IMPRESSIONS, .CLICKS, .CONVERSIONS])}}, "event name " + eventName + " is not supported"); $.assert(.message.event, "Event name is not present. Aborting"); $.assert(typeof .message.event === "string", "event name should be a string"); $.assertConfig(.destination.Config.apiBaseUrl, "API Base URL is not present. Aborting"); $.assertConfig(.destination.Config.clientName, "Client Name is not present. Aborting"); - name: preparePayload template: | - const payload = $.constructFullPayload(.message, .destination.Config); + const payload = $.constructFullPayload($.outputs.eventName, .message, .destination.Config); $.context.payload = $.removeUndefinedAndNullValues(payload); - name: buildResponse template: | - const response = $.constructResponse(.context.payload, .destination.Config, .message); + const response = $.constructResponse($.outputs.eventName, .destination.Config, $.context.payload); response diff --git a/src/cdk/v2/destinations/koddi/rtWorkflow.yaml b/src/cdk/v2/destinations/koddi/rtWorkflow.yaml new file mode 100644 index 0000000000..30dd3fdd95 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/rtWorkflow.yaml @@ -0,0 +1,35 @@ +bindings: + - path: ./config + - name: handleRtTfSingleEventError + path: ../../../../v0/util/index + - path: ./utils +steps: + - name: validateInput + template: | + $.assert(Array.isArray(^) && ^.length > 0, "Invalid event array") + + - name: transform + externalWorkflow: + path: ./procWorkflow.yaml + loopOverInput: true + + - name: successfulEvents + template: | + $.outputs.transform#idx.output.({ + "output": .body.JSON.events[0], + "destination": ^[idx].destination, + "metadata": ^[idx].metadata + })[] + - name: failedEvents + template: | + $.outputs.transform#idx.error.( + $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) + )[] + - name: batchSuccessfulEvents + description: Batches the successfulEvents + template: | + $.batchResponseBuilder($.outputs.successfulEvents); + + - name: finalPayload + template: | + [...$.outputs.failedEvents, ...$.outputs.batchSuccessfulEvents] diff --git a/src/cdk/v2/destinations/koddi/utils.js b/src/cdk/v2/destinations/koddi/utils.js index e9f2c6a412..6e8d5ba12b 100644 --- a/src/cdk/v2/destinations/koddi/utils.js +++ b/src/cdk/v2/destinations/koddi/utils.js @@ -1,32 +1,58 @@ -const { IMPRESSIONS_CONFIG, CLICKS_CONFIG, CONVERSIONS_CONFIG } = require('./config'); -const { constructPayload, defaultRequestConfig } = require('../../../../v0/util'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { EVENT_NAMES, IMPRESSIONS_CONFIG, CLICKS_CONFIG, CONVERSIONS_CONFIG } = require('./config'); +const { constructPayload, defaultRequestConfig, toUnixTimestamp } = require('../../../../v0/util'); + +const validateBidders = (bidders) => { + if (!Array.isArray(bidders)) { + throw new InstrumentationError('properties.bidders should be an array of objects. Aborting.'); + } + if (bidders.length === 0) { + throw new InstrumentationError( + 'properties.bidders should contains at least one bidder. Aborting.', + ); + } + bidders.forEach((bidder) => { + if (!(bidder.bidder || bidder.alternate_bidder)) { + throw new InstrumentationError('bidder or alternate_bidder is not present. Aborting.'); + } + if (!bidder.count) { + throw new InstrumentationError('count is not present. Aborting.'); + } + if (!bidder.base_price) { + throw new InstrumentationError('base_price is not present. Aborting.'); + } + }); +}; /** - * - * @param message - * @param Config - * @returns {{}} + * This function constructs payloads based upon mappingConfig for all calls. + * @param {*} eventName + * @param {*} message + * @param {*} Config + * @returns */ -const constructFullPayload = (message, Config) => { +const constructFullPayload = (eventName, message, Config) => { let payload; - switch (message.event) { - case 'Impressions': + switch (eventName) { + case EVENT_NAMES.IMPRESSIONS: payload = constructPayload(message, IMPRESSIONS_CONFIG); payload.clientName = Config.clientName; break; - case 'Clicks': + case EVENT_NAMES.CLICKS: payload = constructPayload(message, CLICKS_CONFIG); payload.clientName = Config.clientName; - if (Config.testVersionOverride === false) { - payload.properties.test_version_override = null; + if (!Config.testVersionOverride) { + payload.testVersionOverride = null; } - if (Config.overrides === false) { - payload.properties.overrides = null; + if (!Config.overrides) { + payload.overrides = null; } break; - case 'Conversions': + case EVENT_NAMES.CONVERSIONS: payload = constructPayload(message, CONVERSIONS_CONFIG); payload.client_name = Config.clientName; + payload.unixtime = toUnixTimestamp(payload.unixtime); + validateBidders(payload.bidders); break; default: break; @@ -34,16 +60,16 @@ const constructFullPayload = (message, Config) => { return payload; }; -const getEndpoint = (Config, message) => { +const getEndpoint = (eventName, Config) => { let endpoint = Config.apiBaseUrl; - switch (message.event) { - case 'Impressions': + switch (eventName) { + case EVENT_NAMES.IMPRESSIONS: endpoint += '?action=impression'; break; - case 'Clicks': + case EVENT_NAMES.CLICKS: endpoint += '?action=click'; break; - case 'Conversions': + case EVENT_NAMES.CONVERSIONS: endpoint += '/conversion'; break; default: @@ -52,13 +78,20 @@ const getEndpoint = (Config, message) => { return endpoint; }; -const constructResponse = (payload, Config, message) => { +/** + * This function constructs response based upon event. + * @param {*} eventName + * @param {*} Config + * @param {*} payload + * @returns + */ +const constructResponse = (eventName, Config, payload) => { const response = defaultRequestConfig(); - response.endpoint = getEndpoint(Config, message); + response.endpoint = getEndpoint(eventName, Config); response.headers = { accept: 'application/json', }; - if (message.event === 'Conversions') { + if (eventName === EVENT_NAMES.CONVERSIONS) { response.body.JSON = payload; response.method = 'POST'; response.headers = { @@ -72,4 +105,4 @@ const constructResponse = (payload, Config, message) => { return response; }; -module.exports = { constructFullPayload, getEndpoint, constructResponse }; +module.exports = { constructFullPayload, constructResponse }; From a347ab1f7fef9bdf7d797e258867b855acc00876 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Mon, 13 May 2024 11:19:29 +0530 Subject: [PATCH 147/240] feat: onboard monday to proxy (#3347) --- src/v1/destinations/monday/networkHandler.js | 108 +++++++ .../destinations/monday/dataDelivery/data.ts | 189 ++++++++++++ .../destinations/monday/network.ts | 283 ++++++++++++++++++ 3 files changed, 580 insertions(+) create mode 100644 src/v1/destinations/monday/networkHandler.js create mode 100644 test/integrations/destinations/monday/dataDelivery/data.ts diff --git a/src/v1/destinations/monday/networkHandler.js b/src/v1/destinations/monday/networkHandler.js new file mode 100644 index 0000000000..28a7f1abc0 --- /dev/null +++ b/src/v1/destinations/monday/networkHandler.js @@ -0,0 +1,108 @@ +const { TransformerProxyError } = require('../../../v0/util/errorTypes'); +const { proxyRequest, prepareProxyRequest } = require('../../../adapters/network'); +const { + processAxiosResponse, + getDynamicErrorType, +} = require('../../../adapters/utils/networkUtils'); +const { isHttpStatusSuccess } = require('../../../v0/util/index'); +const tags = require('../../../v0/util/tags'); + +const checkIfUpdationOfStatusRequired = (response) => { + let errorMsg = ''; + const responseBodyStatusCode = response.status_code; + if ( + response.hasOwnProperty('error_message') || + response.hasOwnProperty('error_code') || + response.hasOwnProperty('errors') + ) { + errorMsg = response.error_message || response.errors?.map((error) => error.message).join(', '); + return { hasError: true, errorMsg, responseBodyStatusCode }; + } + return { hasError: false, errorMsg, responseBodyStatusCode }; +}; + +// { +// response: { +// errors: [ +// { +// message: "Field 'region' doesn't exist on type 'User'", +// locations: [{ line: 322, column: 5 }], +// fields: ['query', 'me', 'region'], +// }, +// ], +// account_id: 123456789, +// }, +// status: 200, +// } +// Ref: https://developer.monday.com/api-reference/docs/errors + +const responseHandler = (responseParams) => { + const { destinationResponse, rudderJobMetadata } = responseParams; + + const message = '[MONDAY Response V1 Handler] - Request Processed Successfully'; + const responseWithIndividualEvents = []; + const { response, status } = destinationResponse; + + // batching not supported + if (isHttpStatusSuccess(status)) { + const proxyOutput = { + statusCode: 200, + metadata: rudderJobMetadata[0], + error: 'success', + }; + // update status of event if abortable or retryable + const { hasError, errorMsg, responseBodyStatusCode } = + checkIfUpdationOfStatusRequired(response); + if (hasError) { + proxyOutput.statusCode = responseBodyStatusCode || 400; + proxyOutput.error = errorMsg; + } + responseWithIndividualEvents.push(proxyOutput); + + if (responseBodyStatusCode === 500 || responseBodyStatusCode === 429) { + throw new TransformerProxyError( + `MONDAY: Error encountered in transformer proxy V1 with error: ${errorMsg}`, + responseBodyStatusCode, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(responseBodyStatusCode), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); + } + + return { + status, + message, + response: responseWithIndividualEvents, + }; + } + + const errorMsg = + response.error_message || response.errors?.map((error) => error.message).join(', '); + + responseWithIndividualEvents.push({ + statusCode: status, + metadata: rudderJobMetadata, + error: errorMsg, + }); + + throw new TransformerProxyError( + `MONDAY: Error encountered in transformer proxy V1 with error: ${errorMsg}`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); +}; +function networkHandler() { + this.proxy = proxyRequest; + this.processAxiosResponse = processAxiosResponse; + this.prepareProxy = prepareProxyRequest; + this.responseHandler = responseHandler; +} +module.exports = { networkHandler }; diff --git a/test/integrations/destinations/monday/dataDelivery/data.ts b/test/integrations/destinations/monday/dataDelivery/data.ts new file mode 100644 index 0000000000..16d2047095 --- /dev/null +++ b/test/integrations/destinations/monday/dataDelivery/data.ts @@ -0,0 +1,189 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; + +const commonHeaders = { + Authorization: 'authToken', +}; + +const commonRequestParameters = { + headers: commonHeaders, +}; + +export const data: ProxyV1TestData[] = [ + { + id: 'monday_v1_scenario_1', + name: 'monday', + description: 'Sucess reponse from monday', + feature: 'dataDelivery', + successCriteria: 'Should return 200 with no error with destination response', + module: 'destination', + scenario: 'Business', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: 'https://api.monday.com/v2', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[MONDAY Response V1 Handler] - Request Processed Successfully', + response: [ + { + error: 'success', + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + }, + }, + { + id: 'monday_v1_scenario_2', + name: 'monday', + description: 'Error response with 200 status', + feature: 'dataDelivery', + successCriteria: 'Should return 200 with no error with destination response', + module: 'destination', + scenario: 'Business', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + headers: { + Authorization: 'errorAuth', + }, + endpoint: 'https://api.monday.com/v2', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[MONDAY Response V1 Handler] - Request Processed Successfully', + response: [ + { + error: "Field 'region' doesn't exist on type 'User'", + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + }, + }, + { + id: 'monday_v1_scenario_3', + name: 'monday', + description: 'Rate limit exceeded request', + feature: 'dataDelivery', + successCriteria: 'Should return throlled with correct status code', + module: 'destination', + scenario: 'Business', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + headers: { + Authorization: 'rateLimitAuthToken', + }, + endpoint: 'https://api.monday.com/v2', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '{"error_message":"Rate Limit Exceeded.","status_code":429}', + statusCode: 429, + metadata: generateMetadata(1), + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'throttled', + destType: 'MONDAY', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: + 'MONDAY: Error encountered in transformer proxy V1 with error: Rate Limit Exceeded.', + status: 429, + }, + }, + }, + }, + }, + { + id: 'monday_v1_scenario_4', + name: 'monday', + description: 'Invalid request with bad query data', + feature: 'dataDelivery', + successCriteria: 'Should return 400 with error message', + module: 'destination', + scenario: 'Business', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + headers: { + Authorization: 'internalServerAuthToken', + }, + endpoint: 'https://api.monday.com/v2', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '{"error_message":"Internal server error","status_code":500}', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'MONDAY', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: + 'MONDAY: Error encountered in transformer proxy V1 with error: Internal server error', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/monday/network.ts b/test/integrations/destinations/monday/network.ts index f23b9061f8..f91952069a 100644 --- a/test/integrations/destinations/monday/network.ts +++ b/test/integrations/destinations/monday/network.ts @@ -1,4 +1,287 @@ export const networkCallsData = [ + { + httpReq: { + url: 'https://api.monday.com/v2', + method: 'POST', + headers: { + Authorization: 'authToken', + }, + }, + httpRes: { + data: { + data: { + boards: [ + { + name: 'Planning', + columns: [ + { + id: 'name', + title: 'Name', + type: 'name', + description: null, + settings_str: '{}', + }, + { + id: 'subitems', + title: 'Subitems', + type: 'subtasks', + description: null, + settings_str: + '{"allowMultipleItems":true,"itemTypeName":"column.subtasks.title","displayType":"BOARD_INLINE","boardIds":[3160974974]}', + }, + { + id: 'person', + title: 'Person', + type: 'multiple-person', + description: null, + settings_str: '{}', + }, + { + id: 'status', + title: 'Status', + type: 'color', + description: null, + settings_str: + '{"labels":{"0":"Working on it","1":"Done","2":"Stuck"},"labels_positions_v2":{"0":0,"1":2,"2":1,"5":3},"labels_colors":{"0":{"color":"#fdab3d","border":"#E99729","var_name":"orange"},"1":{"color":"#00c875","border":"#00B461","var_name":"green-shadow"},"2":{"color":"#e2445c","border":"#CE3048","var_name":"red-shadow"}}}', + }, + { + id: 'date4', + title: 'Date', + type: 'date', + description: null, + settings_str: '{}', + }, + { + id: 'checkbox', + title: 'Checkbox', + type: 'boolean', + description: null, + settings_str: '{}', + }, + { + id: 'connect_boards', + title: 'Connect boards', + type: 'board-relation', + description: null, + settings_str: '{"allowCreateReflectionColumn":false}', + }, + { + id: 'status_1', + title: 'Other', + type: 'color', + description: null, + settings_str: + '{"labels":{"0":"Working on it","1":"Done","2":"Stuck"},"labels_colors":{"0":{"color":"#fdab3d","border":"#E99729","var_name":"orange"},"1":{"color":"#00c875","border":"#00B461","var_name":"green-shadow"},"2":{"color":"#e2445c","border":"#CE3048","var_name":"red-shadow"}}}', + }, + { + id: 'date_1', + title: 'Date 1', + type: 'date', + description: null, + settings_str: '{"hide_footer":false}', + }, + { + id: 'status_12', + title: 'new status', + type: 'color', + description: null, + settings_str: + '{"labels":{"0":"Working on it","1":"Done","2":"Stuck"},"labels_colors":{"0":{"color":"#fdab3d","border":"#E99729","var_name":"orange"},"1":{"color":"#00c875","border":"#00B461","var_name":"green-shadow"},"2":{"color":"#e2445c","border":"#CE3048","var_name":"red-shadow"}}}', + }, + { + id: 'numbers', + title: 'Numbers', + type: 'numeric', + description: null, + settings_str: '{}', + }, + { + id: 'text', + title: 'Name', + type: 'text', + description: null, + settings_str: '{}', + }, + { + id: 'country', + title: 'Country', + type: 'country', + description: null, + settings_str: '{}', + }, + { + id: 'dropdown', + title: 'Dropdown', + type: 'dropdown', + description: null, + settings_str: + '{"hide_footer":false,"labels":[{"id":1,"name":"dropdown"},{"id":2,"name":"dropup"}]}', + }, + { + id: 'email', + title: 'Email', + type: 'email', + description: null, + settings_str: '{}', + }, + { + id: 'location', + title: 'Location', + type: 'location', + description: null, + settings_str: '{}', + }, + { + id: 'phone', + title: 'Phone', + type: 'phone', + description: null, + settings_str: '{}', + }, + { + id: 'rating', + title: 'Rating', + type: 'rating', + description: null, + settings_str: '{}', + }, + { + id: 'timeline', + title: 'Timeline', + type: 'timerange', + description: null, + settings_str: '{"hide_footer":false}', + }, + { + id: 'dependent_on', + title: 'Dependent On', + type: 'dependency', + description: + 'Choose the item your task will be dependent on. If the “dependent on” item’s date is changing, the other dates will adjust automatically', + settings_str: + '{"boardIds":[3142482015],"dependencyNewInfra":true,"allowMultipleItems":true}', + }, + { + id: 'long_text', + title: 'Long Text', + type: 'long-text', + description: null, + settings_str: '{}', + }, + { + id: 'link', + title: 'Link', + type: 'link', + description: null, + settings_str: '{}', + }, + { + id: 'tags', + title: 'Tags', + type: 'tag', + description: null, + settings_str: '{"hide_footer":false}', + }, + { + id: 'label', + title: 'Label', + type: 'color', + description: '', + settings_str: + '{"done_colors":[1],"labels":{"3":"Label 2","105":"Label 1","156":"Label 3"},"labels_positions_v2":{"3":1,"5":3,"105":0,"156":2},"labels_colors":{"3":{"color":"#0086c0","border":"#3DB0DF","var_name":"blue-links"},"105":{"color":"#9AADBD","border":"#9AADBD","var_name":"winter"},"156":{"color":"#9D99B9","border":"#9D99B9","var_name":"purple_gray"}}}', + }, + { + id: 'world_clock', + title: 'World Clock', + type: 'timezone', + description: null, + settings_str: '{}', + }, + { + id: 'week', + title: 'Week', + type: 'week', + description: null, + settings_str: '{}', + }, + ], + groups: [ + { + id: 'topics', + title: 'This month', + }, + { + id: 'group_title', + title: 'Next month', + }, + ], + }, + ], + }, + account_id: 13215538, + }, + status: 200, + }, + }, + { + httpReq: { + url: 'https://api.monday.com/v2', + method: 'POST', + headers: { + Authorization: 'errorAuth', + }, + }, + httpRes: { + data: { + errors: [ + { + message: "Field 'region' doesn't exist on type 'User'", + locations: [ + { + line: 322, + column: 5, + }, + ], + fields: ['query', 'me', 'region'], + }, + ], + account_id: 123456789, + }, + status: 200, + }, + }, + { + httpReq: { + url: 'https://api.monday.com/v2', + method: 'POST', + headers: { + Authorization: 'rateLimitAuthToken', + }, + }, + httpRes: { + data: { + error_message: 'Rate Limit Exceeded.', + status_code: 429, + }, + status: 200, + }, + }, + { + httpReq: { + url: 'https://api.monday.com/v2', + method: 'POST', + headers: { + Authorization: 'internalServerAuthToken', + }, + }, + httpRes: { + data: { + error_message: 'Internal server error', + status_code: 500, + }, + status: 500, + }, + }, { httpReq: { url: 'https://api.monday.com/v2', From 265a71d20b484205245c20dd3e47d108dc2fd16f Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Mon, 13 May 2024 12:44:40 +0530 Subject: [PATCH 148/240] fix: ninetailed: modify parameter requirements and add default values (#3364) * fix: ninetailed: modify parameter requirements and add default values * chore: add default value for userId --- src/cdk/v2/destinations/ninetailed/config.js | 1 - .../ninetailed/data/contextMapping.json | 13 ++-- .../data/generalPayloadMapping.json | 4 +- .../ninetailed/data/identifyMapping.json | 5 +- .../ninetailed/data/trackMapping.json | 4 +- .../destinations/ninetailed/procWorkflow.yaml | 1 - src/cdk/v2/destinations/ninetailed/utils.js | 1 + .../destinations/ninetailed/commonConfig.ts | 3 +- .../ninetailed/processor/identify.ts | 64 +++++++++++++++---- .../destinations/ninetailed/router/data.ts | 23 +++++-- 10 files changed, 87 insertions(+), 32 deletions(-) diff --git a/src/cdk/v2/destinations/ninetailed/config.js b/src/cdk/v2/destinations/ninetailed/config.js index a59b2a1671..efb1a8908e 100644 --- a/src/cdk/v2/destinations/ninetailed/config.js +++ b/src/cdk/v2/destinations/ninetailed/config.js @@ -19,7 +19,6 @@ const ConfigCategories = { }, }; -// MAX_BATCH_SIZE : // Maximum number of events to send in a single batch const mappingConfig = getMappingConfig(ConfigCategories, __dirname); const batchEndpoint = 'https://experience.ninetailed.co/v2/organizations/{{organisationId}}/environments/{{environment}}/events'; diff --git a/src/cdk/v2/destinations/ninetailed/data/contextMapping.json b/src/cdk/v2/destinations/ninetailed/data/contextMapping.json index f2373b61c1..ad301c8150 100644 --- a/src/cdk/v2/destinations/ninetailed/data/contextMapping.json +++ b/src/cdk/v2/destinations/ninetailed/data/contextMapping.json @@ -1,12 +1,10 @@ [ { "sourceKeys": "app.name", - "required": true, "destKey": "app.name" }, { "sourceKeys": "app.version", - "required": true, "destKey": "app.version" }, { @@ -15,12 +13,16 @@ }, { "sourceKeys": "library.name", - "required": true, - "destKey": "library.name" + "destKey": "library.name", + "metadata": { + "defaultValue": "Rudderstack Ninetailed Destination" + } }, { "sourceKeys": "library.version", - "required": true, + "metadata": { + "defaultValue": "1" + }, "destKey": "library.version" }, { @@ -37,7 +39,6 @@ }, { "sourceKeys": "location", - "required": false, "metadata": { "defaultValue": {} }, diff --git a/src/cdk/v2/destinations/ninetailed/data/generalPayloadMapping.json b/src/cdk/v2/destinations/ninetailed/data/generalPayloadMapping.json index 3ab72d1b9f..22fc637728 100644 --- a/src/cdk/v2/destinations/ninetailed/data/generalPayloadMapping.json +++ b/src/cdk/v2/destinations/ninetailed/data/generalPayloadMapping.json @@ -11,7 +11,9 @@ }, { "sourceKeys": "channel", - "required": true, + "metadata": { + "defaultValue": "server" + }, "destKey": "channel" }, { diff --git a/src/cdk/v2/destinations/ninetailed/data/identifyMapping.json b/src/cdk/v2/destinations/ninetailed/data/identifyMapping.json index e8d3f7797d..b1a340bd98 100644 --- a/src/cdk/v2/destinations/ninetailed/data/identifyMapping.json +++ b/src/cdk/v2/destinations/ninetailed/data/identifyMapping.json @@ -2,13 +2,14 @@ { "sourceKeys": "traits", "sourceFromGenericMap": true, - "required": true, + "metadata": { + "defaultValue": {} + }, "destKey": "traits" }, { "sourceKeys": "userIdOnly", "sourceFromGenericMap": true, - "required": true, "destKey": "userId" } ] diff --git a/src/cdk/v2/destinations/ninetailed/data/trackMapping.json b/src/cdk/v2/destinations/ninetailed/data/trackMapping.json index 44af6dd1a3..5a13f5bba2 100644 --- a/src/cdk/v2/destinations/ninetailed/data/trackMapping.json +++ b/src/cdk/v2/destinations/ninetailed/data/trackMapping.json @@ -1,7 +1,9 @@ [ { "sourceKeys": "properties", - "required": true, + "metadata": { + "defaultValue": {} + }, "destKey": "properties" }, { diff --git a/src/cdk/v2/destinations/ninetailed/procWorkflow.yaml b/src/cdk/v2/destinations/ninetailed/procWorkflow.yaml index 383b850a4d..e31912386a 100644 --- a/src/cdk/v2/destinations/ninetailed/procWorkflow.yaml +++ b/src/cdk/v2/destinations/ninetailed/procWorkflow.yaml @@ -23,7 +23,6 @@ steps: template: | const payload = $.constructFullPayload(.message); $.context.payload = $.removeUndefinedAndNullValues(payload); - - name: buildResponse template: | const response = $.defaultRequestConfig(); diff --git a/src/cdk/v2/destinations/ninetailed/utils.js b/src/cdk/v2/destinations/ninetailed/utils.js index 47b27b3b9d..fec1271561 100644 --- a/src/cdk/v2/destinations/ninetailed/utils.js +++ b/src/cdk/v2/destinations/ninetailed/utils.js @@ -30,6 +30,7 @@ const constructFullPayload = (message) => { message, config.mappingConfig[config.ConfigCategories.IDENTIFY.name], ); + typeSpecifcPayload.userId = typeSpecifcPayload.userId || ''; break; default: break; diff --git a/test/integrations/destinations/ninetailed/commonConfig.ts b/test/integrations/destinations/ninetailed/commonConfig.ts index 4baf72dee1..99df88dd66 100644 --- a/test/integrations/destinations/ninetailed/commonConfig.ts +++ b/test/integrations/destinations/ninetailed/commonConfig.ts @@ -96,11 +96,10 @@ export const contextWithNoLocation = { userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', }; -export const commonInputWithNoLocation = { +export const commonInputWithNoLocationAndChannel = { anonymousId: 'anon_123', messageId: 'dummy_msg_id', context: contextWithNoLocation, - channel: 'web', integrations: { All: true, }, diff --git a/test/integrations/destinations/ninetailed/processor/identify.ts b/test/integrations/destinations/ninetailed/processor/identify.ts index 3bb333c160..b580e17ed0 100644 --- a/test/integrations/destinations/ninetailed/processor/identify.ts +++ b/test/integrations/destinations/ninetailed/processor/identify.ts @@ -2,9 +2,8 @@ import { destination, traits, commonInput, - commonInputWithNoLocation, + commonInputWithNoLocationAndChannel, metadata, - processInstrumentationErrorStatTags, } from '../commonConfig'; import { transformResultBuilder } from '../../../testUtils'; export const identify = [ @@ -110,12 +109,12 @@ export const identify = [ }, }, { - id: 'ninetailed-test-identify-failure-1', + id: 'ninetailed-test-identify-success-2', name: 'ninetailed', description: 'identify call with no userId available', scenario: 'Framework', successCriteria: - 'Error should be thrown for required field userId not present and status code should be 200', + 'No Error should be thrown for field userId not present but default empty string should be provided to userId and status code should be 200', feature: 'processor', module: 'destination', version: 'v0', @@ -125,9 +124,8 @@ export const identify = [ { destination, message: { - ...commonInput, + ...commonInputWithNoLocationAndChannel, type: 'identify', - channel: 'mobile', messageId: 'dummy_msg_id', traits: traits, }, @@ -141,13 +139,55 @@ export const identify = [ status: 200, body: [ { - error: - 'Missing required value from "userIdOnly": Workflow: procWorkflow, Step: preparePayload, ChildStep: undefined, OriginalError: Missing required value from "userIdOnly"', metadata: { destinationId: 'dummyDestId', }, - statTags: processInstrumentationErrorStatTags, - statusCode: 400, + output: transformResultBuilder({ + method: 'POST', + endpoint: + 'https://experience.ninetailed.co/v2/organizations/dummyOrganisationId/environments/main/events', + JSON: { + events: [ + { + context: { + app: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + campaign: { + name: 'campign_123', + source: 'social marketing', + medium: 'facebook', + term: '1 year', + }, + library: { + name: 'RudderstackSDK', + version: 'Ruddderstack SDK version', + }, + locale: 'en-US', + page: { + path: '/signup', + referrer: 'https://rudderstack.medium.com/', + search: '?type=freetrial', + url: 'https://app.rudderstack.com/signup?type=freetrial', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + location: {}, + }, + type: 'identify', + userId: '', + channel: 'server', + messageId: 'dummy_msg_id', + traits: traits, + anonymousId: 'anon_123', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }, + ], + }, + userId: '', + }), + statusCode: 200, }, ], }, @@ -169,7 +209,7 @@ export const identify = [ destination, message: { type: 'identify', - ...commonInputWithNoLocation, + ...commonInputWithNoLocationAndChannel, userId: 'sajal12', traits: traits, integrations: { @@ -224,7 +264,7 @@ export const identify = [ location: {}, }, type: 'identify', - channel: 'web', + channel: 'server', userId: 'sajal12', messageId: 'dummy_msg_id', traits: traits, diff --git a/test/integrations/destinations/ninetailed/router/data.ts b/test/integrations/destinations/ninetailed/router/data.ts index 1bf664d1c4..14ac046127 100644 --- a/test/integrations/destinations/ninetailed/router/data.ts +++ b/test/integrations/destinations/ninetailed/router/data.ts @@ -3,6 +3,7 @@ import { destination, commonOutput, routerInstrumentationErrorStatTags, + context, } from '../commonConfig'; import { trackProperties, pageProperties, traits } from './basicProperties'; import { defaultMockFns } from '../mocks'; @@ -137,9 +138,14 @@ export const data = [ { message: { type: 'identify', - ...commonInput, + messageId: 'dummy_msg_id', + context, + channel: 'web', + integrations: { + All: true, + }, + originalTimestamp: '2021-01-25T15:32:56.409Z', traits, - integrations: { All: true }, }, metadata: { jobId: 3, userId: 'u1' }, destination, @@ -158,7 +164,7 @@ export const data = [ { batched: false, destination, - error: 'Missing required value from "userIdOnly"', + error: 'Missing required value from "anonymousId"', metadata: [{ jobId: 3, userId: 'u1' }], statTags: routerInstrumentationErrorStatTags, statusCode: 400, @@ -259,9 +265,14 @@ export const data = [ { message: { type: 'identify', - ...commonInput, + messageId: 'dummy_msg_id', + context, + channel: 'web', + integrations: { + All: true, + }, + originalTimestamp: '2021-01-25T15:32:56.409Z', traits, - integrations: { All: true }, }, metadata: { jobId: 4, userId: 'u1' }, destination, @@ -280,7 +291,7 @@ export const data = [ { batched: false, destination, - error: 'Missing required value from "userIdOnly"', + error: 'Missing required value from "anonymousId"', metadata: [{ jobId: 4, userId: 'u1' }], statTags: routerInstrumentationErrorStatTags, statusCode: 400, From a354a6245875e4236668f23faca13ea8f8b8a568 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 13 May 2024 08:44:03 +0000 Subject: [PATCH 149/240] chore(release): 1.66.0 --- CHANGELOG.md | 20 ++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a63e10b793..4763c101ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,26 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.66.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.65.1...v1.66.0) (2024-05-13) + + +### Features + +* add identify event and the latest slack event schema ([d0c5942](https://github.com/rudderlabs/rudder-transformer/commit/d0c5942e6255dab97b493fff311a4af71a142fd8)) +* add slack source ([9558e3b](https://github.com/rudderlabs/rudder-transformer/commit/9558e3bf848213bc75a58297d234d823b72a274a)) +* add slack source ([#3148](https://github.com/rudderlabs/rudder-transformer/issues/3148)) ([3cbb011](https://github.com/rudderlabs/rudder-transformer/commit/3cbb011811b6f7b52ab29b4ef2794af9c2a5d5f0)) +* onboard monday to proxy ([#3347](https://github.com/rudderlabs/rudder-transformer/issues/3347)) ([a347ab1](https://github.com/rudderlabs/rudder-transformer/commit/a347ab1f7fef9bdf7d797e258867b855acc00876)) + + +### Bug Fixes + +* add originalTimestamp from slack ts property ([b19eb8c](https://github.com/rudderlabs/rudder-transformer/commit/b19eb8c699b60e90e02b39e3a448e6aac721e37f)) +* default event name normalization to undefined if it cannot be parsed ([db11c3e](https://github.com/rudderlabs/rudder-transformer/commit/db11c3e3d70d13a67d21c5b0043e74ce801bcb4a)) +* filtering url verification event via evvent type ([42d6010](https://github.com/rudderlabs/rudder-transformer/commit/42d6010d93b79ccb996c9548f4c0eeba0e3dacc2)) +* handle error-first ([0771e87](https://github.com/rudderlabs/rudder-transformer/commit/0771e87ff73181c412636f9bde40b8947a3e3080)) +* ninetailed: modify parameter requirements and add default values ([#3364](https://github.com/rudderlabs/rudder-transformer/issues/3364)) ([265a71d](https://github.com/rudderlabs/rudder-transformer/commit/265a71d20b484205245c20dd3e47d108dc2fd16f)) +* use optional chain parameter ([13be5cf](https://github.com/rudderlabs/rudder-transformer/commit/13be5cfb85363cf5c0527247787979467d63caaa)) + ### [1.65.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.65.0...v1.65.1) (2024-05-10) diff --git a/package-lock.json b/package-lock.json index 23f524d0ac..14ab853b82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.65.1", + "version": "1.66.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.65.1", + "version": "1.66.0", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index 00dad8305f..168f49da5e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.65.1", + "version": "1.66.0", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From ff29f8578d2369fcd9f0d86e1b7dffe3146e6a3a Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Mon, 13 May 2024 16:15:00 +0530 Subject: [PATCH 150/240] feat: onboard emarsys destination (#3318) (#3369) feat: onboard emersys destination (#3318) --------- Co-authored-by: Dilip Kola Co-authored-by: Shrouti Gangopadhyay --- src/cdk/v2/destinations/emarsys/config.js | 24 + .../v2/destinations/emarsys/procWorkflow.yaml | 88 ++ .../v2/destinations/emarsys/rtWorkflow.yaml | 38 + src/cdk/v2/destinations/emarsys/utils.js | 411 +++++ src/cdk/v2/destinations/emarsys/utils.test.js | 543 +++++++ src/constants/destinationCanonicalNames.js | 1 + src/features.json | 3 +- src/services/destination/nativeIntegration.ts | 1 + src/v0/destinations/emarsys/deleteUsers.js | 93 ++ src/v0/util/deleteUserUtils.js | 14 +- src/v1/destinations/emarsys/networkHandler.js | 122 ++ .../destinations/emarsys/dataDelivery/data.ts | 560 +++++++ .../destinations/emarsys/deleteUsers/data.ts | 235 +++ .../destinations/emarsys/network.ts | 298 ++++ .../destinations/emarsys/processor/data.ts | 1380 +++++++++++++++++ .../destinations/emarsys/router/data.ts | 646 ++++++++ 16 files changed, 4455 insertions(+), 2 deletions(-) create mode 100644 src/cdk/v2/destinations/emarsys/config.js create mode 100644 src/cdk/v2/destinations/emarsys/procWorkflow.yaml create mode 100644 src/cdk/v2/destinations/emarsys/rtWorkflow.yaml create mode 100644 src/cdk/v2/destinations/emarsys/utils.js create mode 100644 src/cdk/v2/destinations/emarsys/utils.test.js create mode 100644 src/v0/destinations/emarsys/deleteUsers.js create mode 100644 src/v1/destinations/emarsys/networkHandler.js create mode 100644 test/integrations/destinations/emarsys/dataDelivery/data.ts create mode 100644 test/integrations/destinations/emarsys/deleteUsers/data.ts create mode 100644 test/integrations/destinations/emarsys/network.ts create mode 100644 test/integrations/destinations/emarsys/processor/data.ts create mode 100644 test/integrations/destinations/emarsys/router/data.ts diff --git a/src/cdk/v2/destinations/emarsys/config.js b/src/cdk/v2/destinations/emarsys/config.js new file mode 100644 index 0000000000..83067c3cd3 --- /dev/null +++ b/src/cdk/v2/destinations/emarsys/config.js @@ -0,0 +1,24 @@ +const ALLOWED_OPT_IN_VALUES = ['1', '2', '']; +const groupedSuccessfulPayload = { + identify: { + method: 'PUT', + batches: [], + }, + group: { + method: 'POST', + batches: [], + }, + track: { + method: 'POST', + batches: [], + }, +}; + +module.exports = { + MAX_BATCH_SIZE: 1000, + EMAIL_FIELD_ID: 3, + OPT_IN_FILED_ID: 31, + ALLOWED_OPT_IN_VALUES, + MAX_BATCH_SIZE_BYTES: 8000000, // 8 MB, + groupedSuccessfulPayload, +}; diff --git a/src/cdk/v2/destinations/emarsys/procWorkflow.yaml b/src/cdk/v2/destinations/emarsys/procWorkflow.yaml new file mode 100644 index 0000000000..a5c0b33f38 --- /dev/null +++ b/src/cdk/v2/destinations/emarsys/procWorkflow.yaml @@ -0,0 +1,88 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + exportAll: true + - name: removeUndefinedValues + path: ../../../../v0/util + - name: removeUndefinedAndNullValues + path: ../../../../v0/util + - name: defaultRequestConfig + path: ../../../../v0/util + - name: getIntegrationsObj + path: ../../../../v0/util + - name: getFieldValueFromMessage + path: ../../../../v0/util + - name: CommonUtils + path: ../../../../util/common + - path: ./utils + - path: ./config + - path: lodash + name: cloneDeep + +steps: + - name: checkIfProcessed + condition: .message.statusCode + template: | + $.batchMode ? .message.body.JSON : .message + onComplete: return + - name: messageType + template: | + .message.type.toLowerCase() + - name: validateInput + template: | + let messageType = $.outputs.messageType; + $.assert(messageType, "Message type is not present. Aborting message."); + $.assert(messageType in {{$.EventType.([.TRACK, .IDENTIFY, .GROUP])}}, + "message type " + messageType + " is not supported") + $.assertConfig(.destination.Config.emersysUsername, "Emersys user name is not configured. Aborting"); + $.assertConfig(.destination.Config.emersysUserSecret, "Emersys user secret is not configured. Aborting"); + + - name: validateInputForTrack + description: Additional validation for Track events + condition: $.outputs.messageType === {{$.EventType.TRACK}} + template: | + $.assert(.message.event, "event could not be mapped to conversion rule. Aborting.") + - name: preparePayloadForIdentify + description: | + Builds identify payload. ref: https://dev.emarsys.com/docs/core-api-reference/f8ljhut3ac2i1-update-contacts + condition: $.outputs.messageType === {{$.EventType.IDENTIFY}} + template: | + $.context.payload = $.buildIdentifyPayload(.message, .destination.Config,); + - name: preparePayloadForGroup + description: | + Builds group payload. ref: https://dev.emarsys.com/docs/core-api-reference/1m0m70hy3tuov-add-contacts-to-a-contact-list + condition: $.outputs.messageType === {{$.EventType.GROUP}} + template: | + $.context.payload = $.buildGroupPayload(.message, .destination.Config,); + - name: preparePayloadForTrack + description: | + Builds track payload. ref: https://dev.emarsys.com/docs/core-api-reference/fl0xx6rwfbwqb-trigger-an-external-event + condition: $.outputs.messageType === {{$.EventType.TRACK}} + template: | + const properties = ^.message.properties; + const integrationObject = $.getIntegrationsObj(^.message, 'emarsys'); + const emersysIdentifierId = $.deduceCustomIdentifier(integrationObject, ^.destination.Config.emersysCustomIdentifier); + const payload = { + key_id: emersysIdentifierId, + external_id: $.deduceExternalIdValue(^.message,emersysIdentifierId,.destination.Config.fieldMapping), + trigger_id: integrationObject.trigger_id, + data: properties.data, + attachment:$.CommonUtils.toArray(properties.attachment), + event_time: $.getFieldValueFromMessage(^.message, 'timestamp'), + }; + $.context.payload = { + eventType: ^.message.type, + destinationPayload: { + payload: $.removeUndefinedAndNullValues(payload), + eventId: $.deduceEventId(^.message,.destination.Config), + }, + }; + - name: buildResponse + template: | + const response = $.defaultRequestConfig(); + response.body.JSON = $.context.payload; + response.endpoint = $.deduceEndPoint($.context.payload,.destination.Config); + response.method = "POST"; + response.headers = $.buildHeader(.destination.Config) + response diff --git a/src/cdk/v2/destinations/emarsys/rtWorkflow.yaml b/src/cdk/v2/destinations/emarsys/rtWorkflow.yaml new file mode 100644 index 0000000000..0e7132ccad --- /dev/null +++ b/src/cdk/v2/destinations/emarsys/rtWorkflow.yaml @@ -0,0 +1,38 @@ +bindings: + - path: ./utils + - name: handleRtTfSingleEventError + path: ../../../../v0/util/index + +steps: + - name: validateInput + template: | + $.assert(Array.isArray(^) && ^.length > 0, "Invalid event array") + + - name: transform + externalWorkflow: + path: ./procWorkflow.yaml + bindings: + - name: batchMode + value: true + loopOverInput: true + - name: successfulEvents + template: | + $.outputs.transform#idx.output.({ + "message": .[], + "destination": ^ [idx].destination, + "metadata": ^ [idx].metadata + })[] + - name: failedEvents + template: | + $.outputs.transform#idx.error.( + $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) + )[] + + - name: batchSuccessfulEvents + description: Batches the successfulEvents + template: | + $.context.batchedPayload = $.batchResponseBuilder($.outputs.successfulEvents); + + - name: finalPayload + template: | + [...$.outputs.failedEvents, ...$.context.batchedPayload] diff --git a/src/cdk/v2/destinations/emarsys/utils.js b/src/cdk/v2/destinations/emarsys/utils.js new file mode 100644 index 0000000000..2fe686718d --- /dev/null +++ b/src/cdk/v2/destinations/emarsys/utils.js @@ -0,0 +1,411 @@ +const lodash = require('lodash'); +const crypto = require('crypto'); +const { + InstrumentationError, + ConfigurationError, + isDefinedAndNotNullAndNotEmpty, + removeUndefinedAndNullAndEmptyValues, + removeUndefinedAndNullValues, + isDefinedAndNotNull, +} = require('@rudderstack/integrations-lib'); +const { + getIntegrationsObj, + validateEventName, + getValueFromMessage, + getHashFromArray, +} = require('../../../../v0/util'); +const { + EMAIL_FIELD_ID, + MAX_BATCH_SIZE, + OPT_IN_FILED_ID, + ALLOWED_OPT_IN_VALUES, + MAX_BATCH_SIZE_BYTES, + groupedSuccessfulPayload, +} = require('./config'); +const { EventType } = require('../../../../constants'); + +const base64Sha = (str) => { + const hexDigest = crypto.createHash('sha1').update(str).digest('hex'); + return Buffer.from(hexDigest).toString('base64'); +}; + +const getWsseHeader = (user, secret) => { + const nonce = crypto.randomBytes(16).toString('hex'); + const timestamp = new Date().toISOString(); + + const digest = base64Sha(nonce + timestamp + secret); + return `UsernameToken Username="${user}", PasswordDigest="${digest}", Nonce="${nonce}", Created="${timestamp}"`; +}; + +const buildHeader = (destConfig) => { + const { emersysUsername, emersysUserSecret } = destConfig; + if ( + !isDefinedAndNotNullAndNotEmpty(emersysUsername) || + !isDefinedAndNotNullAndNotEmpty(emersysUserSecret) + ) { + throw new ConfigurationError('Either Emarsys user name or user secret is missing. Aborting'); + } + return { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': getWsseHeader(emersysUsername, emersysUserSecret), + }; +}; + +const deduceCustomIdentifier = (integrationObject, emersysCustomIdentifier) => + integrationObject?.customIdentifierId || emersysCustomIdentifier || EMAIL_FIELD_ID; + +const buildIdentifyPayload = (message, destConfig) => { + let destinationPayload; + const { fieldMapping, emersysCustomIdentifier, discardEmptyProperties, defaultContactList } = + destConfig; + const payload = {}; + + const integrationObject = getIntegrationsObj(message, 'emarsys'); + const finalContactList = integrationObject?.contactListId || defaultContactList; + if (!finalContactList || !isDefinedAndNotNullAndNotEmpty(String(finalContactList))) { + throw new InstrumentationError( + 'Cannot a find a specific contact list either through configuration or via integrations object', + ); + } + if (fieldMapping) { + fieldMapping.forEach((trait) => { + const { rudderProperty, emersysProperty } = trait; + const value = getValueFromMessage(message, [ + `traits.${rudderProperty}`, + `context.traits.${rudderProperty}`, + ]); + if (value) { + payload[emersysProperty] = value; + } + }); + } + const emersysIdentifier = deduceCustomIdentifier(integrationObject, emersysCustomIdentifier); + const finalPayload = + discardEmptyProperties === true + ? removeUndefinedAndNullAndEmptyValues(payload) // empty property value has a significance in emersys + : removeUndefinedAndNullValues(payload); + if ( + isDefinedAndNotNull(finalPayload[OPT_IN_FILED_ID]) && + !ALLOWED_OPT_IN_VALUES.includes(String(finalPayload[OPT_IN_FILED_ID])) + ) { + throw new InstrumentationError( + `Only ${ALLOWED_OPT_IN_VALUES} values are allowed for optin field`, + ); + } + + if (isDefinedAndNotNullAndNotEmpty(payload[emersysIdentifier])) { + destinationPayload = { + key_id: emersysIdentifier, + contacts: [finalPayload], + contact_list_id: finalContactList, + }; + } else { + throw new InstrumentationError( + 'Either configured custom contact identifier value or default identifier email value is missing', + ); + } + return { eventType: message.type, destinationPayload }; +}; + +const findRudderPropertyByEmersysProperty = (emersysProperty, fieldMapping) => { + // find the object where the emersysProperty matches the input + const item = lodash.find(fieldMapping, { emersysProperty: String(emersysProperty) }); + // Return the rudderProperty if the object is found, otherwise return null + return item ? item.rudderProperty : 'email'; +}; + +const deduceExternalIdValue = (message, emersysIdentifier, fieldMapping) => { + const configuredPayloadProperty = findRudderPropertyByEmersysProperty( + emersysIdentifier, + fieldMapping, + ); + const externalIdValue = getValueFromMessage(message, [ + `traits.${configuredPayloadProperty}`, + `context.traits.${configuredPayloadProperty}`, + ]); + + if (!isDefinedAndNotNull(deduceExternalIdValue)) { + throw new InstrumentationError( + `Could not find value for externalId required in ${message.type} call. Aborting.`, + ); + } + + return externalIdValue; +}; + +const buildGroupPayload = (message, destConfig) => { + const { emersysCustomIdentifier, defaultContactList, fieldMapping } = destConfig; + const integrationObject = getIntegrationsObj(message, 'emarsys'); + const emersysIdentifier = deduceCustomIdentifier(integrationObject, emersysCustomIdentifier); + const externalIdValue = deduceExternalIdValue(message, emersysIdentifier, fieldMapping); + if (!isDefinedAndNotNull(externalIdValue)) { + throw new InstrumentationError( + `No value found in payload for contact custom identifier of id ${emersysIdentifier}`, + ); + } + const payload = { + key_id: emersysIdentifier, + external_ids: [externalIdValue], + }; + return { + eventType: message.type, + destinationPayload: { + payload, + contactListId: message.groupId || defaultContactList, + }, + }; +}; + +const deduceEventId = (message, destConfig) => { + let eventId; + const { eventsMapping } = destConfig; + const { event } = message; + validateEventName(event); + if (eventsMapping.length > 0) { + const keyMap = getHashFromArray(eventsMapping, 'from', 'to', false); + eventId = keyMap[event]; + } + if (!eventId) { + throw new ConfigurationError(`${event} is not mapped to any Emersys external event. Aborting`); + } + return eventId; +}; + +const deduceEndPoint = (finalPayload) => { + let endPoint; + let eventId; + let contactListId; + const { eventType, destinationPayload } = finalPayload; + switch (eventType) { + case EventType.IDENTIFY: + endPoint = 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1'; + break; + case EventType.GROUP: + contactListId = destinationPayload.contactListId; + endPoint = `https://api.emarsys.net/api/v2/contactlist/${contactListId}/add`; + break; + case EventType.TRACK: + eventId = destinationPayload.eventId; + endPoint = `https://api.emarsys.net/api/v2/event/${eventId}/trigger`; + break; + default: + break; + } + return endPoint; +}; + +const estimateJsonSize = (obj) => new Blob([JSON.stringify(obj)]).size; + +const createSingleIdentifyPayload = (keyId, contacts, contactListId) => ({ + key_id: keyId, + contacts, + contact_list_id: contactListId, +}); + +const ensureSizeConstraints = (contacts) => { + const chunks = []; + let currentBatch = []; + + contacts.forEach((contact) => { + // Start a new batch if adding the next contact exceeds size limits + if ( + currentBatch.length === 0 || + estimateJsonSize([...currentBatch, contact]) < MAX_BATCH_SIZE_BYTES + ) { + currentBatch.push(contact); + } else { + chunks.push(currentBatch); + currentBatch = [contact]; + } + }); + + // Add the remaining batch if not empty + if (currentBatch.length > 0) { + chunks.push(currentBatch); + } + + return chunks; +}; + +const createIdentifyBatches = (events) => { + const groupedIdentifyPayload = lodash.groupBy( + events, + (item) => + `${item.message[0].body.JSON.destinationPayload.key_id}-${item.message[0].body.JSON.destinationPayload.contact_list_id}`, + ); + return lodash.flatMap(groupedIdentifyPayload, (group) => { + const firstItem = group[0].message[0].body.JSON.destinationPayload; + // eslint-disable-next-line @typescript-eslint/naming-convention + const { key_id, contact_list_id } = firstItem; + + const allContacts = lodash.flatMap( + group, + (item) => item.message[0].body.JSON.destinationPayload.contacts, + ); + const initialChunks = lodash.chunk(allContacts, MAX_BATCH_SIZE); + const finalChunks = lodash.flatMap(initialChunks, ensureSizeConstraints); + + // Include metadata for each chunk + return finalChunks.map((contacts) => ({ + payload: createSingleIdentifyPayload(key_id, contacts, contact_list_id), + metadata: group.map((g) => g.metadata), + })); + }); +}; + +const createGroupBatches = (events) => { + const grouped = lodash.groupBy( + events, + (item) => + `${item.message[0].body.JSON.destinationPayload.payload.key_id}-${item.message[0].body.JSON.destinationPayload.contactListId}`, + ); + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + return Object.entries(grouped).flatMap(([key, group]) => { + const keyId = group[0].message[0].body.JSON.destinationPayload.payload.key_id; + const { contactListId } = group[0].message[0].body.JSON.destinationPayload; + const combinedExternalIds = group.reduce((acc, item) => { + acc.push(...item.message[0].body.JSON.destinationPayload.payload.external_ids); + return acc; + }, []); + + const idChunks = lodash.chunk(combinedExternalIds, MAX_BATCH_SIZE); + + return idChunks.map((chunk) => ({ + endpoint: `https://api.emarsys.net/api/v2/contactlist/${contactListId}/add`, + payload: { + key_id: keyId, + external_ids: chunk, + }, + metadata: group.map((g) => g.metadata), + })); + }); +}; + +const createTrackBatches = (events) => [ + { + endpoint: events[0].message[0].endpoint, + payload: events[0].message[0].body.JSON.destinationPayload.payload, + metadata: [events[0].metadata], + }, +]; +const formatIdentifyPayloadsWithEndpoint = (combinedPayloads, endpointUrl = '') => + combinedPayloads.map((singleCombinedPayload) => ({ + endpoint: endpointUrl, + payload: singleCombinedPayload.payload, + metadata: singleCombinedPayload.metadata, + })); + +const buildBatchedRequest = (batches, method, constants, batchedStatus = true) => + batches.map((batch) => ({ + batchedRequest: { + body: { + JSON: batch.payload, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: constants.version, + type: constants.type, + method, + endpoint: batch.endpoint, + headers: constants.headers, + params: {}, + files: {}, + }, + metadata: batch.metadata, + batched: batchedStatus, + statusCode: 200, + destination: constants.destination, + })); + +// Helper to initialize the constants used across batch processing +function initializeConstants(successfulEvents) { + if (successfulEvents.length === 0) return null; + return { + version: successfulEvents[0].message[0].version, + type: successfulEvents[0].message[0].type, + headers: successfulEvents[0].message[0].headers, + destination: successfulEvents[0].destination, + }; +} + +// Helper to append requests based on batched events and constants +function appendRequestsToOutput(groupPayload, output, constants, batched = true) { + if (groupPayload.batches) { + const requests = buildBatchedRequest( + groupPayload.batches, + groupPayload.method, + constants, + batched, + ); + output.push(...requests); + } +} + +// Process batches based on event types +function processEventBatches(typedEventGroups, constants) { + let batchesOfIdentifyEvents; + const finalOutput = []; + + // Process each event group based on type + Object.keys(typedEventGroups).forEach((eventType) => { + switch (eventType) { + case EventType.IDENTIFY: + batchesOfIdentifyEvents = createIdentifyBatches(typedEventGroups[eventType]); + groupedSuccessfulPayload.identify.batches = formatIdentifyPayloadsWithEndpoint( + batchesOfIdentifyEvents, + 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + ); + break; + case EventType.GROUP: + groupedSuccessfulPayload.group.batches = createGroupBatches(typedEventGroups[eventType]); + break; + case EventType.TRACK: + groupedSuccessfulPayload.track.batches = createTrackBatches(typedEventGroups[eventType]); + break; + default: + break; + } + }); + + // Convert batches into requests for each event type and push to final output + appendRequestsToOutput(groupedSuccessfulPayload.identify, finalOutput, constants); + appendRequestsToOutput(groupedSuccessfulPayload.group, finalOutput, constants); + appendRequestsToOutput(groupedSuccessfulPayload.track, finalOutput, constants, false); + + return finalOutput; +} + +// Entry function to create batches from successful events +function batchResponseBuilder(successfulEvents) { + const constants = initializeConstants(successfulEvents); + if (!constants) return []; + + const typedEventGroups = lodash.groupBy( + successfulEvents, + (event) => event.message[0].body.JSON.eventType, + ); + + return processEventBatches(typedEventGroups, constants); +} + +module.exports = { + buildIdentifyPayload, + buildGroupPayload, + buildHeader, + deduceEndPoint, + batchResponseBuilder, + base64Sha, + getWsseHeader, + findRudderPropertyByEmersysProperty, + formatIdentifyPayloadsWithEndpoint, + createSingleIdentifyPayload, + createIdentifyBatches, + ensureSizeConstraints, + createGroupBatches, + deduceExternalIdValue, + deduceEventId, + deduceCustomIdentifier, +}; diff --git a/src/cdk/v2/destinations/emarsys/utils.test.js b/src/cdk/v2/destinations/emarsys/utils.test.js new file mode 100644 index 0000000000..3802567ecb --- /dev/null +++ b/src/cdk/v2/destinations/emarsys/utils.test.js @@ -0,0 +1,543 @@ +const { EVENT_TYPE } = require('rudder-transformer-cdk/build/constants'); +const { + buildIdentifyPayload, + buildGroupPayload, + base64Sha, + getWsseHeader, + findRudderPropertyByEmersysProperty, + createGroupBatches, + deduceEventId, +} = require('./utils'); +const { + checkIfEventIsAbortableAndExtractErrorMessage, +} = require('../../../../v1/destinations/emarsys/networkHandler'); +const crypto = require('crypto'); +const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); +const { responses } = require('../../../../../test/testHelper'); + +describe('Emarsys utils', () => { + describe('base64Sha', () => { + it('should return a base64 encoded SHA1 hash of the input string', () => { + const input = 'test'; + const expected = 'YTk0YThmZTVjY2IxOWJhNjFjNGMwODczZDM5MWU5ODc5ODJmYmJkMw=='; + const result = base64Sha(input); + expect(result).toEqual(expected); + }); + + it('should return an empty string when input is empty', () => { + const input = ''; + const expected = 'ZGEzOWEzZWU1ZTZiNGIwZDMyNTViZmVmOTU2MDE4OTBhZmQ4MDcwOQ=='; + const result = base64Sha(input); + expect(result).toEqual(expected); + }); + }); + + describe('getWsseHeader', () => { + beforeEach(() => { + jest + .spyOn(crypto, 'randomBytes') + .mockReturnValue(Buffer.from('abcdef1234567890abcdef1234567890', 'hex')); + jest.spyOn(Date.prototype, 'toISOString').mockReturnValue('2024-04-28T12:34:56.789Z'); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should generate a correct WSSE header', () => { + const user = 'testUser'; + const secret = 'testSecret'; + const expectedNonce = 'abcdef1234567890abcdef1234567890'; + const expectedTimestamp = '2024-04-28T12:34:56.789Z'; + const expectedDigest = base64Sha(expectedNonce + expectedTimestamp + secret); + const expectedHeader = `UsernameToken Username="${user}", PasswordDigest="${expectedDigest}", Nonce="${expectedNonce}", Created="${expectedTimestamp}"`; + const result = getWsseHeader(user, secret); + + expect(result).toBe(expectedHeader); + }); + }); + + describe('buildIdentifyPayload', () => { + it('should correctly build payload with field mapping', () => { + const message = { + type: 'identify', + traits: { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + optin: 1, + }, + }; + const destination = { + fieldMapping: [ + { rudderProperty: 'firstName', emersysProperty: '1' }, + { rudderProperty: 'lastName', emersysProperty: '2' }, + { rudderProperty: 'email', emersysProperty: '3' }, + { rudderProperty: 'optin', emersysProperty: '31' }, + ], + defaultContactList: 'dummyContactList', + }; + const expectedPayload = { + contact_list_id: 'dummyContactList', + contacts: [ + { + 1: 'John', + 2: 'Doe', + 3: 'john.doe@example.com', + 31: 1, + }, + ], + key_id: 3, + }; + + const result = buildIdentifyPayload(message, destination); + + expect(result.eventType).toBe(EVENT_TYPE.IDENTIFY); + expect(result.destinationPayload).toEqual(expectedPayload); + }); + + it('should throw error when opt-in field value is not allowed', () => { + const message = { + type: 'identify', + traits: { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + optin: 3, + }, + }; + const destination = { + fieldMapping: [ + { rudderProperty: 'firstName', emersysProperty: '1' }, + { rudderProperty: 'lastName', emersysProperty: '2' }, + { rudderProperty: 'email', emersysProperty: '3' }, + { rudderProperty: 'optin', emersysProperty: '31' }, + ], + defaultContactList: 'dummyList', + }; + expect(() => { + buildIdentifyPayload(message, destination); + }).toThrow('Only 1,2, values are allowed for optin field'); + }); + + it('should throw error when no contact list can be assigned field value is not allowed', () => { + const message = { + type: 'identify', + traits: { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + optin: 1, + }, + }; + const destination = { + fieldMapping: [ + { rudderProperty: 'firstName', emersysProperty: '1' }, + { rudderProperty: 'lastName', emersysProperty: '2' }, + { rudderProperty: 'email', emersysProperty: '3' }, + { rudderProperty: 'optin', emersysProperty: '31' }, + ], + }; + expect(() => { + buildIdentifyPayload(message, destination); + }).toThrow( + 'Cannot a find a specific contact list either through configuration or via integrations object', + ); + }); + + it('should correctly build payload with field mapping present in integrations object', () => { + const message = { + type: 'identify', + traits: { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + optin: 1, + }, + integrations: { + EMARSYS: { + customIdentifierId: 1, + contactListId: 'objectListId', + }, + }, + }; + const destination = { + fieldMapping: [ + { rudderProperty: 'firstName', emersysProperty: '1' }, + { rudderProperty: 'lastName', emersysProperty: '2' }, + { rudderProperty: 'email', emersysProperty: '3' }, + { rudderProperty: 'optin', emersysProperty: '31' }, + ], + defaultContactList: 'dummyContactList', + }; + const expectedPayload = { + contact_list_id: 'objectListId', + contacts: [ + { + 1: 'John', + 2: 'Doe', + 3: 'john.doe@example.com', + 31: 1, + }, + ], + key_id: 1, + }; + + const result = buildIdentifyPayload(message, destination); + + expect(result.eventType).toBe(EVENT_TYPE.IDENTIFY); + expect(result.destinationPayload).toEqual(expectedPayload); + }); + }); + + describe('buildGroupPayload', () => { + // Returns an object with eventType and destinationPayload keys when given valid message and destination inputs + it('should return an object with eventType and destinationPayload keys when given valid message and destination inputs with default externalId', () => { + const message = { + type: 'group', + groupId: 'group123', + context: { + traits: { + email: 'test@example.com', + }, + }, + }; + const destination = { + Config: { + emersysCustomIdentifier: '3', + defaultContactList: 'list123', + fieldMapping: [ + { emersysProperty: '100', rudderProperty: 'customId' }, + { emersysProperty: '3', rudderProperty: 'email' }, + ], + }, + }; + const result = buildGroupPayload(message, destination); + expect(result).toEqual({ + eventType: 'group', + destinationPayload: { + payload: { + key_id: 3, + external_ids: ['test@example.com'], + }, + contactListId: 'group123', + }, + }); + }); + + it('should return an object with eventType and destinationPayload keys when given valid message and destination inputs with configured externalId', () => { + const message = { + type: 'group', + groupId: 'group123', + context: { + traits: { + email: 'test@example.com', + customId: '123', + }, + }, + }; + const destination = { + emersysCustomIdentifier: '100', + defaultContactList: 'list123', + fieldMapping: [ + { emersysProperty: '100', rudderProperty: 'customId' }, + { emersysProperty: '3', rudderProperty: 'email' }, + ], + }; + const result = buildGroupPayload(message, destination); + expect(result).toEqual({ + eventType: 'group', + destinationPayload: { + payload: { + key_id: '100', + external_ids: ['123'], + }, + contactListId: 'group123', + }, + }); + }); + + it('should throw an InstrumentationError if emersysCustomIdentifier value is not present in payload', () => { + const message = { + type: 'group', + groupId: 'group123', + context: { + traits: { + email: 'test@example.com', + }, + }, + }; + const destination = { + emersysCustomIdentifier: 'customId', + defaultContactList: 'list123', + fieldMapping: [ + { emersysProperty: 'customId', rudderProperty: 'customId' }, + { emersysProperty: 'email', rudderProperty: 'email' }, + ], + }; + expect(() => { + buildGroupPayload(message, destination); + }).toThrow(InstrumentationError); + }); + }); + + describe('createGroupBatches', () => { + // Should group events by key_id and contactListId + it('should group events by key_id and contactListId when events are provided', () => { + // Arrange + const events = [ + { + message: [ + { + body: { + JSON: { + destinationPayload: { + payload: { + key_id: 'key1', + external_ids: ['id1', 'id2'], + }, + contactListId: 'list1', + }, + }, + }, + }, + ], + metadata: { jobId: 1, userId: 'u1' }, + }, + { + message: [ + { + body: { + JSON: { + destinationPayload: { + payload: { + key_id: 'key2', + external_ids: ['id3', 'id4'], + }, + contactListId: 'list2', + }, + }, + }, + }, + ], + metadata: { jobId: 2, userId: 'u2' }, + }, + { + message: [ + { + body: { + JSON: { + destinationPayload: { + payload: { + key_id: 'key1', + external_ids: ['id5', 'id6'], + }, + contactListId: 'list1', + }, + }, + }, + }, + ], + metadata: { jobId: 3, userId: 'u3' }, + }, + ]; + + // Act + const result = createGroupBatches(events); + + // Assert + expect(result).toEqual([ + { + endpoint: 'https://api.emarsys.net/api/v2/contactlist/list1/add', + payload: { + key_id: 'key1', + external_ids: ['id1', 'id2', 'id5', 'id6'], + }, + metadata: [ + { jobId: 1, userId: 'u1' }, + { jobId: 3, userId: 'u3' }, + ], + }, + { + endpoint: 'https://api.emarsys.net/api/v2/contactlist/list2/add', + payload: { + key_id: 'key2', + external_ids: ['id3', 'id4'], + }, + metadata: [{ jobId: 2, userId: 'u2' }], + }, + ]); + }); + + // Should return an empty array if no events are provided + it('should return an empty array when no events are provided', () => { + // Arrange + const events = []; + + // Act + const result = createGroupBatches(events); + + // Assert + expect(result).toEqual([]); + }); + }); + + describe('findRudderPropertyByEmersysProperty', () => { + // Returns the correct rudderProperty when given a valid emersysProperty and fieldMapping + it('should return the correct rudderProperty when given a valid emersysProperty and fieldMapping', () => { + const emersysProperty = 'firstName'; + const fieldMapping = [ + { emersysProperty: 'email', rudderProperty: 'email' }, + { emersysProperty: 'firstName', rudderProperty: 'firstName' }, + { emersysProperty: 'lastName', rudderProperty: 'lastName' }, + ]; + + const result = findRudderPropertyByEmersysProperty(emersysProperty, fieldMapping); + + expect(result).toBe('firstName'); + }); + + // Returns null when given an empty fieldMapping + it('should return null when given an empty fieldMapping', () => { + const emersysProperty = 'email'; + const fieldMapping = []; + + const result = findRudderPropertyByEmersysProperty(emersysProperty, fieldMapping); + + expect(result).toBe('email'); + }); + }); + + describe('checkIfEventIsAbortableAndExtractErrorMessage', () => { + // Returns {isAbortable: false, errorMsg: ''} if event is neither a string nor an object with keyId. + it('should return {isAbortable: false, errorMsg: ""} when event is neither a string nor an object with keyId', () => { + const event = 123; + const destinationResponse = { + response: { + data: { + errors: { + errorKey: { + errorCode: 'errorMessage', + }, + }, + }, + }, + }; + const keyId = 'keyId'; + + const result = checkIfEventIsAbortableAndExtractErrorMessage( + event, + destinationResponse, + keyId, + ); + + expect(result).toEqual({ isAbortable: false, errorMsg: '' }); + }); + + // Returns {isAbortable: false, errorMsg: ''} if errors object is empty. + it('should return {isAbortable: false, errorMsg: ""} when errors object is empty', () => { + const event = 'event'; + const destinationResponse = { + response: { + data: { + errors: {}, + }, + }, + }; + const keyId = 'keyId'; + + const result = checkIfEventIsAbortableAndExtractErrorMessage( + event, + destinationResponse, + keyId, + ); + + expect(result).toEqual({ isAbortable: false, errorMsg: '' }); + }); + + // Returns {isAbortable: true, errorMsg} if event is a string and has a corresponding error in the errors object. + it('should return {isAbortable: true, errorMsg} when event is a string and has a corresponding error in the errors object', () => { + const event = 'event'; + const destinationResponse = { + response: { + data: { + errors: { + event: { + errorCode: 'errorMessage', + }, + }, + }, + }, + }; + const keyId = 'keyId'; + + const result = checkIfEventIsAbortableAndExtractErrorMessage( + event, + destinationResponse, + keyId, + ); + + expect(result).toEqual({ isAbortable: true, errorMsg: '{"errorCode":"errorMessage"}' }); + }); + + // Returns {isAbortable: true, errorMsg} if event is an object with keyId and has a corresponding error in the errors object. + it('should return {isAbortable: true, errorMsg} when event is an object with keyId and has a corresponding error in the errors object', () => { + const event = { + keyId: 'event', + }; + const destinationResponse = { + response: { + data: { + errors: { + event: { + errorCode: 'errorMessage', + }, + }, + }, + }, + }; + const keyId = 'keyId'; + + const result = checkIfEventIsAbortableAndExtractErrorMessage( + event, + destinationResponse, + keyId, + ); + + expect(result).toEqual({ isAbortable: true, errorMsg: '{"errorCode":"errorMessage"}' }); + }); + }); + + describe('deduceEventId', () => { + // When a valid event name is provided and there is a mapping for it, the function should return the corresponding eventId. + it('should return the corresponding eventId when a valid event name is provided and there is a mapping for it', () => { + const message = { event: 'validEvent' }; + const destConfig = { eventsMapping: [{ from: 'validEvent', to: 'eventId' }] }; + const result = deduceEventId(message, destConfig); + expect(result).toBe('eventId'); + }); + + // When an invalid event name is provided, the function should throw a ConfigurationError. + it('should throw a ConfigurationError when an invalid event name is provided', () => { + const message = { event: 'invalidEvent' }; + const destConfig = { eventsMapping: [{ from: 'validEvent', to: 'eventId' }] }; + expect(() => deduceEventId(message, destConfig)).toThrow(ConfigurationError); + }); + + // When a valid event name is provided and there is no mapping for it, the function should throw a ConfigurationError. + it('should throw a ConfigurationError when a valid event name is provided and there is no mapping for it', () => { + const message = { event: 'validEvent' }; + const destConfig = { eventsMapping: [] }; + expect(() => deduceEventId(message, destConfig)).toThrow(ConfigurationError); + }); + + // When eventsMapping is not an array, the function should throw a TypeError. + it('should throw a TypeError when eventsMapping is not an array', () => { + const message = { event: 'validEvent' }; + const destConfig = { eventsMapping: 'notAnArray' }; + expect(() => deduceEventId(message, destConfig)).toThrow( + 'validEvent is not mapped to any Emersys external event. Aborting', + ); + }); + }); +}); diff --git a/src/constants/destinationCanonicalNames.js b/src/constants/destinationCanonicalNames.js index ee4f4f0b33..19136eab59 100644 --- a/src/constants/destinationCanonicalNames.js +++ b/src/constants/destinationCanonicalNames.js @@ -166,6 +166,7 @@ const DestCanonicalNames = { ], koala: ['Koala', 'koala', 'KOALA'], bloomreach: ['Bloomreach', 'bloomreach', 'BLOOMREACH'], + emarsys: ['EMARSYS', 'Emarsys', 'emarsys'], }; module.exports = { DestHandlerMap, DestCanonicalNames }; diff --git a/src/features.json b/src/features.json index 6d2cac9340..481025b700 100644 --- a/src/features.json +++ b/src/features.json @@ -70,7 +70,8 @@ "KOALA": true, "LINKEDIN_ADS": true, "BLOOMREACH": true, - "MOVABLE_INK": true + "MOVABLE_INK": true, + "EMARSYS": true }, "regulations": [ "BRAZE", diff --git a/src/services/destination/nativeIntegration.ts b/src/services/destination/nativeIntegration.ts index 0bc9308fcd..8fd0f09857 100644 --- a/src/services/destination/nativeIntegration.ts +++ b/src/services/destination/nativeIntegration.ts @@ -221,6 +221,7 @@ export class NativeIntegrationDestinationService implements DestinationService { destinationResponse: processedProxyResponse, rudderJobMetadata, destType: destinationType, + destinationRequest: deliveryRequest, }; let responseProxy = networkHandler.responseHandler(responseParams); // Adaption Logic for V0 to V1 diff --git a/src/v0/destinations/emarsys/deleteUsers.js b/src/v0/destinations/emarsys/deleteUsers.js new file mode 100644 index 0000000000..c6ca746217 --- /dev/null +++ b/src/v0/destinations/emarsys/deleteUsers.js @@ -0,0 +1,93 @@ +const { + NetworkError, + isDefinedAndNotNull, + ConfigurationAuthError, +} = require('@rudderstack/integrations-lib'); +const { httpPOST } = require('../../../adapters/network'); +const { + processAxiosResponse, + getDynamicErrorType, +} = require('../../../adapters/utils/networkUtils'); +const { isHttpStatusSuccess } = require('../../util'); +const { executeCommonValidations } = require('../../util/regulation-api'); +const tags = require('../../util/tags'); +const { getCustomIdBatches } = require('../../util/deleteUserUtils'); +const { + buildHeader, + deduceCustomIdentifier, + findRudderPropertyByEmersysProperty, +} = require('../../../cdk/v2/destinations/emarsys/utils'); + +/** + * This function will help to delete the users one by one from the userAttributes array. + * @param {*} userAttributes Array of objects with userId, email and phone + * @param {*} config Destination.Config provided in dashboard + * @returns + */ +const userDeletionHandler = async (userAttributes, config) => { + const endpoint = 'https://api.emarsys.net/api/v2/contact/delete'; + const headers = buildHeader(config); + const customIdentifier = deduceCustomIdentifier({}, config.emersysCustomIdentifier); + const configuredPayloadProperty = findRudderPropertyByEmersysProperty( + customIdentifier, + config.fieldMapping, + ); + if (!isDefinedAndNotNull(config.defaultContactList)) { + throw new ConfigurationAuthError('No audience list is configured. Aborting'); + } + /** + * identifierBatches = [[u1,u2,u3,...batchSize],[u1,u2,u3,...batchSize]...] + * Ref doc : https://dev.emarsys.com/docs/core-api-reference/szmq945esac90-delete-contacts + */ + const identifierBatches = getCustomIdBatches(userAttributes, configuredPayloadProperty, 1000); + // Note: we will only get 400 status code when no user deletion is present for given userIds so we will not throw error in that case + // eslint-disable-next-line no-restricted-syntax + for (const curBatch of identifierBatches) { + const deleteContactPayload = { + key_id: customIdentifier, + contact_list_id: config.defaultContactList, + }; + deleteContactPayload[`${customIdentifier}`] = curBatch; + // eslint-disable-next-line no-await-in-loop + const deletionResponse = await httpPOST( + endpoint, + { + ...deleteContactPayload, + }, + { + headers, + }, + { + destType: 'emarsys', + feature: 'deleteUsers', + endpointPath: '/contact/delete', + requestMethod: 'POST', + module: 'deletion', + }, + ); + const handledDelResponse = processAxiosResponse(deletionResponse); + if (!isHttpStatusSuccess(handledDelResponse.status) && handledDelResponse.status !== 400) { + throw new NetworkError( + 'User deletion request failed', + handledDelResponse.status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(handledDelResponse.status), + [tags.TAG_NAMES.STATUS]: handledDelResponse.status, + }, + handledDelResponse, + ); + } + } + + return { + statusCode: 200, + status: 'successful', + }; +}; +const processDeleteUsers = async (event) => { + const { userAttributes, config } = event; + executeCommonValidations(userAttributes); + const resp = await userDeletionHandler(userAttributes, config); + return resp; +}; +module.exports = { processDeleteUsers }; diff --git a/src/v0/util/deleteUserUtils.js b/src/v0/util/deleteUserUtils.js index 6cf16d7f9e..22b5ba6a81 100644 --- a/src/v0/util/deleteUserUtils.js +++ b/src/v0/util/deleteUserUtils.js @@ -18,4 +18,16 @@ const getUserIdBatches = (userAttributes, MAX_BATCH_SIZE) => { return userIdBatches; }; -module.exports = { getUserIdBatches }; +const getCustomIdBatches = (userAttributes, customIdentifier, MAX_BATCH_SIZE) => { + const identifierArray = []; + userAttributes.forEach((userAttribute) => { + // Dropping the user if customIdentifier is not present + if (userAttribute[customIdentifier]) { + identifierArray.push(userAttribute[customIdentifier]); + } + }); + const identifierBatches = lodash.chunk(identifierArray, MAX_BATCH_SIZE); + return identifierBatches; +}; + +module.exports = { getUserIdBatches, getCustomIdBatches }; diff --git a/src/v1/destinations/emarsys/networkHandler.js b/src/v1/destinations/emarsys/networkHandler.js new file mode 100644 index 0000000000..cef8013028 --- /dev/null +++ b/src/v1/destinations/emarsys/networkHandler.js @@ -0,0 +1,122 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +const { isObject } = require('@rudderstack/integrations-lib'); +const { TransformerProxyError } = require('../../../v0/util/errorTypes'); +const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); +const { isHttpStatusSuccess } = require('../../../v0/util/index'); + +const { + processAxiosResponse, + getDynamicErrorType, +} = require('../../../adapters/utils/networkUtils'); +const tags = require('../../../v0/util/tags'); + +// ref : https://dev.emarsys.com/docs/emarsys-core-api-guides/c47a64a8ea7dc-http-200-errors +function checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse, keyId) { + const { errors } = destinationResponse.response.data; + + // Determine if event is a string or an object, then fetch the corresponding key or value + let errorKey; + if (typeof event === 'string') { + errorKey = event; + } else if (typeof event === 'object' && event[keyId]) { + errorKey = event[keyId]; + } else { + return { isAbortable: false, errorMsg: '' }; // Early return if neither condition is met or if keyId is missing in the object + } + + // Check if this key has a corresponding error in the errors object + if (errors && isObject(errors) && errors[errorKey]) { + // const errorCode = Object.keys(errors[errorKey])[0]; // Assume there is at least one error code + const errorMsg = JSON.stringify(errors[errorKey]); + return { isAbortable: true, errorMsg }; + } + + // if '' is present in the error object, that means, it is a root level error, and none of the events are supposed to be successful + if (errors && isObject(errors) && errors['']) { + const errorMsg = JSON.stringify(errors['']); + return { isAbortable: true, errorMsg }; + } + + // Return false and an empty error message if no error is found + return { isAbortable: false, errorMsg: '' }; +} + +const responseHandler = (responseParams) => { + const { destinationResponse, rudderJobMetadata, destinationRequest } = responseParams; + const message = `[EMARSYS Response V1 Handler] - Request Processed Successfully`; + let responseWithIndividualEvents = []; + const { response, status } = destinationResponse; + + // ref : https://dev.emarsys.com/docs/emarsys-core-api-guides/5e68295991665-http-400-errors + if (!isHttpStatusSuccess(status)) { + const errorMessage = response.replyText || 'unknown error format'; + responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ + statusCode: status, + metadata, + error: errorMessage, + })); + throw new TransformerProxyError( + `EMARSYS: Error transformer proxy v1 during EMARSYS response transformation. ${errorMessage}`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); + } + + if (isHttpStatusSuccess(status)) { + // check for Partial Event failures and Successes + // eslint-disable-next-line @typescript-eslint/naming-convention + const { contacts, external_ids, key_id } = destinationRequest.body.JSON; + const finalData = contacts || external_ids; + finalData.forEach((event, idx) => { + const proxyOutput = { + statusCode: 200, + metadata: rudderJobMetadata[idx], + error: 'success', + }; + // update status of partial event if abortable + const { isAbortable, errorMsg } = checkIfEventIsAbortableAndExtractErrorMessage( + event, + destinationResponse, + key_id, + ); + if (isAbortable) { + proxyOutput.statusCode = 400; + proxyOutput.error = errorMsg; + } + responseWithIndividualEvents.push(proxyOutput); + }); + return { + status, + message, + destinationResponse, + response: responseWithIndividualEvents, + }; + } + + // ref : https://dev.emarsys.com/docs/emarsys-core-api-guides/45c776d275862-http-500-errors + + throw new TransformerProxyError( + `EMARSYS: Error transformer proxy v1 during EMARSYS response transformation`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); +}; + +function networkHandler() { + this.prepareProxy = prepareProxyRequest; + this.proxy = proxyRequest; + this.processAxiosResponse = processAxiosResponse; + this.responseHandler = responseHandler; +} + +module.exports = { networkHandler, checkIfEventIsAbortableAndExtractErrorMessage }; diff --git a/test/integrations/destinations/emarsys/dataDelivery/data.ts b/test/integrations/destinations/emarsys/dataDelivery/data.ts new file mode 100644 index 0000000000..ac3ec780f7 --- /dev/null +++ b/test/integrations/destinations/emarsys/dataDelivery/data.ts @@ -0,0 +1,560 @@ +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; +import { ProxyV1TestData } from '../../../testTypes'; + +export const headerBlockWithCorrectAccessToken = { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', +}; + +export const headerBlockWithWrongAccessToken = { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy2", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', +}; + +export const correctContactCreateUpdateData = [ + { + '2': 'Person0', + '3': 'person0@example.com', + '10569': 'efghi', + '10519': 'efghi', + '31': 1, + '39': 'abc', + }, + { + '2': true, + '3': 'abcde', + '10569': 'efgh', + '10519': 1234, + '31': 2, + '39': 'abc', + }, +]; + +export const wrongContactCreateUpdateData = [ + { + '2': 'Person0', + '3': 'person0@example.com', + '10569': 'efghi', + '10519': 'efghi', + '31': 1, + '39': 'abc', + }, + { + '2': true, + '3': 'person0@example.com', + '10569': 1234, + '10519': 'efgh', + '31': 2, + '39': 'abc', + }, +]; + +export const contactPayload = { + key_id: 10569, + contacts: correctContactCreateUpdateData, + contact_list_id: 'dummy', +}; + +export const correctGroupCallPayload = { + key_id: 'right_id', + external_ids: ['personABC@example.com'], +}; + +export const groupPayloadWithWrongKeyId = { + key_id: 'wrong_id', + external_ids: ['efghi', 'jklmn'], +}; + +export const groupPayloadWithWrongExternalId = { + key_id: 'right_id', + external_ids: ['efghi', 'jklmn', 'unknown', 'person4@example.com'], +}; + +export const correctContactWithWrongKeyIdCreateUpdateData = [ + { + '2': 'Person0', + '3': 'person0@example.com', + '10569': 'efghi', + '10519': 'efghi', + '31': 1, + '39': 'abc', + '100': 'abc', + }, + { + '2': true, + '3': 'abcde', + '10569': 'efgh', + '10519': 1234, + '31': 2, + '39': 'abc', + '100': 'abc', + }, +]; + +export const statTags = { + destType: 'EMARSYS', + errorCategory: 'network', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', +}; + +export const metadata = [ + { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: false, + }, + { + jobId: 2, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: false, + }, +]; + +const commonIdentifyRequestParametersWithWrongData = { + method: 'PUT', + headers: headerBlockWithCorrectAccessToken, + JSON: { ...contactPayload, contacts: wrongContactCreateUpdateData }, +}; + +const commonIdentifyRequestParameters = { + method: 'PUT', + headers: headerBlockWithCorrectAccessToken, + JSON: { ...contactPayload }, +}; + +const commonIdentifyRequestParametersWithWrongKeyId = { + method: 'PUT', + headers: headerBlockWithCorrectAccessToken, + JSON: { + ...contactPayload, + contacts: correctContactWithWrongKeyIdCreateUpdateData, + key_id: 100, + }, +}; + +const commonGroupRequestParametersWithWrongData = { + method: 'POST', + headers: headerBlockWithCorrectAccessToken, + JSON: groupPayloadWithWrongExternalId, +}; + +const commonGroupRequestParameters = { + method: 'POST', + headers: headerBlockWithCorrectAccessToken, + JSON: correctGroupCallPayload, +}; + +const commonGroupRequestParametersWithWrongKeyId = { + method: 'POST', + headers: headerBlockWithCorrectAccessToken, + JSON: groupPayloadWithWrongKeyId, +}; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'emarsys_v1_scenario_1', + name: 'emarsys', + description: 'Identify Event fails due to wrong key_id', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + ...commonIdentifyRequestParametersWithWrongKeyId, + }, + metadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[EMARSYS Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + status: 200, + data: { + ids: [], + errors: { + '': { + '2004': 'Invalid key field id: 100', + }, + }, + }, + }, + status: 200, + }, + response: [ + { + statusCode: 400, + metadata: generateMetadata(1), + error: '{"2004":"Invalid key field id: 100"}', + }, + { + statusCode: 400, + metadata: generateMetadata(2), + error: '{"2004":"Invalid key field id: 100"}', + }, + ], + }, + }, + }, + }, + }, + { + id: 'emarsys_v1_scenario_1', + name: 'emarsys', + description: 'correct Identify event passes with 200 status code', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: `https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1`, + ...commonIdentifyRequestParameters, + }, + metadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[EMARSYS Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + replyCode: 0, + replyText: 'OK', + data: { ids: ['138621551', 968984932] }, + }, + status: 200, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + { + statusCode: 200, + metadata: generateMetadata(2), + error: 'success', + }, + ], + }, + }, + }, + }, + }, + { + id: 'emarsys_v1_scenario_1', + name: 'emarsys', + description: 'Identify Event fails due to wrong data', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: `https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1`, + ...commonIdentifyRequestParametersWithWrongData, + }, + metadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[EMARSYS Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + status: 200, + data: { + ids: ['138621551'], + errors: { + '1234': { + '2010': 'Contacts with the external id already exist: 3', + }, + }, + }, + }, + status: 200, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + { + statusCode: 400, + metadata: generateMetadata(2), + error: '{"2010":"Contacts with the external id already exist: 3"}', + }, + ], + }, + }, + }, + }, + }, + { + id: 'emarsys_v1_scenario_1', + name: 'emarsys', + description: 'correct Group event passes with 200 status code', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: `https://api.emarsys.net/api/v2/contactlist/900337462/add`, + ...commonGroupRequestParameters, + }, + [generateMetadata(1)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[EMARSYS Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + replyCode: 0, + replyText: 'OK', + data: { errors: [], inserted_contacts: 1 }, + }, + status: 200, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + ], + }, + }, + }, + }, + }, + { + id: 'emarsys_v1_scenario_1', + name: 'emarsys', + description: 'Group Event fails due to wrong key_id', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: `https://api.emarsys.net/api/v2/contactlist/900337462/add`, + ...commonGroupRequestParametersWithWrongKeyId, + }, + metadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags, + message: + 'EMARSYS: Error transformer proxy v1 during EMARSYS response transformation. Invalid key field id: wrong_id', + response: [ + { + statusCode: 400, + metadata: generateMetadata(1), + error: '{"replyCode":2004,"replyText":"Invalid key field id: wrong_id","data":""}', + }, + { + statusCode: 400, + metadata: generateMetadata(2), + error: '{"replyCode":2004,"replyText":"Invalid key field id: wrong_id","data":""}', + }, + ], + }, + }, + }, + }, + }, + { + id: 'emarsys_v1_scenario_1', + name: 'emarsys', + description: 'Group Event fails due to wrong data', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: `https://api.emarsys.net/api/v2/contactlist/900337462/add`, + ...commonGroupRequestParametersWithWrongData, + }, + [generateMetadata(1), generateMetadata(2), generateMetadata(3), generateMetadata(4)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[EMARSYS Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + replyCode: 0, + replyText: 'OK', + data: { + inserted_contacts: 2, + errors: { + jklmn: { + '2008': 'No contact found with the external id: 3', + }, + unknown: { + '2008': 'No contact found with the external id: 3', + }, + }, + }, + }, + status: 200, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + { + statusCode: 400, + metadata: generateMetadata(2), + error: '{"2008":"No contact found with the external id: 3"}', + }, + { + statusCode: 400, + metadata: generateMetadata(3), + error: '{"2008":"No contact found with the external id: 3"}', + }, + { + statusCode: 200, + metadata: generateMetadata(4), + error: 'success', + }, + ], + }, + }, + }, + }, + }, + { + id: 'emarsys_v1_scenario_1', + name: 'emarsys', + description: 'Group Event fails due to wrong contact list id', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://api.emarsys.net/api/v2/contactlist/wrong-id/add', + ...commonGroupRequestParameters, + }, + [generateMetadata(1)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags, + message: + 'EMARSYS: Error transformer proxy v1 during EMARSYS response transformation. Action Wrong-id is invalid.', + response: [ + { + statusCode: 400, + metadata: generateMetadata(1), + error: '{"replyCode":1,"replyText":"Action Wrong-id is invalid.","data":""}', + }, + ], + }, + }, + }, + }, + }, +]; + +export const data = [...testScenariosForV1API]; diff --git a/test/integrations/destinations/emarsys/deleteUsers/data.ts b/test/integrations/destinations/emarsys/deleteUsers/data.ts new file mode 100644 index 0000000000..2bafe58a4c --- /dev/null +++ b/test/integrations/destinations/emarsys/deleteUsers/data.ts @@ -0,0 +1,235 @@ +export const mockFns = (_) => { + // @ts-ignore + jest.useFakeTimers().setSystemTime(new Date('2023-10-14')); + jest.mock('crypto', () => ({ + ...jest.requireActual('crypto'), + randomBytes: jest.fn().mockReturnValue(Buffer.from('5398e214ae99c2e50afb709a3bc423f9', 'hex')), + })); +}; + +const commonEventMap = [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, +]; + +const commonFieldMap = [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'lastName', + emersysProperty: '2', + }, + { + rudderProperty: 'firstName', + emersysProperty: '1', + }, + { + rudderProperty: 'custom-field', + emersysProperty: 'custom_id', + }, +]; + +export const data = [ + { + name: 'emarsys', + description: 'Missing emersysUsername key', + feature: 'userDeletion', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destType: 'EMARSYS', + userAttributes: [ + { + userId: '1234', + phone: '1234567890', + email: 'abc@xyc.com', + }, + ], + config: { + discardEmptyProperties: true, + emersysUsername: undefined, + emersysUserSecret: 'dummySecret', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: commonEventMap, + fieldMapping: commonFieldMap, + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + ], + }, + }, + output: { + response: { + status: 400, + body: [ + { + statusCode: 400, + error: 'Either Emarsys user name or user secret is missing. Aborting', + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Default contact list is not configured', + feature: 'userDeletion', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destType: 'EMARSYS', + userAttributes: [ + { + userId: '1234', + phone: '1234567890', + email: 'abc@xyc.com', + lastName: 'doe', + }, + ], + config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '2', + defaultContactList: undefined, + eventsMapping: commonEventMap, + fieldMapping: commonFieldMap, + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + ], + }, + }, + output: { + response: { + status: 400, + body: [ + { + statusCode: 400, + error: 'No audience list is configured. Aborting', + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'custom identifier is not present in user attribute', + feature: 'userDeletion', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destType: 'EMARSYS', + userAttributes: [ + { + userId: '1234', + phone: '1234567890', + lastName: 'doe', + }, + ], + config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: commonEventMap, + fieldMapping: commonFieldMap, + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 200, + status: 'successful', + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'user not present for deletion', + feature: 'userDeletion', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destType: 'EMARSYS', + userAttributes: [ + { + userId: '1234', + email: 'abc@gmail.com', + phone: '1234567890', + lastName: 'doe', + }, + ], + config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: commonEventMap, + fieldMapping: commonFieldMap, + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 200, + status: 'successful', + }, + ], + }, + }, + }, +].map((d) => ({ ...d, mockFns })); diff --git a/test/integrations/destinations/emarsys/network.ts b/test/integrations/destinations/emarsys/network.ts new file mode 100644 index 0000000000..c4954afd91 --- /dev/null +++ b/test/integrations/destinations/emarsys/network.ts @@ -0,0 +1,298 @@ +export const headerBlockWithCorrectAccessToken = { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', +}; + +export const headerBlockWithWrongAccessToken = { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy2", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', +}; + +export const correctContactCreateUpdateData = [ + { + '2': 'Person0', + '3': 'person0@example.com', + '10569': 'efghi', + '10519': 'efghi', + '31': 1, + '39': 'abc', + }, + { + '2': true, + '3': 'abcde', + '10569': 'efgh', + '10519': 1234, + '31': 2, + '39': 'abc', + }, +]; + +export const correctContactWithWrongKeyIdCreateUpdateData = [ + { + '2': 'Person0', + '3': 'person0@example.com', + '10569': 'efghi', + '10519': 'efghi', + '31': 1, + '39': 'abc', + '100': 'abc', + }, + { + '2': true, + '3': 'abcde', + '10569': 'efgh', + '10519': 1234, + '31': 2, + '39': 'abc', + '100': 'abc', + }, +]; + +export const wrongContactCreateUpdateData = [ + { + '2': 'Person0', + '3': 'person0@example.com', + '10569': 'efghi', + '10519': 'efghi', + '31': 1, + '39': 'abc', + }, + { + '2': true, + '3': 'person0@example.com', + '10569': 1234, + '10519': 'efgh', + '31': 2, + '39': 'abc', + }, +]; + +export const contactPayload = { + key_id: 10569, + contacts: correctContactCreateUpdateData, + contact_list_id: 'dummy', +}; + +export const correctGroupCallPayload = { + key_id: 'right_id', + external_ids: ['personABC@example.com'], +}; + +export const groupPayloadWithWrongKeyId = { + key_id: 'wrong_id', + external_ids: ['efghi', 'jklmn'], +}; + +export const groupPayloadWithWrongExternalId = { + key_id: 'right_id', + external_ids: ['efghi', 'jklmn', 'unknown', 'person4@example.com'], +}; + +export const comonHeader = { + Accept: 'application/json', + 'Content-Type': 'application/json', + + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="MjEzMDY5ZmI3NjMwNzE1N2M1ZTI5MWMzMzI3ODQxNDU2YWM4NTI3YQ==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2023-10-14T00:00:00.000Z"', +}; + +// MOCK DATA +const businessMockData = [ + { + description: 'Mock response from destination depicting request with a correct contact payload', + httpReq: { + method: 'PUT', + url: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: headerBlockWithCorrectAccessToken, + data: contactPayload, + }, + httpRes: { + data: { + replyCode: 0, + replyText: 'OK', + data: { ids: ['138621551', 968984932] }, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response from destination depicting request with a partially wrong contact payload', + httpReq: { + method: 'PUT', + url: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: headerBlockWithCorrectAccessToken, + data: { ...contactPayload, contacts: wrongContactCreateUpdateData }, + }, + httpRes: { + data: { + data: { + ids: ['138621551'], + errors: { '1234': { '2010': 'Contacts with the external id already exist: 3' } }, + }, + status: 200, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response from destination depicting request with a wrong key_id in payload', + httpReq: { + method: 'PUT', + url: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: headerBlockWithCorrectAccessToken, + data: { + ...contactPayload, + contacts: correctContactWithWrongKeyIdCreateUpdateData, + key_id: 100, + }, + }, + httpRes: { + data: { + data: { ids: [], errors: { '': { '2004': 'Invalid key field id: 100' } } }, + status: 200, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response from destination for correct group call ', + httpReq: { + method: 'POST', + url: 'https://api.emarsys.net/api/v2/contactlist/900337462/add', + headers: headerBlockWithCorrectAccessToken, + data: correctGroupCallPayload, + }, + httpRes: { + data: { replyCode: 0, replyText: 'OK', data: { inserted_contacts: 1, errors: [] } }, + status: 200, + }, + }, + { + description: 'Mock response from destination for group call with wrong key_id ', + httpReq: { + method: 'POST', + url: 'https://api.emarsys.net/api/v2/contactlist/900337462/add', + headers: headerBlockWithCorrectAccessToken, + data: groupPayloadWithWrongKeyId, + }, + httpRes: { + data: { replyCode: 2004, replyText: 'Invalid key field id: wrong_id', data: '' }, + status: 400, + }, + }, + { + description: 'Mock response from destination for group call with wrong data ', + httpReq: { + method: 'POST', + url: 'https://api.emarsys.net/api/v2/contactlist/900337462/add', + headers: headerBlockWithCorrectAccessToken, + data: groupPayloadWithWrongExternalId, + }, + httpRes: { + data: { + replyCode: 0, + replyText: 'OK', + data: { + inserted_contacts: 2, + errors: { + jklmn: { '2008': 'No contact found with the external id: 3' }, + unknown: { '2008': 'No contact found with the external id: 3' }, + }, + }, + }, + status: 200, + }, + }, + { + description: 'Mock response from destination for correct group call, with wrong contact list ', + httpReq: { + method: 'POST', + url: 'https://api.emarsys.net/api/v2/contactlist/wrong-id/add', + headers: headerBlockWithCorrectAccessToken, + data: correctGroupCallPayload, + }, + httpRes: { + data: { replyCode: 1, replyText: 'Action Wrong-id is invalid.', data: '' }, + status: 400, + }, + }, +]; + +const deleteNwData = [ + { + httpReq: { + method: 'post', + url: 'https://api.emarsys.net/api/v2/contact/delete', + data: { + key_id: 3, + contact_list_id: 'dummy', + 3: ['abc@gmail.com'], + }, + headers: comonHeader, + }, + httpRes: { + data: { + replyCode: 2008, + replyText: 'No contact found with the external id: 3 - abc@gmail.com', + data: '', + }, + status: 200, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.emarsys.net/api/v2/contact/delete', + data: { + userIds: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'], + }, + headers: comonHeader, + }, + httpRes: { + data: 'Your application has made too many requests in too short a time.', + status: 429, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.emarsys.net/api/v2/contact/delete', + data: { + userIds: ['9'], + }, + headers: comonHeader, + }, + httpRes: { + data: { + error: 'User deletion request failed', + }, + status: 400, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.emarsys.net/api/v2/contact/delete', + data: { + userIds: ['1', '2', '3'], + }, + headers: comonHeader, + }, + httpRes: { + data: { + requestId: 'request_1', + }, + status: 200, + }, + }, +]; + +export const networkCallsData = [...businessMockData, ...deleteNwData]; diff --git a/test/integrations/destinations/emarsys/processor/data.ts b/test/integrations/destinations/emarsys/processor/data.ts new file mode 100644 index 0000000000..fbeca6f4d8 --- /dev/null +++ b/test/integrations/destinations/emarsys/processor/data.ts @@ -0,0 +1,1380 @@ +export const mockFns = (_) => { + // @ts-ignore + jest.useFakeTimers().setSystemTime(new Date('2023-10-14')); + jest.mock('crypto', () => ({ + ...jest.requireActual('crypto'), + randomBytes: jest.fn().mockReturnValue(Buffer.from('5398e214ae99c2e50afb709a3bc423f9', 'hex')), + })); +}; + +const comonHeader = { + Accept: 'application/json', + 'Content-Type': 'application/json', + + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="MjEzMDY5ZmI3NjMwNzE1N2M1ZTI5MWMzMzI3ODQxNDU2YWM4NTI3YQ==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2023-10-14T00:00:00.000Z"', +}; + +export const data = [ + { + name: 'emarsys', + description: 'Test 1 : Track call custom identifier mapped from destination config ', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + event: 'Order Completed', + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + }, + }, + integrations: { + All: true, + EMARSYS: { + trigger_id: 'EVENT_TRIGGER_ID', + }, + }, + properties: { + company: 'testComp', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + }, + messageId: '2536eda4-d638-4c93-8014-8ffe3f083214', + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'track', + userId: 'userId06', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '3', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/event/purchase/trigger', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'track', + destinationPayload: { + payload: { + key_id: '3', + external_id: 'abc@gmail.com', + trigger_id: 'EVENT_TRIGGER_ID', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + event_time: '2023-07-06T11:59:02.402+05:30', + }, + eventId: 'purchase', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: + 'Test 2 : Track call custom identifier mapped from destination config with custom field', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + event: 'Order Completed', + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + custom_field: 'value', + }, + }, + integrations: { + All: true, + EMARSYS: { + trigger_id: 'EVENT_TRIGGER_ID', + customIdentifierId: 'custom_id', + }, + }, + properties: { + company: 'testComp', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + }, + messageId: '2536eda4-d638-4c93-8014-8ffe3f083214', + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'track', + userId: 'userId06', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '3', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'custom_field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/event/purchase/trigger', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'track', + destinationPayload: { + payload: { + key_id: 'custom_id', + external_id: 'value', + trigger_id: 'EVENT_TRIGGER_ID', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + event_time: '2023-07-06T11:59:02.402+05:30', + }, + eventId: 'purchase', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 3: Track call with trigger id mapped from integrations object', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + event: 'Order Completed', + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + custom_field: 'value', + }, + }, + integrations: { + All: true, + EMARSYS: { + trigger_id: 'EVENT_TRIGGER_ID', + }, + }, + properties: { + company: 'testComp', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + }, + messageId: '2536eda4-d638-4c93-8014-8ffe3f083214', + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'track', + userId: 'userId06', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'custom_field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/event/purchase/trigger', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'track', + destinationPayload: { + payload: { + key_id: 3, + external_id: 'abc@gmail.com', + trigger_id: 'EVENT_TRIGGER_ID', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + event_time: '2023-07-06T11:59:02.402+05:30', + }, + eventId: 'purchase', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 4 : group call with default external id email ', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + }, + }, + integrations: { + All: true, + }, + traits: { + company: 'testComp', + }, + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'group', + userId: 'userId06', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'custom_field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/contactlist/dummy/add', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'group', + destinationPayload: { + payload: { + key_id: 3, + external_ids: ['abc@gmail.com'], + }, + contactListId: 'dummy', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 5 : group call, custom identifier id mapped from integration object', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + custom_field: 'value', + }, + }, + integrations: { + All: true, + EMARSYS: { + customIdentifierId: 'custom_id', + }, + }, + traits: { + company: 'testComp', + }, + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'group', + userId: 'userId06', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'custom_field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/contactlist/dummy/add', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'group', + destinationPayload: { + payload: { + key_id: 'custom_id', + external_ids: ['value'], + }, + contactListId: 'dummy', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 6 : custom identifier mapped from destination config', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + custom_field: 'value', + }, + }, + integrations: { + All: true, + }, + traits: { + company: 'testComp', + }, + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'group', + userId: 'userId06', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '2', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'lastName', + emersysProperty: '2', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/contactlist/dummy/add', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'group', + destinationPayload: { + payload: { + key_id: '2', + external_ids: ['Doe'], + }, + contactListId: 'dummy', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 7 : Identify call with contact list id mapped from integrations objects', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + 'custom-field': 'value', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + integrations: { + All: true, + EMARSYS: { + contactListId: 123, + }, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '2', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'lastName', + emersysProperty: '2', + }, + { + rudderProperty: 'custom-field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'identify', + destinationPayload: { + key_id: '2', + contacts: [ + { + '2': 'one', + '3': 'testone@gmail.com', + custom_id: 'value', + }, + ], + contact_list_id: 123, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 8 : identify call customIdentifierId mapped from integration object', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + 'custom-field': 'value', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + userId: 'testuserId1', + integrations: { + All: true, + EMARSYS: { + customIdentifierId: '1', + }, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '2', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'lastName', + emersysProperty: '2', + }, + { + rudderProperty: 'firstName', + emersysProperty: '1', + }, + { + rudderProperty: 'custom-field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'identify', + destinationPayload: { + key_id: '1', + contacts: [ + { + '1': 'test', + '2': 'one', + '3': 'testone@gmail.com', + custom_id: 'value', + }, + ], + contact_list_id: 'dummy', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 9 : custom identifier mapped from default email value', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + 'custom-field': 'value', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + userId: 'testuserId1', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'lastName', + emersysProperty: '2', + }, + { + rudderProperty: 'firstName', + emersysProperty: '1', + }, + { + rudderProperty: 'custom-field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'identify', + destinationPayload: { + key_id: 3, + contacts: [ + { + '1': 'test', + '2': 'one', + '3': 'testone@gmail.com', + custom_id: 'value', + }, + ], + contact_list_id: 'dummy', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 10 : identify call error for not finding custom identifier', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + channel: 'web', + context: { + traits: { + firstName: 'test', + lastName: 'one', + 'custom-field': 'value', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + userId: 'testuserId1', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'lastName', + emersysProperty: '2', + }, + { + rudderProperty: 'firstName', + emersysProperty: '1', + }, + { + rudderProperty: 'custom-field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Either configured custom contact identifier value or default identifier email value is missing: Workflow: procWorkflow, Step: preparePayloadForIdentify, ChildStep: undefined, OriginalError: Either configured custom contact identifier value or default identifier email value is missing', + metadata: {}, + statTags: { + destType: 'EMARSYS', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, +].map((d) => ({ ...d, mockFns })); diff --git a/test/integrations/destinations/emarsys/router/data.ts b/test/integrations/destinations/emarsys/router/data.ts new file mode 100644 index 0000000000..8f449bd351 --- /dev/null +++ b/test/integrations/destinations/emarsys/router/data.ts @@ -0,0 +1,646 @@ +import crypto from 'crypto'; +const config = { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '3', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Product Added', + to: 'addToCart', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'firstName', + emersysProperty: '1', + }, + { + rudderProperty: 'lastName', + emersysProperty: '2', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], +}; + +const commonDestination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'emarsys', + DisplayName: 'Emarsys', + Config: { + cdkV2Enabled: true, + }, + }, + WorkspaceID: '123', + Transformations: [], + Config: config, + Enabled: true, +}; + +export const mockFns = (_) => { + // @ts-ignore + jest.useFakeTimers().setSystemTime(new Date('2019-10-14')); + jest.mock('crypto', () => ({ + ...jest.requireActual('crypto'), + randomBytes: jest.fn().mockReturnValue(Buffer.from('5398e214ae99c2e50afb709a3bc423f9', 'hex')), + })); +}; + +export const data = [ + { + id: 'emarsys-track-test-1', + name: 'emarsys', + description: 'combined batch', + scenario: 'Business', + successCriteria: + 'Identify, group events should be batched based on audience list and key_id criteria and track should not be batched ', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 1, + }, + destination: commonDestination, + }, + { + message: { + channel: 'web', + context: { + traits: { + email: 'testtwo@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 2, + }, + destination: commonDestination, + }, + { + message: { + channel: 'web', + context: { + traits: { + email: 'testtwo@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + integrations: { + All: true, + EMARSYS: { + contactListId: 'dummy2', + customIdentifierId: '1', + }, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 3, + }, + destination: commonDestination, + }, + { + message: { + channel: 'web', + context: { + traits: { + email: 'testtwo@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + integrations: { + All: true, + EMARSYS: { + contactListId: 'dummy2', + customIdentifierId: '2', + }, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 4, + }, + destination: commonDestination, + }, + { + message: { + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + }, + }, + integrations: { + All: true, + }, + traits: { + company: 'testComp', + }, + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'group', + userId: 'userId06', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 5, + }, + destination: commonDestination, + }, + { + message: { + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc2@gmail.com', + lastName: 'Doe2', + firstName: 'John2', + }, + }, + integrations: { + All: true, + }, + traits: { + company: 'testComp', + }, + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'group', + userId: 'userId06', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 6, + }, + destination: commonDestination, + }, + { + message: { + channel: 'web', + context: { + traits: { + firstName: 'test', + lastName: 'one', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 7, + }, + destination: commonDestination, + }, + { + message: { + event: 'Order Completed', + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + }, + }, + integrations: { + All: true, + }, + properties: { + company: 'testComp', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + }, + messageId: '2536eda4-d638-4c93-8014-8ffe3f083214', + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'track', + userId: 'userId06', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 8, + }, + destination: commonDestination, + }, + ], + destType: 'emarsys', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 7, + }, + ], + batched: false, + statusCode: 400, + error: + 'Either configured custom contact identifier value or default identifier email value is missing', + statTags: { + destType: 'EMARSYS', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'router', + implementation: 'cdkV2', + module: 'destination', + }, + destination: commonDestination, + }, + { + batchedRequest: { + body: { + JSON: { + key_id: '3', + contacts: [ + { + '1': 'test', + '2': 'one', + '3': 'testone@gmail.com', + }, + { + '1': 'test', + '2': 'one', + '3': 'testtwo@gmail.com', + }, + ], + contact_list_id: 'dummy', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'PUT', + endpoint: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', + }, + params: {}, + files: {}, + }, + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 1, + }, + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 2, + }, + ], + batched: true, + statusCode: 200, + destination: commonDestination, + }, + { + batchedRequest: { + body: { + JSON: { + key_id: '1', + contacts: [ + { + '1': 'test', + '2': 'one', + '3': 'testtwo@gmail.com', + }, + ], + contact_list_id: 'dummy2', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'PUT', + endpoint: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', + }, + params: {}, + files: {}, + }, + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 3, + }, + ], + batched: true, + statusCode: 200, + destination: commonDestination, + }, + { + batchedRequest: { + body: { + JSON: { + key_id: '2', + contacts: [ + { + '1': 'test', + '2': 'one', + '3': 'testtwo@gmail.com', + }, + ], + contact_list_id: 'dummy2', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'PUT', + endpoint: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', + }, + params: {}, + files: {}, + }, + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 4, + }, + ], + batched: true, + statusCode: 200, + destination: commonDestination, + }, + { + batchedRequest: { + body: { + JSON: { + key_id: '3', + external_ids: ['abc@gmail.com', 'abc2@gmail.com'], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/contactlist/dummy/add', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', + }, + params: {}, + files: {}, + }, + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 5, + }, + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 6, + }, + ], + batched: true, + statusCode: 200, + destination: commonDestination, + }, + { + batchedRequest: { + body: { + JSON: { + key_id: '3', + external_id: 'abc@gmail.com', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + event_time: '2023-07-06T11:59:02.402+05:30', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/event/purchase/trigger', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', + }, + params: {}, + files: {}, + }, + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 8, + }, + ], + batched: false, + statusCode: 200, + destination: commonDestination, + }, + ], + }, + }, + }, + }, +].map((d) => ({ ...d, mockFns })); From 8fe6bf0f1943ec712c531a55d857b4c14f26370f Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Mon, 13 May 2024 16:36:09 +0530 Subject: [PATCH 151/240] chore: edit changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4763c101ee..3cc10351e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. See [standa * add slack source ([9558e3b](https://github.com/rudderlabs/rudder-transformer/commit/9558e3bf848213bc75a58297d234d823b72a274a)) * add slack source ([#3148](https://github.com/rudderlabs/rudder-transformer/issues/3148)) ([3cbb011](https://github.com/rudderlabs/rudder-transformer/commit/3cbb011811b6f7b52ab29b4ef2794af9c2a5d5f0)) * onboard monday to proxy ([#3347](https://github.com/rudderlabs/rudder-transformer/issues/3347)) ([a347ab1](https://github.com/rudderlabs/rudder-transformer/commit/a347ab1f7fef9bdf7d797e258867b855acc00876)) +* onboard emarsys destination ([#3369](https://github.com/rudderlabs/rudder-transformer/issues/3369)) ([ff29f85](https://github.com/rudderlabs/rudder-transformer/commit/ff29f8578d2369fcd9f0d86e1b7dffe3146e6a3a)) ### Bug Fixes From bc7970c4f0a70e9fe8ad06ffd92f8f4b2a4ec910 Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Mon, 13 May 2024 18:04:23 +0530 Subject: [PATCH 152/240] fix: fixed some issue and added unit test --- src/cdk/v2/destinations/koddi/utils.js | 18 +- src/cdk/v2/destinations/koddi/utils.test.js | 423 ++++++++++++++++++++ src/features.json | 3 +- 3 files changed, 438 insertions(+), 6 deletions(-) create mode 100644 src/cdk/v2/destinations/koddi/utils.test.js diff --git a/src/cdk/v2/destinations/koddi/utils.js b/src/cdk/v2/destinations/koddi/utils.js index 6e8d5ba12b..f6521d1a66 100644 --- a/src/cdk/v2/destinations/koddi/utils.js +++ b/src/cdk/v2/destinations/koddi/utils.js @@ -1,6 +1,11 @@ const { InstrumentationError } = require('@rudderstack/integrations-lib'); const { EVENT_NAMES, IMPRESSIONS_CONFIG, CLICKS_CONFIG, CONVERSIONS_CONFIG } = require('./config'); -const { constructPayload, defaultRequestConfig, toUnixTimestamp } = require('../../../../v0/util'); +const { + constructPayload, + defaultRequestConfig, + toUnixTimestamp, + stripTrailingSlash, +} = require('../../../../v0/util'); const validateBidders = (bidders) => { if (!Array.isArray(bidders)) { @@ -55,13 +60,13 @@ const constructFullPayload = (eventName, message, Config) => { validateBidders(payload.bidders); break; default: - break; + throw new InstrumentationError(`event name ${eventName} is not supported.`); } return payload; }; const getEndpoint = (eventName, Config) => { - let endpoint = Config.apiBaseUrl; + let endpoint = stripTrailingSlash(Config.apiBaseUrl); switch (eventName) { case EVENT_NAMES.IMPRESSIONS: endpoint += '?action=impression'; @@ -73,7 +78,7 @@ const getEndpoint = (eventName, Config) => { endpoint += '/conversion'; break; default: - break; + throw new InstrumentationError(`event name ${eventName} is not supported.`); } return endpoint; }; @@ -86,6 +91,9 @@ const getEndpoint = (eventName, Config) => { * @returns */ const constructResponse = (eventName, Config, payload) => { + if (!Object.values(EVENT_NAMES).includes(eventName)) { + throw new InstrumentationError(`event name ${eventName} is not supported.`); + } const response = defaultRequestConfig(); response.endpoint = getEndpoint(eventName, Config); response.headers = { @@ -105,4 +113,4 @@ const constructResponse = (eventName, Config, payload) => { return response; }; -module.exports = { constructFullPayload, constructResponse }; +module.exports = { getEndpoint, validateBidders, constructFullPayload, constructResponse }; diff --git a/src/cdk/v2/destinations/koddi/utils.test.js b/src/cdk/v2/destinations/koddi/utils.test.js new file mode 100644 index 0000000000..e31bd6d9a7 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/utils.test.js @@ -0,0 +1,423 @@ +const { + getEndpoint, + validateBidders, + constructFullPayload, + constructResponse, +} = require('./utils'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); + +describe('getEndpoint', () => { + it('returns the correct endpoint for IMPRESSIONS event', () => { + const eventName = 'impressions'; + const Config = { + apiBaseUrl: 'https://www.test-client.com/', + clientName: 'test-client', + }; + const result = getEndpoint(eventName, Config); + expect(result).toEqual('https://www.test-client.com?action=impression'); + }); + + it('returns the correct endpoint for CLICKS event', () => { + const eventName = 'clicks'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const result = getEndpoint(eventName, Config); + expect(result).toEqual('https://www.test-client.com?action=click'); + }); + + it('returns the correct endpoint for IMPRESSIONS event', () => { + const eventName = 'conversions'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const result = getEndpoint(eventName, Config); + expect(result).toEqual('https://www.test-client.com/conversion'); + }); + + it('should throw error for unsupported event', () => { + const eventName = 'test'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + expect(() => getEndpoint(eventName, Config).toThrow(InstrumentationError)); + expect(() => getEndpoint(eventName, Config).toThrow('event name test is not supported.')); + }); +}); + +describe('validateBidders', () => { + it('should throw error if bidders is not an array', () => { + const bidders = {}; + expect(() => validateBidders(bidders)).toThrow(InstrumentationError); + expect(() => validateBidders(bidders)).toThrow( + 'properties.bidders should be an array of objects. Aborting.', + ); + }); + + it('should throw error if bidders is an empty array', () => { + const bidders = []; + expect(() => validateBidders(bidders)).toThrow(InstrumentationError); + expect(() => validateBidders(bidders)).toThrow( + 'properties.bidders should contains at least one bidder. Aborting.', + ); + }); + + it('should throw error if bidder or alternate_bidder is not present', () => { + const bidders = [ + { count: 1, base_price: 100 }, + { bidder: 'bidder1', count: 2, base_price: 200 }, + { alternate_bidder: 'alternate1', count: 3, base_price: 300 }, + ]; + expect(() => validateBidders(bidders)).toThrow(InstrumentationError); + expect(() => validateBidders(bidders)).toThrow( + 'bidder or alternate_bidder is not present. Aborting.', + ); + }); + + it('should throw error if count is not present', () => { + const bidders = [{ bidder: 'bidder1', alternate_bidder: 'alternate1', base_price: 100 }]; + expect(() => validateBidders(bidders)).toThrow(InstrumentationError); + expect(() => validateBidders(bidders)).toThrow('count is not present. Aborting.'); + }); + + it('should throw error if base_price is not present', () => { + const bidders = [ + { bidder: 'bidder1', alternate_bidder: 'alternate1', count: 1 }, // Missing base_price + ]; + expect(() => validateBidders(bidders)).toThrow(InstrumentationError); + expect(() => validateBidders(bidders)).toThrow('base_price is not present. Aborting.'); + }); + + it('should not throw error if all fields are present for all bidders', () => { + const bidders = [ + { bidder: 'bidder1', alternate_bidder: 'alternate1', count: 1, base_price: 100 }, + { bidder: 'bidder2', alternate_bidder: 'alternate2', count: 2, base_price: 200 }, + ]; + expect(() => validateBidders(bidders)).not.toThrow(); + }); +}); + +describe('constructFullPayload', () => { + it('should construct payload for IMPRESSIONS event', () => { + const eventName = 'impressions'; + const message = { + type: 'track', + event: 'Impressions Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + timestamp: '2024-03-03T00:29:12.117+05:30', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const expectedPayload = { + beaconIssued: '2024-03-04T15:32:56.409Z', + clientName: 'test-client', + rank: 1, + trackingData: 'dummy-tracking-data', + ts: '2024-03-03T00:29:12.117+05:30', + }; + const payload = constructFullPayload(eventName, message, Config); + expect(payload).toEqual(expectedPayload); + }); + it('should throw error if required value is missing for IMPRESSIONS event', () => { + const eventName = 'impressions'; + const message = { + type: 'track', + event: 'Impressions Event', + properties: { + tracking_data: '', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + timestamp: '2024-03-03T00:29:12.117+05:30', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + try { + const payload = constructFullPayload(eventName, message, Config); + } catch (error) { + expect(error.message).toEqual('Missing required value from "properties.tracking_data"'); + } + }); + + it('should construct payload for CLICKS event', () => { + const eventName = 'clicks'; + const message = { + type: 'track', + event: 'Clicks Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + anonymousId: '1234', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const expectedPayload = { + beaconIssued: '2024-03-04T15:32:56.409Z', + clientName: 'test-client', + rank: 1, + trackingData: 'dummy-tracking-data', + userGuid: '1234', + overrides: null, + testVersionOverride: null, + }; + const payload = constructFullPayload(eventName, message, Config); + expect(payload).toEqual(expectedPayload); + }); + it('should construct payload with non-null value if overrides and testVersionOverride are enable and values for these are provided for CLICKS event ', () => { + const eventName = 'clicks'; + const message = { + type: 'track', + event: 'Clicks Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + overrides: 'overridden-value', + testVersionOverride: 1, + }, + anonymousId: '1234', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + overrides: true, + testVersionOverride: false, + }; + const expectedPayload = { + beaconIssued: '2024-03-04T15:32:56.409Z', + clientName: 'test-client', + rank: 1, + trackingData: 'dummy-tracking-data', + userGuid: '1234', + overrides: 'overridden-value', + testVersionOverride: null, + }; + const payload = constructFullPayload(eventName, message, Config); + expect(payload).toEqual(expectedPayload); + }); + it('should throw error if required value is missing for CLICKS event', () => { + const eventName = 'clicks'; + const message = { + type: 'track', + event: 'Clicks Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + try { + const payload = constructFullPayload(eventName, message, Config); + } catch (error) { + expect(error.message).toEqual('Missing required value from "userId"'); + } + }); + + it('should construct payload for CONVERSIONS event', () => { + const eventName = 'conversions'; + const message = { + type: 'track', + event: 'Conversions Event', + properties: { + currency: 'USD', + order_id: '123', + bidders: [ + { + bidder: 'dummy-bidder-id', + count: 1, + base_price: 100.1, + }, + ], + }, + context: { + locale: 'en-US', + ip: '127.0.0.1', + }, + timestamp: '2024-03-03T00:29:12.117+05:30', + anonymousId: '1234', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const expectedPayload = { + client_name: 'test-client', + culture: 'en-US', + currency: 'USD', + transaction_id: '123', + unixtime: 1709405952, + userGuid: '1234', + user_ip: '127.0.0.1', + bidders: [ + { + bidder: 'dummy-bidder-id', + count: 1, + base_price: 100.1, + }, + ], + }; + const payload = constructFullPayload(eventName, message, Config); + expect(payload).toEqual(expectedPayload); + }); + it('should throw error if required value is missing for CONVERSIONS event', () => { + const eventName = 'conversions'; + const message = { + type: 'track', + event: 'Conversions Event', + properties: { + currency: 'USD', + order_id: '123', + bidders: [ + { + bidder: 'dummy-bidder-id', + count: 1, + base_price: 100.1, + }, + ], + }, + context: { + ip: '127.0.0.1', + }, + timestamp: '2024-03-03T00:29:12.117+05:30', + anonymousId: '1234', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + try { + const payload = constructFullPayload(eventName, message, Config); + } catch (error) { + expect(error.message).toEqual('Missing required value from "context.locale"'); + } + }); + + it('should throw error for unsupported event', () => { + const eventName = 'test'; + const message = {}; + const Config = {}; + expect(() => constructFullPayload(eventName, message, Config).toThrow(InstrumentationError)); + expect(() => + constructFullPayload(eventName, message, Config).toThrow('event name test is not supported.'), + ); + }); +}); + +describe('constructResponse', () => { + it('should construct response for IMPRESSIONS event', () => { + const eventName = 'impressions'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const payload = { + beaconIssued: '2024-03-04T15:32:56.409Z', + clientName: 'test-client', + rank: 1, + trackingData: 'dummy-tracking-data', + ts: '2024-03-03T00:29:12.117+05:30', + }; + const expectedResponse = { + endpoint: 'https://www.test-client.com?action=impression', + headers: { + accept: 'application/json', + }, + method: 'GET', + params: payload, + }; + const response = constructResponse(eventName, Config, payload); + expect(response).toMatchObject(expectedResponse); + }); + + it('should construct response for CLICKS event', () => { + const eventName = 'clicks'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const payload = { + beaconIssued: '2024-03-04T15:32:56.409Z', + clientName: 'test-client', + rank: 1, + trackingData: 'dummy-tracking-data', + userGuid: '1234', + }; + const expectedResponse = { + endpoint: 'https://www.test-client.com?action=click', + headers: { + accept: 'application/json', + }, + method: 'GET', + params: payload, + }; + const response = constructResponse(eventName, Config, payload); + expect(response).toMatchObject(expectedResponse); + }); + + it('should construct response for CONVERSIONS event', () => { + const eventName = 'conversions'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const payload = { + client_name: 'test-client', + culture: 'en-US', + currency: 'USD', + transaction_id: '123', + unixtime: 1709405952, + userGuid: '1234', + user_ip: '127.0.0.1', + bidders: [ + { + bidder: 'dummy-bidder-id', + count: 1, + base_price: 100.1, + }, + ], + }; + + const expectedResponse = { + endpoint: 'https://www.test-client.com/conversion', + headers: { + accept: 'application/json', + 'content-type': 'application/json', + }, + method: 'POST', + body: { + JSON: payload, + }, + }; + const response = constructResponse(eventName, Config, payload); + expect(response).toMatchObject(expectedResponse); + }); + + it('should throw error for unsupported event', () => { + const eventName = 'test'; + const Config = {}; + const payload = {}; + expect(() => constructResponse(eventName, Config, payload).toThrow(InstrumentationError)); + expect(() => + constructResponse(eventName, Config, payload).toThrow('event name test is not supported.'), + ); + }); +}); diff --git a/src/features.json b/src/features.json index 6d2cac9340..49b82e722a 100644 --- a/src/features.json +++ b/src/features.json @@ -70,7 +70,8 @@ "KOALA": true, "LINKEDIN_ADS": true, "BLOOMREACH": true, - "MOVABLE_INK": true + "MOVABLE_INK": true, + "KODDI": true }, "regulations": [ "BRAZE", From d4a82e2d2df7ee86c4b149bca7e0c12be3a6a545 Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Mon, 13 May 2024 18:39:09 +0530 Subject: [PATCH 153/240] fix: fixed unit test issue --- src/cdk/v2/destinations/koddi/utils.test.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cdk/v2/destinations/koddi/utils.test.js b/src/cdk/v2/destinations/koddi/utils.test.js index e31bd6d9a7..ed12ecd5dd 100644 --- a/src/cdk/v2/destinations/koddi/utils.test.js +++ b/src/cdk/v2/destinations/koddi/utils.test.js @@ -43,8 +43,8 @@ describe('getEndpoint', () => { apiBaseUrl: 'https://www.test-client.com', clientName: 'test-client', }; - expect(() => getEndpoint(eventName, Config).toThrow(InstrumentationError)); - expect(() => getEndpoint(eventName, Config).toThrow('event name test is not supported.')); + expect(() => getEndpoint(eventName, Config)).toThrow(InstrumentationError); + expect(() => getEndpoint(eventName, Config)).toThrow('event name test is not supported.'); }); }); @@ -91,7 +91,7 @@ describe('validateBidders', () => { expect(() => validateBidders(bidders)).toThrow('base_price is not present. Aborting.'); }); - it('should not throw error if all fields are present for all bidders', () => { + it('should not throw error if all required fields are present for all bidders', () => { const bidders = [ { bidder: 'bidder1', alternate_bidder: 'alternate1', count: 1, base_price: 100 }, { bidder: 'bidder2', alternate_bidder: 'alternate2', count: 2, base_price: 200 }, @@ -315,9 +315,9 @@ describe('constructFullPayload', () => { const eventName = 'test'; const message = {}; const Config = {}; - expect(() => constructFullPayload(eventName, message, Config).toThrow(InstrumentationError)); - expect(() => - constructFullPayload(eventName, message, Config).toThrow('event name test is not supported.'), + expect(() => constructFullPayload(eventName, message, Config)).toThrow(InstrumentationError); + expect(() => constructFullPayload(eventName, message, Config)).toThrow( + 'event name test is not supported.', ); }); }); @@ -415,9 +415,9 @@ describe('constructResponse', () => { const eventName = 'test'; const Config = {}; const payload = {}; - expect(() => constructResponse(eventName, Config, payload).toThrow(InstrumentationError)); - expect(() => - constructResponse(eventName, Config, payload).toThrow('event name test is not supported.'), + expect(() => constructResponse(eventName, Config, payload)).toThrow(InstrumentationError); + expect(() => constructResponse(eventName, Config, payload)).toThrow( + 'event name test is not supported.', ); }); }); From 533990203d0e2e122beaef5d1db88f60c27be494 Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Tue, 14 May 2024 12:23:04 +0530 Subject: [PATCH 154/240] chore: adding emarsys to regulations --- src/features.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/features.json b/src/features.json index 481025b700..a7e4b70109 100644 --- a/src/features.json +++ b/src/features.json @@ -85,7 +85,8 @@ "ENGAGE", "CUSTIFY", "SENDGRID", - "SPRIG" + "SPRIG", + "EMARSYS" ], "supportSourceTransformV1": true, "supportTransformerProxyV1": true From 3ada3b7ca33b53f782a93616658281ccc075a3e0 Mon Sep 17 00:00:00 2001 From: ItsSudip Date: Tue, 14 May 2024 15:08:10 +0530 Subject: [PATCH 155/240] chore: update changelog --- CHANGELOG.md | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cc10351e5..5db5ec047a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,21 +7,14 @@ All notable changes to this project will be documented in this file. See [standa ### Features -* add identify event and the latest slack event schema ([d0c5942](https://github.com/rudderlabs/rudder-transformer/commit/d0c5942e6255dab97b493fff311a4af71a142fd8)) -* add slack source ([9558e3b](https://github.com/rudderlabs/rudder-transformer/commit/9558e3bf848213bc75a58297d234d823b72a274a)) -* add slack source ([#3148](https://github.com/rudderlabs/rudder-transformer/issues/3148)) ([3cbb011](https://github.com/rudderlabs/rudder-transformer/commit/3cbb011811b6f7b52ab29b4ef2794af9c2a5d5f0)) -* onboard monday to proxy ([#3347](https://github.com/rudderlabs/rudder-transformer/issues/3347)) ([a347ab1](https://github.com/rudderlabs/rudder-transformer/commit/a347ab1f7fef9bdf7d797e258867b855acc00876)) -* onboard emarsys destination ([#3369](https://github.com/rudderlabs/rudder-transformer/issues/3369)) ([ff29f85](https://github.com/rudderlabs/rudder-transformer/commit/ff29f8578d2369fcd9f0d86e1b7dffe3146e6a3a)) +* add slack source ([#3148](https://github.com/rudderlabs/rudder-transformer/issues/3148)) +* onboard monday to proxy ([#3347](https://github.com/rudderlabs/rudder-transformer/issues/3347)) +* onboard emarsys destination ([#3369](https://github.com/rudderlabs/rudder-transformer/issues/3369)) ### Bug Fixes -* add originalTimestamp from slack ts property ([b19eb8c](https://github.com/rudderlabs/rudder-transformer/commit/b19eb8c699b60e90e02b39e3a448e6aac721e37f)) -* default event name normalization to undefined if it cannot be parsed ([db11c3e](https://github.com/rudderlabs/rudder-transformer/commit/db11c3e3d70d13a67d21c5b0043e74ce801bcb4a)) -* filtering url verification event via evvent type ([42d6010](https://github.com/rudderlabs/rudder-transformer/commit/42d6010d93b79ccb996c9548f4c0eeba0e3dacc2)) -* handle error-first ([0771e87](https://github.com/rudderlabs/rudder-transformer/commit/0771e87ff73181c412636f9bde40b8947a3e3080)) -* ninetailed: modify parameter requirements and add default values ([#3364](https://github.com/rudderlabs/rudder-transformer/issues/3364)) ([265a71d](https://github.com/rudderlabs/rudder-transformer/commit/265a71d20b484205245c20dd3e47d108dc2fd16f)) -* use optional chain parameter ([13be5cf](https://github.com/rudderlabs/rudder-transformer/commit/13be5cfb85363cf5c0527247787979467d63caaa)) +* ninetailed: modify parameter requirements and add default values ([#3364](https://github.com/rudderlabs/rudder-transformer/issues/3364)) ### [1.65.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.65.0...v1.65.1) (2024-05-10) From 189cf9367a907dc1848257733e13713245458579 Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Tue, 14 May 2024 20:40:04 +0530 Subject: [PATCH 156/240] fix: added componenet test --- src/cdk/v2/destinations/koddi/rtWorkflow.yaml | 16 +- .../integrations/destinations/koddi/common.ts | 78 ++++++ .../destinations/koddi/processor/clicks.ts | 70 ++++++ .../koddi/processor/conversions.ts | 142 +++++++++++ .../destinations/koddi/processor/data.ts | 5 + .../koddi/processor/impressions.ts | 70 ++++++ .../destinations/koddi/router/data.ts | 232 ++++++++++++++++++ 7 files changed, 603 insertions(+), 10 deletions(-) create mode 100644 test/integrations/destinations/koddi/common.ts create mode 100644 test/integrations/destinations/koddi/processor/clicks.ts create mode 100644 test/integrations/destinations/koddi/processor/conversions.ts create mode 100644 test/integrations/destinations/koddi/processor/data.ts create mode 100644 test/integrations/destinations/koddi/processor/impressions.ts create mode 100644 test/integrations/destinations/koddi/router/data.ts diff --git a/src/cdk/v2/destinations/koddi/rtWorkflow.yaml b/src/cdk/v2/destinations/koddi/rtWorkflow.yaml index 30dd3fdd95..dd438a911c 100644 --- a/src/cdk/v2/destinations/koddi/rtWorkflow.yaml +++ b/src/cdk/v2/destinations/koddi/rtWorkflow.yaml @@ -1,8 +1,7 @@ bindings: - - path: ./config - name: handleRtTfSingleEventError path: ../../../../v0/util/index - - path: ./utils + steps: - name: validateInput template: | @@ -16,20 +15,17 @@ steps: - name: successfulEvents template: | $.outputs.transform#idx.output.({ - "output": .body.JSON.events[0], + "batchedRequest": ., + "batched": false, "destination": ^[idx].destination, - "metadata": ^[idx].metadata + "metadata": ^[idx].metadata[], + "statusCode": 200 })[] - name: failedEvents template: | $.outputs.transform#idx.error.( $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) )[] - - name: batchSuccessfulEvents - description: Batches the successfulEvents - template: | - $.batchResponseBuilder($.outputs.successfulEvents); - - name: finalPayload template: | - [...$.outputs.failedEvents, ...$.outputs.batchSuccessfulEvents] + [...$.outputs.successfulEvents, ...$.outputs.failedEvents] diff --git a/test/integrations/destinations/koddi/common.ts b/test/integrations/destinations/koddi/common.ts new file mode 100644 index 0000000000..ec83a0f446 --- /dev/null +++ b/test/integrations/destinations/koddi/common.ts @@ -0,0 +1,78 @@ +import { Destination } from '../../../../src/types'; + +const destType = 'koddi'; +const destTypeInUpperCase = 'KODDI'; +const displayName = 'Koddi'; +const channel = 'web'; +const destination: Destination = { + Config: { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }, + DestinationDefinition: { + DisplayName: displayName, + ID: '123', + Name: destTypeInUpperCase, + Config: { cdkV2Enabled: true }, + }, + Enabled: true, + ID: '123', + Name: destTypeInUpperCase, + Transformations: [], + WorkspaceID: 'test-workspace-id', +}; + +const processorInstrumentationErrorStatTags = { + destType: destTypeInUpperCase, + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +const RouterInstrumentationErrorStatTags = { + ...processorInstrumentationErrorStatTags, + feature: 'router', +}; + +const getHeader = { + accept: 'application/json', +}; + +const postHeader = { + ...getHeader, + 'content-type': 'application/json', +}; + +const bidders = [ + { + bidder: 'bidder1', + alternate_bidder: 'alternate1', + count: 1, + base_price: 100, + total_price: 227, + }, +]; + +const alternateBidders = [ + { + count: 1, + base_price: 100, + total_price: 227, + }, +]; + +export { + destType, + channel, + destination, + processorInstrumentationErrorStatTags, + RouterInstrumentationErrorStatTags, + getHeader, + postHeader, + bidders, + alternateBidders, +}; diff --git a/test/integrations/destinations/koddi/processor/clicks.ts b/test/integrations/destinations/koddi/processor/clicks.ts new file mode 100644 index 0000000000..6270c0468d --- /dev/null +++ b/test/integrations/destinations/koddi/processor/clicks.ts @@ -0,0 +1,70 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, channel, destination, getHeader } from '../common'; + +export const clicks: ProcessorTestData[] = [ + { + id: 'Clicks-test', + name: destType, + description: 'Clicks call: Example Clicks Event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Clicks Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventName: 'Clicks', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'GET', + endpoint: destination.Config.apiBaseUrl + '?action=click', + headers: getHeader, + userId: '', + params: { + trackingData: 'dummy-tracking-data', + rank: 1, + beaconIssued: '2024-03-04T15:32:56.409Z', + userGuid: 'userId123', + clientName: destination.Config.clientName, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/koddi/processor/conversions.ts b/test/integrations/destinations/koddi/processor/conversions.ts new file mode 100644 index 0000000000..35d1670b95 --- /dev/null +++ b/test/integrations/destinations/koddi/processor/conversions.ts @@ -0,0 +1,142 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { + destType, + channel, + destination, + postHeader, + bidders, + alternateBidders, + processorInstrumentationErrorStatTags, +} from '../common'; + +export const conversions: ProcessorTestData[] = [ + { + id: 'Conversions-test-1', + name: destType, + description: 'Conversions call: Example Conversions Event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Conversions Event', + properties: { + currency: 'USD', + transaction_id: 'ABC123', + bidders, + }, + context: { + locale: 'en-US', + ip: '127.0.0.1', + }, + integrations: { + All: true, + koddi: { + eventName: 'Conversions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: destination.Config.apiBaseUrl + '/conversion', + headers: postHeader, + userId: '', + JSON: { + client_name: destination.Config.clientName, + culture: 'en-US', + currency: 'USD', + transaction_id: 'ABC123', + unixtime: 1709566376, + userGuid: 'userId123', + user_ip: '127.0.0.1', + bidders: bidders, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'Conversions-test-2', + name: destType, + description: 'Conversions call: Example Conversions Event with missing required field', + scenario: 'Framework+Business', + successCriteria: 'Response should contain error and status code should be 400', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Conversions Event', + properties: { + currency: 'USD', + transaction_id: 'ABC123', + alternateBidders, + }, + context: { + locale: 'en-US', + ip: '127.0.0.1', + }, + integrations: { + All: true, + koddi: { + eventName: 'Conversions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Missing required value from "properties.bidders": Workflow: procWorkflow, Step: preparePayload, ChildStep: undefined, OriginalError: Missing required value from "properties.bidders"', + statusCode: 400, + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/koddi/processor/data.ts b/test/integrations/destinations/koddi/processor/data.ts new file mode 100644 index 0000000000..5c3d7da472 --- /dev/null +++ b/test/integrations/destinations/koddi/processor/data.ts @@ -0,0 +1,5 @@ +import { impressions } from './impressions'; +import { clicks } from './clicks'; +import { conversions } from './conversions'; + +export const data = [...impressions, ...clicks, ...conversions]; diff --git a/test/integrations/destinations/koddi/processor/impressions.ts b/test/integrations/destinations/koddi/processor/impressions.ts new file mode 100644 index 0000000000..35ffcad22b --- /dev/null +++ b/test/integrations/destinations/koddi/processor/impressions.ts @@ -0,0 +1,70 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, channel, destination, getHeader } from '../common'; + +export const impressions: ProcessorTestData[] = [ + { + id: 'Impressions-test', + name: destType, + description: 'Impressions call: Example Impression Event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Impression Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventName: 'Impressions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'GET', + endpoint: destination.Config.apiBaseUrl + '?action=impression', + headers: getHeader, + userId: '', + params: { + trackingData: 'dummy-tracking-data', + rank: 1, + beaconIssued: '2024-03-04T15:32:56.409Z', + ts: '2024-03-04T15:32:56.409Z', + clientName: destination.Config.clientName, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/koddi/router/data.ts b/test/integrations/destinations/koddi/router/data.ts new file mode 100644 index 0000000000..f5fae9bfd4 --- /dev/null +++ b/test/integrations/destinations/koddi/router/data.ts @@ -0,0 +1,232 @@ +import { RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; +import { + destType, + channel, + destination, + getHeader, + postHeader, + RouterInstrumentationErrorStatTags, + bidders, +} from '../common'; + +const routerRequest: RouterTransformationRequest = { + input: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Impression Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventName: 'Impressions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Clicks Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventName: 'Clicks', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(2), + }, + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Conversions Event', + properties: { + currency: 'USD', + transaction_id: 'ABC123', + bidders, + }, + context: { + locale: 'en-US', + ip: '127.0.0.1', + }, + integrations: { + All: true, + koddi: { + eventName: 'Conversions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(3), + }, + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Impression Event', + properties: { + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventName: 'Impressions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(4), + }, + ], + destType, +}; + +export const data = [ + { + id: 'koddi-router-test', + name: destType, + description: 'Basic Router Test to test payloads and missing field error', + scenario: 'Framework', + successCriteria: + 'Some events should be transformed successfully and some should fail for missing fields and status code should be 200', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: routerRequest, + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batched: false, + batchedRequest: { + version: '1', + type: 'REST', + method: 'GET', + endpoint: destination.Config.apiBaseUrl + '?action=impression', + headers: getHeader, + params: { + trackingData: 'dummy-tracking-data', + rank: 1, + beaconIssued: '2024-03-04T15:32:56.409Z', + ts: '2024-03-04T15:32:56.409Z', + clientName: destination.Config.clientName, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + destination, + metadata: [generateMetadata(1)], + statusCode: 200, + }, + { + batched: false, + batchedRequest: { + version: '1', + type: 'REST', + method: 'GET', + endpoint: destination.Config.apiBaseUrl + '?action=click', + headers: getHeader, + params: { + trackingData: 'dummy-tracking-data', + rank: 1, + beaconIssued: '2024-03-04T15:32:56.409Z', + userGuid: 'userId123', + clientName: destination.Config.clientName, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + destination, + metadata: [generateMetadata(2)], + statusCode: 200, + }, + { + batched: false, + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: destination.Config.apiBaseUrl + '/conversion', + headers: postHeader, + params: {}, + body: { + JSON: { + client_name: 'test-client', + culture: 'en-US', + currency: 'USD', + transaction_id: 'ABC123', + unixtime: 1709566376, + userGuid: 'userId123', + user_ip: '127.0.0.1', + bidders: bidders, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + destination, + metadata: [generateMetadata(3)], + statusCode: 200, + }, + { + batched: false, + error: 'Missing required value from "properties.tracking_data"', + destination, + metadata: [generateMetadata(4)], + statTags: RouterInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + }, +]; From fce4ef973500411c7ad812e7949bb1b73dabc3ba Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Wed, 15 May 2024 12:04:47 +0530 Subject: [PATCH 157/240] feat: facebook custom audience app secret support (#3357) * feat: facebook custom audience app secret support * fix: review comment addressed * fix: add test case description --- .../fb_custom_audience/transform.js | 9 +- .../destinations/fb_custom_audience/util.js | 22 ++- .../fb_custom_audience/processor/data.ts | 152 +++++++++++++++++- 3 files changed, 180 insertions(+), 3 deletions(-) diff --git a/src/v0/destinations/fb_custom_audience/transform.js b/src/v0/destinations/fb_custom_audience/transform.js index 9320a3476b..dfe9a04618 100644 --- a/src/v0/destinations/fb_custom_audience/transform.js +++ b/src/v0/destinations/fb_custom_audience/transform.js @@ -20,6 +20,7 @@ const { prepareDataField, getSchemaForEventMappedToDest, batchingWithPayloadSize, + generateAppSecretProof, } = require('./util'); const { getEndPoint, @@ -88,7 +89,7 @@ const prepareResponse = ( userSchema, isHashRequired = true, ) => { - const { accessToken, disableFormat, type, subType, isRaw } = destination.Config; + const { accessToken, disableFormat, type, subType, isRaw, appSecret } = destination.Config; const mappedToDestination = get(message, MappedToDestinationKey); @@ -105,6 +106,12 @@ const prepareResponse = ( prepareParams.access_token = accessToken; + if (isDefinedAndNotNullAndNotEmpty(appSecret)) { + const dateNow = Date.now(); + prepareParams.appsecret_time = Math.floor(dateNow / 1000); // Get current Unix time in seconds + prepareParams.appsecret_proof = generateAppSecretProof(accessToken, appSecret, dateNow); + } + // creating the payload field for parameters if (isRaw) { paramsPayload.is_raw = isRaw; diff --git a/src/v0/destinations/fb_custom_audience/util.js b/src/v0/destinations/fb_custom_audience/util.js index 1f096215f3..9385dfbd36 100644 --- a/src/v0/destinations/fb_custom_audience/util.js +++ b/src/v0/destinations/fb_custom_audience/util.js @@ -1,5 +1,6 @@ const lodash = require('lodash'); const sha256 = require('sha256'); +const crypto = require('crypto'); const get = require('get-value'); const jsonSize = require('json-size'); const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); @@ -206,4 +207,23 @@ const prepareDataField = ( return data; }; -module.exports = { prepareDataField, getSchemaForEventMappedToDest, batchingWithPayloadSize }; +// ref: https://developers.facebook.com/docs/facebook-login/security/#generate-the-proof + +const generateAppSecretProof = (accessToken, appSecret, dateNow) => { + const currentTime = Math.floor(dateNow / 1000); // Get current Unix time in seconds + const data = `${accessToken}|${currentTime}`; + + // Creating a HMAC SHA-256 hash with the app_secret as the key + const hmac = crypto.createHmac('sha256', appSecret); + hmac.update(data); + const appsecretProof = hmac.digest('hex'); + + return appsecretProof; +}; + +module.exports = { + prepareDataField, + getSchemaForEventMappedToDest, + batchingWithPayloadSize, + generateAppSecretProof, +}; diff --git a/test/integrations/destinations/fb_custom_audience/processor/data.ts b/test/integrations/destinations/fb_custom_audience/processor/data.ts index 602f8a7fb6..75fa321aca 100644 --- a/test/integrations/destinations/fb_custom_audience/processor/data.ts +++ b/test/integrations/destinations/fb_custom_audience/processor/data.ts @@ -1,5 +1,10 @@ import { getEndPoint } from '../../../../../src/v0/destinations/fb_custom_audience/config'; +export const mockFns = (_) => { + // @ts-ignore + jest.useFakeTimers().setSystemTime(new Date('2023-10-15')); +}; + export const data = [ { name: 'fb_custom_audience', @@ -54463,4 +54468,149 @@ export const data = [ }, }, }, -]; + { + name: 'fb_custom_audience', + description: + 'If App secret is configured in the UI, appsecret_proof and appsecret_time will be added to destination request.', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + userId: 'user 1', + anonymousId: 'anon-id-new', + event: 'event1', + type: 'audiencelist', + properties: { + listData: { + add: [ + { + EMAIL: 'shrouti2@abc.com', + DOBM: '2', + DOBD: '13', + DOBY: '2013', + PHONE: '@09432457768', + GEN: 'f', + FI: 'Ms.', + MADID: 'ABC', + ZIP: 'ZIP ', + ST: '123abc ', + COUNTRY: 'IN', + }, + ], + }, + }, + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + timestamp: '2020-02-02T00:23:09.544Z', + }, + destination: { + Config: { + accessToken: 'ABC', + appSecret: 'dummySecret', + userSchema: [ + 'EMAIL', + 'DOBM', + 'DOBD', + 'DOBY', + 'PHONE', + 'GEN', + 'FI', + 'MADID', + 'ZIP', + 'ST', + 'COUNTRY', + ], + isHashRequired: false, + disableFormat: false, + audienceId: 'aud1', + isRaw: true, + type: 'NA', + subType: 'ANYTHING', + maxUserCount: '50', + }, + Enabled: true, + Transformations: [], + IsProcessorEnabled: true, + }, + libraries: [], + request: { + query: {}, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: getEndPoint('aud1'), + headers: {}, + params: { + access_token: 'ABC', + appsecret_proof: 'd103874f3b5f01f57c4f84edfb96ac94055da8f83c2b45e6f26dafca9188ff4d', + appsecret_time: 1697328000, + payload: { + is_raw: true, + data_source: { + sub_type: 'ANYTHING', + }, + schema: [ + 'EMAIL', + 'DOBM', + 'DOBD', + 'DOBY', + 'PHONE', + 'GEN', + 'FI', + 'MADID', + 'ZIP', + 'ST', + 'COUNTRY', + ], + data: [ + [ + 'shrouti2@abc.com', + '2', + '13', + '2013', + '@09432457768', + 'f', + 'Ms.', + 'ABC', + 'ZIP ', + '123abc ', + 'IN', + ], + ], + }, + }, + userId: '', + body: { + JSON: {}, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + }, + statusCode: 200, + }, + ], + }, + }, + }, +].map((d) => ({ ...d, mockFns })); From 3ff9a5e8e955b929a1b04a89dcf0ccbc49e18648 Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Wed, 15 May 2024 16:47:49 +0530 Subject: [PATCH 158/240] feat(ga4): component test refactor (#3220) Co-authored-by: Sai Sankeerth --- test/integrations/destinations/ga4/mocks.ts | 4 +- .../destinations/ga4/processor/data.ts | 15221 +--------------- .../ga4/processor/ecomTestData.ts | 2466 +++ .../ga4/processor/groupTestData.ts | 306 + .../ga4/processor/pageTestData.ts | 304 + .../ga4/processor/trackTestData.ts | 2951 +++ .../ga4/processor/validationTestData.ts | 947 + test/integrations/testTypes.ts | 1 + test/integrations/testUtils.ts | 5 +- 9 files changed, 6993 insertions(+), 15212 deletions(-) create mode 100644 test/integrations/destinations/ga4/processor/ecomTestData.ts create mode 100644 test/integrations/destinations/ga4/processor/groupTestData.ts create mode 100644 test/integrations/destinations/ga4/processor/pageTestData.ts create mode 100644 test/integrations/destinations/ga4/processor/trackTestData.ts create mode 100644 test/integrations/destinations/ga4/processor/validationTestData.ts diff --git a/test/integrations/destinations/ga4/mocks.ts b/test/integrations/destinations/ga4/mocks.ts index 7b47fe9f44..3a27349ff7 100644 --- a/test/integrations/destinations/ga4/mocks.ts +++ b/test/integrations/destinations/ga4/mocks.ts @@ -1,3 +1,5 @@ export const defaultMockFns = () => { - jest.spyOn(Date, 'now').mockImplementation(() => new Date('2022-04-29T05:17:09Z').valueOf()); + return jest + .spyOn(Date, 'now') + .mockImplementation(() => new Date('2022-04-29T05:17:09Z').valueOf()); }; diff --git a/test/integrations/destinations/ga4/processor/data.ts b/test/integrations/destinations/ga4/processor/data.ts index 4465ec9e2c..ba5b53c7d2 100644 --- a/test/integrations/destinations/ga4/processor/data.ts +++ b/test/integrations/destinations/ga4/processor/data.ts @@ -1,15212 +1,13 @@ -import { defaultMockFns } from '../mocks'; +import { pageTestData } from './pageTestData'; +import { ecommTestData } from './ecomTestData'; +import { trackTestData } from './trackTestData'; +import { groupTestData } from './groupTestData'; +import { validationTestData } from './validationTestData'; + export const data = [ - { - name: 'ga4', - description: "(gtag) check all property mappings for 'Products Searched' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99v4f979fb997ce453373900f891', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'Products Searched', - properties: { - query: 't-shirts', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-22T10:57:58Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - timestamp_micros: 1650950229000000, - events: [ - { - name: 'search', - params: { - search_term: 't-shirts', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product list viewed' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - rudderId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99v4f979fb997ce453373900f891', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product list viewed', - properties: { - list_id: 'related_products', - category: 'Related_products', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_item_list', - params: { - item_list_id: 'related_products', - item_list_name: 'Related_products', - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'promotion viewed' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'promotion viewed', - properties: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '0', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'client_id', - events: [ - { - name: 'view_promotion', - params: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 0, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'promotion clicked' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'promotion clicked', - properties: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '0', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'select_promotion', - params: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 0, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) exclude only 'products' property from 'promotion clicked' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'promotion clicked', - properties: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'select_promotion', - params: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product clicked' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product clicked', - properties: { - list_id: 'related_products', - category: 'Related_products', - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - timezone: { - name: 'Europe/Tallinn', - }, - engagementTimeMsec: 100, - sessionId: 655, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'select_item', - params: { - item_list_id: 'related_products', - item_list_name: 'Related_products', - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Related_products', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - timezone_name: 'Europe/Tallinn', - engagement_time_msec: 100, - session_id: 655, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product viewed' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product viewed', - properties: { - currency: 'USD', - total: '7.77', - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_item', - params: { - currency: 'USD', - value: 7.77, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'promotion clicked' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'promotion clicked', - properties: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '0', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - typesOfClient: 'gtag', - firebaseAppId: '1:17864591371:android:7a9520d3c78962e21f9fee', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'client_id', - events: [ - { - name: 'select_promotion', - params: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 0, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(firebase) check all property mappings for 'promotion clicked' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'promotion clicked', - properties: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '0', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - typesOfClient: 'firebase', - firebaseAppId: '1:17864591371:android:7a9520d3c78962e21f9fee', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - firebase_app_id: '1:17864591371:android:7a9520d3c78962e21f9fee', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - app_instance_id: 'f0dd99b6f979fb551ce583373900f937', - events: [ - { - name: 'select_promotion', - params: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 0, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product added' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - total: '7.77', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'add_to_cart', - params: { - currency: 'USD', - value: 7.77, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product removed' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product removed', - properties: { - currency: 'USD', - total: '7.77', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - engagementTimeMsec: 100, - sessionId: 655, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'remove_from_cart', - params: { - currency: 'USD', - value: 7.77, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - engagement_time_msec: 100, - session_id: 655, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'cart viewed' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'cart viewed', - properties: { - currency: 'USD', - total: '7.77', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_cart', - params: { - currency: 'USD', - value: 7.77, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'checkout started' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'checkout started', - properties: { - currency: 'USD', - total: 7.77, - coupon: 'SUMMER_FUN', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'begin_checkout', - params: { - currency: 'USD', - value: 7.77, - coupon: 'SUMMER_FUN', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - "(gtag) check all property mappings for 'payment info entered' -> 'add_payment_info' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'payment info entered', - properties: { - currency: 'USD', - value: '7.77', - coupon: 'SUMMER_FUN', - payment_method: 'Credit Card', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - params: { - currency: 'USD', - value: 7.77, - coupon: 'SUMMER_FUN', - payment_type: 'Credit Card', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - name: 'add_payment_info', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - "(gtag) check all property mappings for 'checkout Step Completed' -> 'add_shipping_info' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'checkout Step Completed', - properties: { - currency: 'USD', - value: '7.77', - coupon: 'SUMMER_FUN', - shipping_method: 'Ground', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - params: { - currency: 'USD', - value: 7.77, - coupon: 'SUMMER_FUN', - shipping_tier: 'Ground', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - name: 'add_shipping_info', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'order completed' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'order completed', - properties: { - currency: 'USD', - order_id: 'T_12345', - total: 12.21, - affiliation: 'Google Store', - coupon: 'SUMMER_FUN', - shipping: 3.33, - tax: 1.11, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'purchase', - params: { - currency: 'USD', - transaction_id: 'T_12345', - value: 12.21, - affiliation: 'Google Store', - coupon: 'SUMMER_FUN', - shipping: 3.33, - tax: 1.11, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'order refunded' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'order refunded', - properties: { - currency: 'USD', - order_id: 'T_12345', - total: 12.21, - affiliation: 'Google Store', - coupon: 'SUMMER_FUN', - shipping: 3.33, - tax: 1.11, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'refund', - params: { - currency: 'USD', - transaction_id: 'T_12345', - value: 12.21, - affiliation: 'Google Store', - coupon: 'SUMMER_FUN', - shipping: 3.33, - tax: 1.11, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) exclude only 'products' property from 'order refunded' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'order refunded', - properties: { - currency: 'USD', - order_id: 'T_12345', - total: 12.21, - affiliation: 'Google Store', - coupon: 'SUMMER_FUN', - shipping: 3.33, - tax: 1.11, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'refund', - params: { - currency: 'USD', - transaction_id: 'T_12345', - value: 12.21, - affiliation: 'Google Store', - coupon: 'SUMMER_FUN', - shipping: 3.33, - tax: 1.11, - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product added to wishlist' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added to wishlist', - properties: { - currency: 'USD', - total: '7.77', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'add_to_wishlist', - params: { - currency: 'USD', - value: 7.77, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product_shared' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product_shared', - properties: { - share_via: 'Twitter', - content_type: 'image', - item_id: 'C_12345', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'share', - params: { - method: 'Twitter', - content_type: 'image', - item_id: 'C_12345', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'product_shared' event name without it's properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product_shared', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'share', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'cart Shared' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'cart Shared', - properties: { - share_via: 'Twitter', - content_type: 'image', - item_id: 'C_12345', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'share', - params: { - method: 'Twitter', - content_type: 'image', - item_id: 'C_12345', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'cart Shared' event name with empty properties: {}", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'cart Shared', - properties: {}, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'share', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) check all property mappings for group call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'group', - properties: { - group_id: 'G_12345', - engagementTimeMsec: 100, - sessionId: 655, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'group', - params: { - group_id: 'G_12345', - engagement_time_msec: 100, - session_id: 655, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) group: send only group event name without it's properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'group', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'group', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'earn virtual currency' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'earn virtual currency', - properties: { - virtual_currency_name: 'Gems', - value: 5, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'earn_virtual_currency', - params: { - virtual_currency_name: 'Gems', - value: 5, - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'earn virtual currency' event name without it's properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'earn virtual currency', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'earn_virtual_currency', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'generate_lead' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'generate_lead', - properties: { - currency: 'USD', - value: 99.99, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'generate_lead', - params: { - currency: 'USD', - value: 99.99, - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'level_up' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'level_up', - properties: { - level: 5, - character: 'Player 1', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'level_up', - params: { - level: 5, - character: 'Player 1', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'level_up' event name without it's properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'level_up', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'level_up', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'login' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'login', - properties: { - method: 'Google', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'login', - params: { - method: 'Google', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'login' event name without it's properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'login', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'login', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'post_score' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'post_score', - properties: { - score: 10000, - level: 5, - character: 'Player 1', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'post_score', - params: { - score: 10000, - level: 5, - character: 'Player 1', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'login' event name with its required `score` properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'post_score', - properties: { - score: 10000, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'post_score', - params: { - score: 10000, - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'select_content' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'select_content', - properties: { - content_type: 'product', - item_id: 'I_12345', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'select_content', - params: { - content_type: 'product', - item_id: 'I_12345', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'group' event name without it's properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'select_content', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'select_content', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'sign_up' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'sign_up', - properties: { - method: 'Google', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'sign_up', - params: { - method: 'Google', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'sign_up' event name without it's properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'sign_up', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'sign_up', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'spend_virtual_currency' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'spend_virtual_currency', - properties: { - value: 5, - virtual_currency_name: 'Gems', - item_name: 'Starter Boost', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'spend_virtual_currency', - params: { - value: 5, - virtual_currency_name: 'Gems', - item_name: 'Starter Boost', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - "(gtag) send only 'spend_virtual_currency' event name with it's required 'value' and 'virtual_currency_name' properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'spend_virtual_currency', - properties: { - value: 5, - virtual_currency_name: 'Gems', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'spend_virtual_currency', - params: { - value: 5, - virtual_currency_name: 'Gems', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send 'tutorial_begin' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutorial_begin', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'tutorial_begin', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send 'tutorial_complete' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutorial_complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'tutorial_complete', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send all properties for 'unlock_achievement' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'unlock_achievement', - properties: { - achievement_id: 'A_12345', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'unlock_achievement', - params: { - achievement_id: 'A_12345', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send all properties for 'view_search_results' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'view_search_results', - properties: { - search_term: 'Clothing', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_search_results', - params: { - search_term: 'Clothing', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) pass only 'products: [...]' property for 'view_search_results' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'view_search_results', - properties: { - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_search_results', - params: { - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - "(gtag) pass custom event name 'rudderstack event' to GA4 along with custom properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'rudderstack event', - properties: { - total: '10', - timezone: { - name: 'Europe/Tallinn', - }, - engagementTimeMsec: 100, - sessionId: 655, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'rudderstack_event', - params: { - total: '10', - timezone_name: 'Europe/Tallinn', - engagement_time_msec: 100, - session_id: 655, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - "(gtag) pass custom event name 'rudderstack event' to GA4 along with custom properties and user_properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'rudderstack event', - properties: { - total: '10', - user_properties: { - price: '19', - }, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - user_properties: { - price: { - value: '19', - }, - }, - events: [ - { - name: 'rudderstack_event', - params: { - total: '10', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error]: (gtag) pass reserved event name to GA4', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'ad_click', - properties: { - total: '10', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'track:: Reserved event names are not allowed', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Exclusion]: (gtag) pass reserved property name to GA4 for custom events', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'rudderstack event', - properties: { - firebase_conversion: 'firebase_conversion', - google_id: '1234', - ga_value: 'ga_value', - value: '10', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'rudderstack_event', - params: { - value: '10', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - '[Exclusion]: (gtag) pass reserved property name and reserved properties in `user_properties` to GA4 for custom events', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'rudderstack event', - properties: { - firebase_conversion: 'firebase_conversion', - google_id: '1234', - ga_value: 'ga_value', - value: '10', - user_properties: { - first_open_time: 'first_open_time', - user_id: 'user_id', - firebase_value: 'firebase_value', - price: '100', - }, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - user_properties: { - price: { - value: '100', - }, - }, - events: [ - { - name: 'rudderstack_event', - params: { - value: '10', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error]: (gtag) pass reserved event names along with reserved properties', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'app_remove', - properties: { - firebase_conversion: 'firebase_conversion', - google_id: '1234', - ga_value: 'ga_value', - value: '10', - user_properties: { - first_open_time: 'first_open_time', - user_id: 'user_id', - firebase_value: 'firebase_value', - price: '100', - }, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'track:: Reserved event names are not allowed', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Error] (gtag) pass reserved custom prefix names to GA4 events', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'firebase_event1', - properties: { - firebase_conversion: 'firebase_conversion', - google_id: '1234', - ga_value: 'ga_value', - value: '10', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Reserved custom prefix names are not allowed', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product added' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - total: '7.77', - google_data: 'google_data', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'add_to_cart', - params: { - currency: 'USD', - value: 7.77, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) pass custom event name with its properties', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'rudderstack event', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'rudderstack_event', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) map 'product added' properties to ga4 'add_to_cart' items array", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - affiliation: 'Google Merchandise Store', - brand: 'Google', - category: 'Related_products', - coupon: 'SUMMER_FUN', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - list_id: 'related_products', - location_id: 'L_12345', - name: 'Monopoly: 3rd Edition', - position: '1', - price: '19', - product_id: '507f1f77bcf86cd799439011', - products: [], - quantity: '2', - total: '7.77', - variant: 'green', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - FORM: {}, - JSON: { - client_id: 'client_id', - events: [ - { - name: 'add_to_cart', - params: { - currency: 'USD', - engagement_time_msec: 1, - items: [ - { - affiliation: 'Google Merchandise Store', - coupon: 'SUMMER_FUN', - currency: 'USD', - discount: 2.22, - index: 1, - item_brand: 'Google', - item_category: 'Related_products', - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_id: '507f1f77bcf86cd799439011', - item_list_id: 'related_products', - item_list_name: 'Related Products', - item_name: 'Monopoly: 3rd Edition', - item_variant: 'green', - location_id: 'L_12345', - price: 19, - quantity: 2, - }, - ], - list_id: 'related_products', - value: 7.77, - }, - }, - ], - non_personalized_ads: true, - timestamp_micros: 1650950229000000, - }, - JSON_ARRAY: {}, - XML: {}, - }, - endpoint: 'https://www.google-analytics.com/mp/collect', - files: {}, - headers: { - 'Content-Type': 'application/json', - HOST: 'www.google-analytics.com', - }, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - type: 'REST', - version: '1', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - '(gtag) pass event name with invalid data type for products: {...} properties (when products parameter is optional)', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - total: '7.77', - products: { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - affiliation: 'Google Merchandise Store', - brand: 'Google', - category: 'Related_products', - coupon: 'SUMMER_FUN', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - list_id: 'related_products', - location_id: 'L_12345', - name: 'Monopoly: 3rd Edition', - position: '1', - price: '19', - product_id: '507f1f77bcf86cd799439011', - quantity: '2', - variant: 'green', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - FORM: {}, - JSON: { - client_id: 'client_id', - events: [ - { - name: 'add_to_cart', - params: { - currency: 'USD', - engagement_time_msec: 1, - items: [ - { - affiliation: 'Google Merchandise Store', - coupon: 'SUMMER_FUN', - currency: 'USD', - discount: 2.22, - index: 1, - item_brand: 'Google', - item_category: 'Related_products', - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_id: '507f1f77bcf86cd799439011', - item_list_id: 'related_products', - item_list_name: 'Related Products', - item_name: 'Monopoly: 3rd Edition', - item_variant: 'green', - location_id: 'L_12345', - price: 19, - quantity: 2, - }, - ], - list_id: 'related_products', - value: 7.77, - }, - }, - ], - non_personalized_ads: true, - timestamp_micros: 1650950229000000, - }, - JSON_ARRAY: {}, - XML: {}, - }, - endpoint: 'https://www.google-analytics.com/mp/collect', - files: {}, - headers: { - 'Content-Type': 'application/json', - HOST: 'www.google-analytics.com', - }, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - type: 'REST', - version: '1', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - '[Error] (gtag) pass event name to GA4 with missing fields i.e required in products: [..]', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - total: '7.77', - products: [ - { - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'One of product_id or name is required', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: - '[Error] (gtag) pass event name to GA4 with missing fields i.e required in properties', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'Product Viewed', - properties: { - currency: 'USD', - total: '7.77', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'One of product_id or name is required', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Error] (gtag) missing API Secret', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutotial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'API Secret not found. Aborting ', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'configuration', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Error] (gtag) missing measurementId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutotial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: '', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'measurementId must be provided. Aborting', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'configuration', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '(gtag) firing group event with event name. should take event name by default', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'group', - event: 'tutorial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'client_id', - events: [ - { - name: 'join_group', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error] (gtag) payload has missing message.type', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - event: 'tutotial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Message Type is not present. Aborting message.', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Error] (gtag) payload has missing event name', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Event name is required', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '(gtag) taking client_id from anonymousId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutotial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - events: [ - { - name: 'tutotial_complete', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error] (firebase) payload has missing ga4AppInstanceId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutotial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: '', - firebaseAppId: '1:17864591371:android:7a9520d3c78962e21f9fee', - blockPageViewEvent: false, - typesOfClient: 'firebase', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'ga4AppInstanceId must be provided under externalId', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Error] (firebase) pass reserved event name', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'app_store_subscription_cancel', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: '', - firebaseAppId: '1:17864591371:android:7a9520d3c78962e21f9fee', - blockPageViewEvent: false, - typesOfClient: 'firebase', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Reserved custom event names are not allowed', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'tutorial complete' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutorial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - non_personalized_ads: true, - events: [ - { - name: 'tutorial_complete', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'cart viewed' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'cart viewed', - properties: { - currency: 'USD', - total: '7.77', - products: [ - { - product_id: 0, - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_cart', - params: { - currency: 'USD', - value: 7.77, - engagement_time_msec: 1, - items: [ - { - item_id: 0, - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error]: (firebase) missing firebaseAppId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'promotion clicked', - properties: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '0', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: '', - typesOfClient: 'firebase', - firebaseAppId: '', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'firebaseAppId must be provided. Aborting', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'configuration', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '(gtag) firing page call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - rudderId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'page', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - events: [ - { - name: 'page_view', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) firing page call with custom properties', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - rudderId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'page', - event: 'page view', - properties: { - view: 'login', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - events: [ - { - name: 'page_view', - params: { - view: 'login', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error]: (gtag) pass timestamp more than 72 hours into the past', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-20T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutotial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - typesOfClient: 'gtag', - blockPageViewEvent: false, - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Allowed timestamp is [72 hours] into the past', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Error]: (gtag) pass timestamp more than 15 min into the future', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-05-05T15:47:57Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutotial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - typesOfClient: 'gtag', - blockPageViewEvent: false, - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Allowed timestamp is [15 minutes] into the future', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: - '(gtag) pass custom properties along with products: [..] parameters to GA4 standard events along with its stated ones', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'order completed', - properties: { - checkout_id: '12345', - order_id: '1234', - myCustomProp: 'My arbitray value', - affiliation: 'Apple Store', - total: 20, - revenue: 15, - shipping: 22, - tax: 1, - discount: 1.5, - coupon: 'ImagePro', - currency: 'USD', - products: [ - { - product_id: '123', - sku: 'G-32', - name: 'Monopoly', - price: 14, - quantity: 1, - category: 'Games', - item_category2: 'Board games', - url: 'https://www.website.com/product/path', - image_url: 'https://www.website.com/product/path.jpg', - }, - ], - timezone: { - name: 'Europe/Tallinn', - }, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'purchase', - params: { - checkout_id: '12345', - transaction_id: '1234', - myCustomProp: 'My arbitray value', - affiliation: 'Apple Store', - value: 20, - shipping: 22, - tax: 1, - discount: 1.5, - coupon: 'ImagePro', - currency: 'USD', - engagement_time_msec: 1, - items: [ - { - item_id: '123', - sku: 'G-32', - item_name: 'Monopoly', - price: 14, - quantity: 1, - item_category: 'Games', - item_category2: 'Board games', - url: 'https://www.website.com/product/path', - image_url: 'https://www.website.com/product/path.jpg', - }, - ], - timezone_name: 'Europe/Tallinn', - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - '(gtag) pass custom properties excluding products: [..] parameter to GA4 standard events along with its stated ones', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'promotion clicked', - properties: { - 'customProp-1': 'check-1', - 'customProp-2': 'check-2', - timezone: { - name: 'Europe/Tallinn', - }, - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'select_promotion', - params: { - 'customProp-1': 'check-1', - 'customProp-2': 'check-2', - timezone_name: 'Europe/Tallinn', - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) message type group -> 'join_group' with custom event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'group', - traits: { - custom1: 1234, - custom2: 'custom2', - timezone: { - name: 'Europe/Tallinn', - }, - engagementTimeMsec: 100, - sessionId: 655, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'client_id', - events: [ - { - name: 'join_group', - params: { - custom1: 1234, - custom2: 'custom2', - timezone_name: 'Europe/Tallinn', - engagement_time_msec: 100, - session_id: 655, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag): check args keyword for price x currency multiplication', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - price: 2.4, - quantity: 2, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'client_id', - events: [ - { - name: 'add_to_cart', - params: { - currency: 'USD', - value: 4.8, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - price: 2.4, - quantity: 2, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - "(gtag): take page properties from context.page for 'page' call along with custom properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - rudderId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - page: { - url: 'http://morkey.in', - path: '/cart', - title: 'miphone', - search: 'MI', - referrer: 'morkey', - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'page', - integrations: { - All: true, - }, - properties: { - cust1: 1234, - engagementTimeMsec: 100, - sessionId: 655, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - events: [ - { - name: 'page_view', - params: { - page_referrer: 'morkey', - page_title: 'miphone', - page_location: 'http://morkey.in', - cust1: 1234, - engagement_time_msec: 100, - session_id: 655, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error] GA4: event not as string', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: { - name: 'promotion_viewed', - }, - properties: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '0', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'track:: event name should be string', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Error] GA4: client_id not found in all four path', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - price: 2.4, - quantity: 2, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: true, - sendLoginSignup: true, - newOrExistingUserTrait: 'firstLogin', - loginSignupMethod: 'method', - generateLead: true, - generateLeadValueTrait: 'value', - generateLeadCurrencyTrait: 'currency', - clientIdFieldIdentifier: 'properties.client_id', - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'ga4ClientId, anonymousId or messageId must be provided', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'configuration', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: - "client_id isn't sent from the path defined in the webapp config, falling back to default values i.e here it is anonymousId", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - price: 2.4, - quantity: 2, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: true, - sendLoginSignup: true, - newOrExistingUserTrait: 'firstLogin', - loginSignupMethod: 'method', - generateLead: true, - generateLeadValueTrait: 'value', - generateLeadCurrencyTrait: 'currency', - clientIdFieldIdentifier: 'properties.client_id', - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'add_to_cart', - params: { - items: [ - { - index: 1, - price: 19, - coupon: 'SUMMER_FUN', - item_id: '507f1f77bcf86cd799439011', - currency: 'USD', - discount: 2.22, - quantity: 2, - item_name: 'Monopoly: 3rd Edition', - item_brand: 'Google', - affiliation: 'Google Merchandise Store', - location_id: 'L_12345', - item_list_id: 'related_products', - item_variant: 'green', - item_category: 'Apparel', - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_name: 'Related Products', - }, - ], - price: 2.4, - value: 4.8, - currency: 'USD', - quantity: 2, - engagement_time_msec: 1, - }, - }, - ], - client_id: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - 'any custom or item property with array value, is flattened with underscore delimeter', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - originalTimestamp: '2022-04-26T05:17:09Z', - properties: { - currency: 'USD', - total: '7.77', - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - address: { - city: 'kolkata', - district: '24pgs', - }, - categoryLevels: ['Furniture', 'Bedroom Furniture', 'Dressers & Chests'], - products: [ - { - product_id: '1234', - product_details: { - colour: 'red', - shape: 'rectangle', - }, - productLevels: ['test1', 'test2', 'test3'], - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-11-14T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'client_id', - events: [ - { - name: 'add_to_cart', - params: { - currency: 'USD', - value: 7.77, - items: [ - { - item_id: '1234', - product_details_colour: 'red', - product_details_shape: 'rectangle', - productLevels_0: 'test1', - productLevels_1: 'test2', - productLevels_2: 'test3', - }, - ], - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - address_city: 'kolkata', - address_district: '24pgs', - categoryLevels_0: 'Furniture', - categoryLevels_1: 'Bedroom Furniture', - categoryLevels_2: 'Dressers & Chests', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: 'extract session_id from context.sessionId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - sessionId: 16678456735, - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - price: 2.4, - quantity: 2, - client_id: 'client@1234', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: true, - sendLoginSignup: true, - newOrExistingUserTrait: 'firstLogin', - loginSignupMethod: 'method', - generateLead: true, - generateLeadValueTrait: 'value', - generateLeadCurrencyTrait: 'currency', - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'add_to_cart', - params: { - items: [ - { - index: 1, - price: 19, - coupon: 'SUMMER_FUN', - item_id: '507f1f77bcf86cd799439011', - currency: 'USD', - discount: 2.22, - quantity: 2, - item_name: 'Monopoly: 3rd Edition', - item_brand: 'Google', - affiliation: 'Google Merchandise Store', - location_id: 'L_12345', - item_list_id: 'related_products', - item_variant: 'green', - item_category: 'Apparel', - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_name: 'Related Products', - }, - ], - price: 2.4, - value: 4.8, - currency: 'USD', - quantity: 2, - session_id: 16678456735, - engagement_time_msec: 1, - client_id: 'client@1234', - }, - }, - ], - client_id: 'client_id', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) send integer userId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - userId: 34567, - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - sessionId: 16678456735, - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - price: 2.4, - quantity: 2, - client_id: 'client@1234', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: true, - sendLoginSignup: true, - newOrExistingUserTrait: 'firstLogin', - loginSignupMethod: 'method', - generateLead: true, - generateLeadValueTrait: 'value', - generateLeadCurrencyTrait: 'currency', - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'add_to_cart', - params: { - items: [ - { - index: 1, - price: 19, - coupon: 'SUMMER_FUN', - item_id: '507f1f77bcf86cd799439011', - currency: 'USD', - discount: 2.22, - quantity: 2, - item_name: 'Monopoly: 3rd Edition', - item_brand: 'Google', - affiliation: 'Google Merchandise Store', - location_id: 'L_12345', - item_list_id: 'related_products', - item_variant: 'green', - item_category: 'Apparel', - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_name: 'Related Products', - }, - ], - price: 2.4, - value: 4.8, - currency: 'USD', - quantity: 2, - session_id: 16678456735, - engagement_time_msec: 1, - client_id: 'client@1234', - }, - }, - ], - client_id: 'client_id', - user_id: '34567', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) login event with user_properties', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - traits: { - campaign: 'advertizing', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'login', - properties: { - method: 'facebook', - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'login', - params: { - method: 'facebook', - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - user_properties: { - campaign: { - value: 'advertizing', - }, - }, - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) sign_up event', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - traits: { - campaign: 'advertizing', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'sign_up', - properties: { - method: 'google', - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'sign_up', - params: { - method: 'google', - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - user_properties: { - campaign: { - value: 'advertizing', - }, - }, - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) generate_lead event', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - traits: { - campaign: 'advertizing', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'generate_lead', - properties: { - source: 'instagram', - value: 20, - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'generate_lead', - params: { - currency: 'USD', - value: 20, - source: 'instagram', - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - user_properties: { - campaign: { - value: 'advertizing', - }, - }, - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) track call with page information such as url, title, referrer', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - page: { - initial_referrer: '$direct', - path: '/', - referrer: '$direct', - tab_url: 'https://www.rudderstack.com/', - title: 'Document', - url: 'https://www.rudderstack.com/', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'generate_lead', - properties: { - source: 'instagram', - value: 20, - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'generate_lead', - params: { - currency: 'USD', - value: 20, - source: 'instagram', - page_location: 'https://www.rudderstack.com/', - page_referrer: '$direct', - page_title: 'Document', - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - '(gtag) track event with hybrid connection mode using buffer cloud mode event approach', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - traits: { - campaign: 'advertizing', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'generate_lead', - properties: { - source: 'instagram', - value: 20, - }, - integrations: { - All: true, - 'Google Analytics 4 (GA4)': { - clientId: '554581488.1683172875', - sessionId: '1683172875', - }, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - connectionMode: 'hybrid', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'generate_lead', - params: { - currency: 'USD', - value: 20, - source: 'instagram', - session_id: '1683172875', - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - user_properties: { - campaign: { - value: 'advertizing', - }, - }, - client_id: '554581488.1683172875', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - '(gtag) track event with hybrid connection mode using override client_id and session_id approach', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - traits: { - campaign: 'advertizing', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - sessionId: 1683172874065, - }, - type: 'track', - event: 'generate_lead', - properties: { - source: 'instagram', - value: 20, - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - connectionMode: 'hybrid', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'generate_lead', - params: { - currency: 'USD', - value: 20, - source: 'instagram', - session_id: 1683172874065, - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - user_properties: { - campaign: { - value: 'advertizing', - }, - }, - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) firing group calls with GA4 hybrid mode connection', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'group', - event: 'tutorial complete', - integrations: { - 'Google Analytics 4': { - clientId: '4718026.1683606287', - sessionId: '1683606287', - sessionNumber: 1, - }, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - connectionMode: 'hybrid', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - non_personalized_ads: true, - client_id: '4718026.1683606287', - events: [ - { - name: 'join_group', - params: { - engagement_time_msec: 1, - session_id: '1683606287', - session_number: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) sign_up event with all data types of user_properties', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - traits: { - campaign: 'advertizing', - name: 'rudder', - age: 45, - hobby: ['dancing', 'singing', 'reading'], - enableEURegion: false, - isEnterpriseUser: { - value: false, - }, - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'sign_up', - properties: { - method: 'google', - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'sign_up', - params: { - method: 'google', - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - user_properties: { - age: { - value: 45, - }, - name: { - value: 'rudder', - }, - campaign: { - value: 'advertizing', - }, - enableEURegion: { - value: false, - }, - }, - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error]: (gtag) event name starts with numbers', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: '1234_sign_up', - properties: { - total: '10', - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - connectionMode: 'cloud', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: - 'Event name must start with a letter and can only contain letters, numbers, and underscores', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '(gtag) event having multiple empty array and object parameters', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'track', - event: 'logIn', - userId: 'user@1', - group_id: 'group@1', - anon_id: '78e95d6d-58c0-4237-b99e-2ef510b6d502', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - properties: { - user_interest: 'Moderate', - company_interest: '', - profile: [ - { - is_6qa: true, - product: null, - product_fit: 'Moderate', - product_stage: 'Purchase', - intent_score: 89, - profile_score: 52, - product_display$name: 'rudderstack', - }, - ], - user_company: 'Analytics consulting', - user_account: '1', - user_id_mappings: '330098|245252|461224|282599', - company_naics_6sense: '5173', - usr_consent: null, - firebase_user_id: 'kdgMnP', - google_user_id: 'G-123456', - company_domain: 'consulting.net', - company_region: 'New Zealand', - user_product_interests: { - ids: [], - list: [ - { - id: 330098, - name: [], - }, - { - id: 245252, - name: {}, - }, - ], - names: [], - }, - company_country: {}, - company_industry: 'Business Analytics', - company_revenue: '$5M - $10M', - company_annual_revenue: '5568000', - company_sic_description: '', - company_naics_description: [], - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - connectionMode: 'cloud', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'login', - params: { - company_annual_revenue: '5568000', - company_domain: 'consulting.net', - company_industry: 'Business Analytics', - company_naics_6sense: '5173', - company_region: 'New Zealand', - company_revenue: '$5M - $10M', - engagement_time_msec: 1, - profile_0_intent_score: 89, - profile_0_is_6qa: true, - profile_0_product_display$name: 'rudderstack', - profile_0_product_fit: 'Moderate', - profile_0_product_stage: 'Purchase', - profile_0_profile_score: 52, - user_account: '1', - user_company: 'Analytics consulting', - user_id_mappings: '330098|245252|461224|282599', - user_interest: 'Moderate', - user_product_interests_list_0_id: 330098, - user_product_interests_list_1_id: 245252, - }, - }, - ], - user_id: 'user@1', - client_id: 'client_id', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) campaign_details custom event', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - campaign: { - id: 'google_1234', - name: 'Summer_fun', - source: 'google', - medium: 'cpc', - term: 'summer+travel', - content: 'logo link', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'Campaign Details', - properties: {}, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'QyWKGHj8QhG2L4ePAPiXCA', - measurementId: 'G-T40PE6BET4', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'campaign_details', - params: { - campaign_id: 'google_1234', - campaign: 'Summer_fun', - source: 'google', - medium: 'cpc', - term: 'summer+travel', - content: 'logo link', - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'QyWKGHj8QhG2L4ePAPiXCA', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) send consents setting to ga4 with login event', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'dummyGA4AppInstanceId', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'login', - properties: { - method: 'Google', - }, - integrations: { - All: true, - GA4: { - consents: { - ad_personalization: 'GRANTED', - ad_user_data: 'GRANTED', - }, - }, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-123456', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-123456', - }, - body: { - JSON: { - client_id: 'client_id', - consent: { - ad_personalization: 'GRANTED', - ad_user_data: 'GRANTED', - }, - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'login', - params: { - method: 'Google', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) send consents setting to ga4 with login event', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'dummyGA4AppInstanceId', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'login', - properties: { - method: 'Google', - }, - integrations: { - All: true, - GA4: { - consents: { - ad_personalization: 'NOT_SPECIFIED', - ad_user_data: 'DENIED', - }, - }, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-123456', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-123456', - }, - body: { - JSON: { - client_id: 'client_id', - consent: { - ad_user_data: 'DENIED', - }, - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'login', - params: { - method: 'Google', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, + ...pageTestData, + ...trackTestData, + ...ecommTestData, + ...groupTestData, + ...validationTestData, ]; diff --git a/test/integrations/destinations/ga4/processor/ecomTestData.ts b/test/integrations/destinations/ga4/processor/ecomTestData.ts new file mode 100644 index 0000000000..238e44222b --- /dev/null +++ b/test/integrations/destinations/ga4/processor/ecomTestData.ts @@ -0,0 +1,2466 @@ +import { defaultMockFns } from '../mocks'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedTrackPayload, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'GA4', + DestinationDefinition: { + ID: '123', + Name: 'GA4', + DisplayName: 'Google Analytics 4 (GA4)', + Config: {}, + }, + Config: { + apiSecret: 'dummyApiSecret', + measurementId: 'dummyMeasurementId', + firebaseAppId: '', + blockPageViewEvent: false, + typesOfClient: 'gtag', + extendPageViewParams: false, + sendUserId: false, + eventFilteringOption: 'disable', + blacklistedEvents: [ + { + eventName: '', + }, + ], + whitelistedEvents: [ + { + eventName: '', + }, + ], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const deviceInfo = { + adTrackingEnabled: 'false', + advertisingId: 'dummyAdvertisingId', + id: 'device@1', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, +}; + +const commonOutputHeaders = { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', +}; + +const commonOutputParams = { + api_secret: 'dummyApiSecret', + measurement_id: 'dummyMeasurementId', +}; + +const rudderId = 'dummyRudderId'; +const anonymousId = 'dummyAnonId'; +const externalId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, +]; +const externalIdWithClientId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, + { + type: 'ga4ClientId', + id: 'dummyClientId', + }, +]; +const sentAt = '2022-04-20T15:20:57Z'; +const originalTimestamp = '2022-04-26T05:17:09Z'; + +const timezone = { + name: 'Europe/Tallinn', +}; +const clientId = 'dummyClientId'; +const userId = 'default-user-id'; + +const coupon = 'SUMMER_FUN'; +const defaultCurrency = 'USD'; +const search_term = 'Clothing'; +const shipping_method = 'Ground'; +const payment_method = 'Credit Card'; +const list_id = 'related_products'; +const category = 'Related_products'; + +const value = 7.77; +const total = '7.77'; +const sessionId = 655; +const engagementTimeMsec = 100; +const non_personalized_ads = true; +const defaultEngagementTimeMsec = 1; +const timestamp_micros = 1650950229000000; + +const commonProductInfo = [ + { + product_id: '507f1f77bcf86cd799439011', + name: 'Monopoly: 3rd Edition', + coupon: 'SUMMER_FUN', + category: 'Apparel', + brand: 'Google', + variant: 'green', + price: '19', + quantity: '2', + position: '1', + affiliation: 'Google Merchandise Store', + currency: 'USD', + discount: 2.22, + item_category2: 'Adult', + item_category3: 'Shirts', + item_category4: 'Crew', + item_category5: 'Short sleeve', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + }, +]; + +const expectedOutputItems = [ + { + item_id: '507f1f77bcf86cd799439011', + item_name: 'Monopoly: 3rd Edition', + coupon: 'SUMMER_FUN', + item_category: 'Apparel', + item_brand: 'Google', + item_variant: 'green', + price: 19, + quantity: 2, + index: 1, + affiliation: 'Google Merchandise Store', + currency: 'USD', + discount: 2.22, + item_category2: 'Adult', + item_category3: 'Shirts', + item_category4: 'Crew', + item_category5: 'Short sleeve', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + }, +]; + +const promotionEventsCommonProductInfo = [ + { + product_id: '507f1f77bcf86cd799439011', + name: 'Monopoly: 3rd Edition', + coupon: 'SUMMER_FUN', + category: 'Apparel', + brand: 'Google', + variant: 'green', + price: '19', + quantity: '2', + position: '0', + affiliation: 'Google Merchandise Store', + currency: 'USD', + discount: 2.22, + item_category2: 'Adult', + item_category3: 'Shirts', + item_category4: 'Crew', + item_category5: 'Short sleeve', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + promotion_id: 'P_12345', + promotion_name: 'Summer Sale', + creative_name: 'summer_banner2', + creative_slot: 'featured_app_1', + }, +]; + +const promotionEventsCommonParams = { + creative_name: 'Summer Banner', + creative_slot: 'featured_app_1', + location_id: 'L_12345', + promotion_id: 'P_12345', + promotion_name: 'Summer Sale', +}; + +const promotionEventsExpectedOutputItems = [ + { + item_id: '507f1f77bcf86cd799439011', + item_name: 'Monopoly: 3rd Edition', + coupon: 'SUMMER_FUN', + item_category: 'Apparel', + item_brand: 'Google', + item_variant: 'green', + price: 19, + quantity: 2, + index: 0, + affiliation: 'Google Merchandise Store', + currency: 'USD', + discount: 2.22, + item_category2: 'Adult', + item_category3: 'Shirts', + item_category4: 'Crew', + item_category5: 'Short sleeve', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + promotion_id: 'P_12345', + promotion_name: 'Summer Sale', + creative_name: 'summer_banner2', + creative_slot: 'featured_app_1', + }, +]; + +const promotionEventsExpectedOutputParams = { + creative_name: 'Summer Banner', + creative_slot: 'featured_app_1', + location_id: 'L_12345', + promotion_id: 'P_12345', + promotion_name: 'Summer Sale', + engagement_time_msec: defaultEngagementTimeMsec, +}; + +const orderEventsCommonParams = { + currency: defaultCurrency, + order_id: 'T_12345', + total: 12.21, + affiliation: 'Google Store', + coupon, + shipping: 3.33, + tax: 1.11, +}; + +const orderEventsExpectedOutputParams = { + currency: defaultCurrency, + transaction_id: 'T_12345', + value: 12.21, + affiliation: 'Google Store', + coupon, + shipping: 3.33, + tax: 1.11, +}; + +const shareProductsCommonParams = { + share_via: 'Twitter', + content_type: 'image', + item_id: 'C_12345', +}; + +const shareProductsExpectedOutputParams = { + method: 'Twitter', + content_type: 'image', + item_id: 'C_12345', +}; + +const eventEndPoint = 'https://www.google-analytics.com/mp/collect'; + +export const ecommTestData: ProcessorTestData[] = [ + { + id: 'ga4-ecom-test-1', + name: 'ga4', + description: 'Track event call for Products Searched event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product searched event properties and event name should be search', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'Products Searched', + sentAt, + context: { + externalId, + }, + properties: { + query: 't-shirts', + }, + anonymousId, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: 'dummyAnonId', + timestamp_micros, + user_id: userId, + events: [ + { + name: 'search', + params: { + search_term: 't-shirts', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-2', + name: 'ga4', + description: 'Track event call for product list viewed event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product list viewed event properties and event name should be view_item_list', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product list viewed', + rudderId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + properties: { + list_id, + category, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: 'default-anonymousId', + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'view_item_list', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + item_list_id: 'related_products', + item_list_name: 'Related_products', + items: expectedOutputItems, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-3', + name: 'ga4', + description: 'Track event call for promotion viewed event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all promotion viewed event properties and event name should be view_promotion', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'promotion viewed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...promotionEventsCommonParams, + products: promotionEventsCommonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'view_promotion', + params: { + ...promotionEventsExpectedOutputParams, + items: promotionEventsExpectedOutputItems, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-4', + name: 'ga4', + description: 'Track event call for promotion clicked event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all promotion clicked event properties and event name should be select_promotion', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'promotion clicked', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...promotionEventsCommonParams, + products: promotionEventsCommonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'select_promotion', + params: { + ...promotionEventsExpectedOutputParams, + items: promotionEventsExpectedOutputItems, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-5', + name: 'ga4', + description: + 'Track event call for promotion clicked event by excuding products from properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all promotion clicked event properties except products and event name should be select_promotion', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'promotion clicked', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...promotionEventsCommonParams, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'select_promotion', + params: { + ...promotionEventsExpectedOutputParams, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-6', + name: 'ga4', + description: 'Track event call for product clicked event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product clicked event properties and event name should be select_item', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product clicked', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...commonProductInfo[0], + list_id, + category, + timezone, + sessionId, + engagementTimeMsec, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'select_item', + params: { + item_list_id: list_id, + item_list_name: category, + items: [{ ...expectedOutputItems[0], item_category: category }], + timezone_name: timezone.name, + engagement_time_msec: engagementTimeMsec, + session_id: sessionId, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-7', + name: 'ga4', + description: 'Track event call for product viewed event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product viewed event properties and event name should be view_item', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product viewed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total, + ...commonProductInfo[0], + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'view_item', + params: { + currency: defaultCurrency, + value, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-8', + name: 'ga4', + description: 'Track event call for product added event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product added event properties and event name should be add_to_cart', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product added', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + currency: defaultCurrency, + total, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_to_cart', + params: { + currency: defaultCurrency, + value, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-9', + name: 'ga4', + description: 'Track event call for product removed event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product removed event properties and event name should be remove_from_cart', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product removed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + currency: defaultCurrency, + total, + products: commonProductInfo, + sessionId, + engagementTimeMsec, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'remove_from_cart', + params: { + currency: defaultCurrency, + value, + items: expectedOutputItems, + engagement_time_msec: engagementTimeMsec, + session_id: sessionId, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-10', + name: 'ga4', + description: 'Track event call for cart viewed event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all cart viewed event properties and event name should be view_cart', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'cart viewed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + currency: defaultCurrency, + total, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'view_cart', + params: { + currency: defaultCurrency, + value, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-11', + name: 'ga4', + description: 'Track event call for checkout started event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all checkout started event properties and event name should be begin_checkout', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'checkout started', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + currency: defaultCurrency, + total, + coupon, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'begin_checkout', + params: { + currency: defaultCurrency, + value, + coupon, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-12', + name: 'ga4', + description: 'Track event call for payment info entered event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all payment info entered event properties and event name should be add_payment_info', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'payment info entered', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + currency: defaultCurrency, + total, + coupon, + payment_method, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_payment_info', + params: { + currency: defaultCurrency, + value, + coupon, + payment_type: payment_method, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-13', + name: 'ga4', + description: 'Track event call for Checkout Step Completed event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all Checkout Step Completed event properties and event name should be add_shipping_info', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'Checkout Step Completed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + currency: defaultCurrency, + total, + coupon, + shipping_method, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_shipping_info', + params: { + currency: defaultCurrency, + value, + coupon, + shipping_tier: shipping_method, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-14', + name: 'ga4', + description: 'Track event call for order completed event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all order completed event properties and event name should be purchase', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'order completed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...orderEventsCommonParams, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'purchase', + params: { + ...orderEventsExpectedOutputParams, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-15', + name: 'ga4', + description: 'Track event call for order refunded event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all order refunded event properties and event name should be refund', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'order refunded', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...orderEventsCommonParams, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'refund', + params: { + ...orderEventsExpectedOutputParams, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-16', + name: 'ga4', + description: 'Track event call for order refunded event and exclude products from properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all order refunded event properties except products and event name should be refund', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'order refunded', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...orderEventsCommonParams, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'refund', + params: { + ...orderEventsExpectedOutputParams, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-17', + name: 'ga4', + description: 'Track event call for product added to wishlist event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product added to wishlist event properties and event name should be add_to_wishlist', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product added to wishlist', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + currency: defaultCurrency, + total, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_to_wishlist', + params: { + currency: defaultCurrency, + value, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-18', + name: 'ga4', + description: 'Track event call for product_shared event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product_shared event properties and event name should be share', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product_shared', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: shareProductsCommonParams, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'share', + params: { + ...shareProductsExpectedOutputParams, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-19', + name: 'ga4', + description: 'Track event call for product_shared event without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no product_shared event properties and event name should be share', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product_shared', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'share', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-20', + name: 'ga4', + description: 'Track event call for cart Shared event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all cart Shared event properties and event name should be share', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'cart Shared', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: shareProductsCommonParams, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'share', + params: { + ...shareProductsExpectedOutputParams, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-21', + name: 'ga4', + description: 'Track event call for cart Shared event without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no cart Shared event properties and event name should be share', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'cart Shared', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'share', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-22', + name: 'ga4', + description: + 'Track event call for promotion clicked event with all event properties and type of client is firebase', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all promotion clicked event properties and event name should be select_promotion and response should contain all firebase related params', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { + ...destination.Config, + typesOfClient: 'firebase', + firebaseAppId: 'dummyFirebaseAppId', + }, + }, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'promotion clicked', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...promotionEventsCommonParams, + products: promotionEventsCommonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: { + api_secret: 'dummyApiSecret', + firebase_app_id: 'dummyFirebaseAppId', + }, + JSON: { + app_instance_id: 'dummyAppInstanceId', + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'select_promotion', + params: { + ...promotionEventsExpectedOutputParams, + items: promotionEventsExpectedOutputItems, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-23', + name: 'ga4', + description: 'Track event call for view_search_results event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all view_search_results event properties and event name should be view_search_results', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'view_search_results', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + search_term, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'view_search_results', + params: { + search_term, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-24', + name: 'ga4', + description: 'Track event call for view_search_results event with only products as properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain only products as view_search_results event properties and event name should be view_search_results', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'view_search_results', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'view_search_results', + params: { + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-25', + name: 'ga4', + description: 'Track event call for product added event with products information at root level', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain items array with product information and event name should be add_to_cart', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product added', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total, + ...commonProductInfo[0], + products: [], + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_to_cart', + params: { + currency: defaultCurrency, + value, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-26', + name: 'ga4', + description: 'Track event call for product added event with products as an object', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain items array with product information and event name should be add_to_cart', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product added', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total, + ...commonProductInfo[0], + products: commonProductInfo[0], + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_to_cart', + params: { + currency: defaultCurrency, + value, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-27', + name: 'ga4', + description: + 'Scenario to test custom properties along with products: [..] parameters to GA4 standard events along with its stated ones', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain both custom and standard properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'order completed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + revenue: 15, + discount: 1.5, + checkout_id: '12345', + myCustomProp: 'My arbitray value', + ...orderEventsCommonParams, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'purchase', + params: { + affiliation: 'Google Store', + checkout_id: '12345', + coupon: 'SUMMER_FUN', + discount: 1.5, + currency: defaultCurrency, + myCustomProp: 'My arbitray value', + shipping: 3.33, + tax: 1.11, + transaction_id: 'T_12345', + value: 12.21, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-28', + name: 'ga4', + description: + 'Scenario to test custom properties excluding products: [..] parameter to GA4 standard events along with its stated ones', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain both custom and standard properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'promotion clicked', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + timezone, + promotion_page: '/products', + promotion_channel: 'facebook', + ...promotionEventsCommonParams, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'select_promotion', + params: { + promotion_page: '/products', + promotion_channel: 'facebook', + timezone_name: 'Europe/Tallinn', + ...promotionEventsExpectedOutputParams, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-29', + name: 'ga4', + description: 'Scenario to test price x currency multiplication for product added event', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain value calculated from price x currency and event name should be add_to_cart', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product added', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + price: 2.4, + quantity: 2, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_to_cart', + params: { + price: 2.4, + quantity: 2, + value: 4.8, + currency: defaultCurrency, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-30', + name: 'ga4', + description: + 'Scenario to test any custom or item property with array value, is flattened with underscore delimeter', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and all the nested properties should get flattened in final payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product added', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + properties: { + ...commonProductInfo[0], + address: { + city: 'kolkata', + district: '24pgs', + }, + categoryLevels: ['Furniture', 'Bedroom Furniture', 'Dressers & Chests'], + products: [ + { + product_id: '1234', + product_details: { + colour: 'red', + shape: 'rectangle', + }, + productLevels: ['test1', 'test2', 'test3'], + }, + ], + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_to_cart', + params: { + value: 38, + ...commonProductInfo[0], + address_city: 'kolkata', + address_district: '24pgs', + categoryLevels_0: 'Furniture', + categoryLevels_1: 'Bedroom Furniture', + categoryLevels_2: 'Dressers & Chests', + items: [ + { + item_id: '1234', + productLevels_0: 'test1', + productLevels_1: 'test2', + productLevels_2: 'test3', + product_details_colour: 'red', + product_details_shape: 'rectangle', + }, + ], + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, +]; diff --git a/test/integrations/destinations/ga4/processor/groupTestData.ts b/test/integrations/destinations/ga4/processor/groupTestData.ts new file mode 100644 index 0000000000..68daad3a8e --- /dev/null +++ b/test/integrations/destinations/ga4/processor/groupTestData.ts @@ -0,0 +1,306 @@ +import { defaultMockFns } from '../mocks'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder, generateGroupPayload } from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'GA4', + DestinationDefinition: { + ID: '123', + Name: 'GA4', + DisplayName: 'Google Analytics 4 (GA4)', + Config: {}, + }, + Config: { + apiSecret: 'dummyApiSecret', + measurementId: 'dummyMeasurementId', + firebaseAppId: '', + blockPageViewEvent: false, + typesOfClient: 'gtag', + extendPageViewParams: false, + sendUserId: false, + eventFilteringOption: 'disable', + blacklistedEvents: [ + { + eventName: '', + }, + ], + whitelistedEvents: [ + { + eventName: '', + }, + ], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const deviceInfo = { + adTrackingEnabled: 'false', + advertisingId: 'dummyAdvertisingId', + id: 'device@1', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, +}; + +const commonOutputHeaders = { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', +}; + +const commonOutputParams = { + api_secret: 'dummyApiSecret', + measurement_id: 'dummyMeasurementId', +}; + +const anonymousId = 'dummyAnonId'; +const externalIdWithClientId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, + { + type: 'ga4ClientId', + id: 'dummyClientId', + }, +]; +const sentAt = '2022-04-20T15:20:57Z'; +const originalTimestamp = '2022-04-26T05:17:09Z'; +const timezone = { + name: 'Europe/Tallinn', +}; +const clientId = 'dummyClientId'; +const userId = 'default-user-id'; + +const sessionId = 655; +const engagementTimeMsec = 100; +const non_personalized_ads = true; +const defaultEngagementTimeMsec = 1; +const timestamp_micros = 1650950229000000; + +const eventEndPoint = 'https://www.google-analytics.com/mp/collect'; + +export const groupTestData: ProcessorTestData[] = [ + { + id: 'ga4-group-test-1', + name: 'ga4', + description: 'Group event call with event name', + scenario: 'Business', + successCriteria: 'Response status code should be 200 and event name should be join_group', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateGroupPayload({ + type: 'group', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'join_group', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-group-test-2', + name: 'ga4', + description: 'Group event call with event name and properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and event payload should contain all properties and event name should be join_group', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateGroupPayload({ + type: 'group', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + traits: { + org: 'rudderlabs', + sector: 'cdp', + timezone, + engagementTimeMsec, + sessionId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'join_group', + params: { + sector: 'cdp', + org: 'rudderlabs', + timezone_name: timezone.name, + session_id: sessionId, + engagement_time_msec: engagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-group-test-3', + name: 'ga4', + description: 'Scenario to test firing group calls with GA4 hybrid mode connection', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should containe track event properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, connectionMode: 'hybrid' }, + }, + message: { + type: 'group', + event: 'tutorial complete', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + sessionId: 1683172874065, + }, + integrations: { + 'Google Analytics 4': { + clientId: '4718026.1683606287', + sessionId: '1683606287', + sessionNumber: 1, + }, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: '4718026.1683606287', + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'join_group', + params: { + session_id: '1683606287', + session_number: 1, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, +]; diff --git a/test/integrations/destinations/ga4/processor/pageTestData.ts b/test/integrations/destinations/ga4/processor/pageTestData.ts new file mode 100644 index 0000000000..fa0b187aea --- /dev/null +++ b/test/integrations/destinations/ga4/processor/pageTestData.ts @@ -0,0 +1,304 @@ +import { defaultMockFns } from '../mocks'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + transformResultBuilder, + generatePageOrScreenPayload, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'GA4', + DestinationDefinition: { + ID: '123', + Name: 'GA4', + DisplayName: 'Google Analytics 4 (GA4)', + Config: {}, + }, + Config: { + apiSecret: 'dummyApiSecret', + measurementId: 'dummyMeasurementId', + firebaseAppId: '', + blockPageViewEvent: false, + typesOfClient: 'gtag', + extendPageViewParams: false, + sendUserId: false, + eventFilteringOption: 'disable', + blacklistedEvents: [ + { + eventName: '', + }, + ], + whitelistedEvents: [ + { + eventName: '', + }, + ], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const deviceInfo = { + adTrackingEnabled: 'false', + advertisingId: 'dummyAdvertisingId', + id: 'device@1', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, +}; + +const commonOutputHeaders = { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', +}; + +const commonOutputParams = { + api_secret: 'dummyApiSecret', + measurement_id: 'dummyMeasurementId', +}; + +const anonymousId = 'dummyAnonId'; +const externalId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, +]; +const page = { + url: 'http://morkey.in', + path: '/cart', + title: 'miphone', + search: 'MI', + referrer: 'morkey', +}; +const sentAt = '2022-04-20T15:20:57Z'; +const originalTimestamp = '2022-04-26T05:17:09Z'; + +const userId = 'default-userId'; +const non_personalized_ads = true; +const defaultEngagementTimeMsec = 1; +const timestamp_micros = 1650950229000000; + +const expectedOutputParams = { + page_title: 'miphone', + page_referrer: 'morkey', + page_location: 'http://morkey.in', +}; + +const eventEndPoint = 'https://www.google-analytics.com/mp/collect'; + +export const pageTestData: ProcessorTestData[] = [ + { + id: 'ga4-page-test-1', + name: 'ga4', + description: 'Scenario to test page call', + scenario: 'Business', + successCriteria: 'Response status code should be 200 and event name should be page_view', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generatePageOrScreenPayload( + { + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + originalTimestamp, + }, + 'page', + ), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'page_view', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-page-test-2', + name: 'ga4', + description: 'Scenario to test page call with custom properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and event payload should contain all custom properties event name should be page_view', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generatePageOrScreenPayload( + { + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + properties: { + view: 'login', + }, + originalTimestamp, + }, + 'page', + ), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'page_view', + params: { + view: 'login', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-page-test-3', + name: 'ga4', + description: + 'Scenario to test take page properties from context.page for page call along with custom properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and event payload should contain all custom properties event name should be page_view', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generatePageOrScreenPayload( + { + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + page, + }, + properties: { + id: 'dummyId', + }, + originalTimestamp, + }, + 'page', + ), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'page_view', + params: { + id: 'dummyId', + ...expectedOutputParams, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, +]; diff --git a/test/integrations/destinations/ga4/processor/trackTestData.ts b/test/integrations/destinations/ga4/processor/trackTestData.ts new file mode 100644 index 0000000000..ce991ff845 --- /dev/null +++ b/test/integrations/destinations/ga4/processor/trackTestData.ts @@ -0,0 +1,2951 @@ +import { defaultMockFns } from '../mocks'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedTrackPayload, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'GA4', + DestinationDefinition: { + ID: '123', + Name: 'GA4', + DisplayName: 'Google Analytics 4 (GA4)', + Config: {}, + }, + Config: { + apiSecret: 'dummyApiSecret', + measurementId: 'dummyMeasurementId', + firebaseAppId: '', + blockPageViewEvent: false, + typesOfClient: 'gtag', + extendPageViewParams: false, + sendUserId: false, + eventFilteringOption: 'disable', + blacklistedEvents: [ + { + eventName: '', + }, + ], + whitelistedEvents: [ + { + eventName: '', + }, + ], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const deviceInfo = { + adTrackingEnabled: 'false', + advertisingId: 'dummyAdvertisingId', + id: 'device@1', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, +}; + +const commonOutputHeaders = { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', +}; + +const commonOutputParams = { + api_secret: 'dummyApiSecret', + measurement_id: 'dummyMeasurementId', +}; + +const anonymousId = 'dummyAnonId'; +const externalId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, +]; +const externalIdWithClientId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, + { + type: 'ga4ClientId', + id: 'dummyClientId', + }, +]; +const page = { + initial_referrer: '$direct', + path: '/', + referrer: '$direct', + tab_url: 'https://www.rudderstack.com/', + title: 'Document', + url: 'https://www.rudderstack.com/', +}; +const campaign = { + id: 'google_1234', + name: 'Summer_fun', + source: 'google', + medium: 'cpc', + term: 'summer+travel', + content: 'logo link', +}; +const sentAt = '2022-04-20T15:20:57Z'; +const originalTimestamp = '2022-04-26T05:17:09Z'; + +const clientId = 'dummyClientId'; +const userId = 'default-user-id'; +const groupId = 'dummyGroupId'; +const defaultCurrency = 'USD'; +const value = 7.77; +const total = 10; +const sessionId = 655; +const engagementTimeMsec = 100; +const non_personalized_ads = true; +const defaultEngagementTimeMsec = 1; +const timestamp_micros = 1650950229000000; +const timezone = { + name: 'Europe/Tallinn', +}; + +const expectedCampaignOutputParams = { + campaign_id: 'google_1234', + campaign: 'Summer_fun', + source: 'google', + medium: 'cpc', + term: 'summer+travel', + content: 'logo link', +}; + +const eventEndPoint = 'https://www.google-analytics.com/mp/collect'; + +export const trackTestData: ProcessorTestData[] = [ + { + id: 'ga4-track-test-1', + name: 'ga4', + description: 'Track event call for earn virtual currency event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all earn virtual currency event properties and event name should be earn_virtual_currency', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'earn virtual currency', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + value, + virtual_currency_name: 'Gems', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'earn_virtual_currency', + params: { + value, + virtual_currency_name: 'Gems', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-2', + name: 'ga4', + description: 'Track event call for earn virtual currency without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no earn virtual currency event properties and event name should be earn_virtual_currency', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'earn virtual currency', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'earn_virtual_currency', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-3', + name: 'ga4', + description: 'Track event call for generate_lead event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all generate_lead event properties and event name should be generate_lead', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'generate_lead', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + value, + currency: defaultCurrency, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'generate_lead', + params: { + value, + currency: defaultCurrency, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-4', + name: 'ga4', + description: 'Track event call for level_up event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all level_up event properties and event name should be level_up', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'level_up', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + level: 5, + character: 'Player 1', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'level_up', + params: { + level: 5, + character: 'Player 1', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-5', + name: 'ga4', + description: 'Track event call for level_up without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no level_up event properties and event name should be level_up', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'level_up', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'level_up', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-6', + name: 'ga4', + description: 'Track event call for group event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all group event properties and event name should be group', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'group', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + group_id: groupId, + engagementTimeMsec, + sessionId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'group', + params: { + group_id: groupId, + session_id: sessionId, + engagement_time_msec: engagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-7', + name: 'ga4', + description: 'Track event call for group without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no group event properties and event name should be group', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'group', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'group', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-8', + name: 'ga4', + description: 'Track event call for login event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all login event properties and event name should be login', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'login', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + method: 'Google', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'login', + params: { + method: 'Google', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-9', + name: 'ga4', + description: 'Track event call for login without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no login event properties and event name should be login', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'login', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'login', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-10', + name: 'ga4', + description: 'Track event call for post_score event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all post_score event properties and event name should be post_score', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'post_score', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + score: 10000, + level: 5, + character: 'Player 1', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'post_score', + params: { + score: 10000, + level: 5, + character: 'Player 1', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-11', + name: 'ga4', + description: 'Track event call for post_score event with only required event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain only required post_score event properties and event name should be post_score', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'post_score', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + score: 10000, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'post_score', + params: { + score: 10000, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-12', + name: 'ga4', + description: 'Track event call for select_content event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all select_content event properties and event name should be select_content', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'select_content', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + item_id: 'I_12345', + content_type: 'product', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'select_content', + params: { + item_id: 'I_12345', + content_type: 'product', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-13', + name: 'ga4', + description: 'Track event call for select_content without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no select_content event properties and event name should be select_content', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'select_content', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'select_content', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-14', + name: 'ga4', + description: 'Track event call for sign_up event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all sign_up event properties and event name should be sign_up', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'sign_up', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + method: 'Google', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'sign_up', + params: { + method: 'Google', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-15', + name: 'ga4', + description: 'Track event call for sign_up without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no sign_up event properties and event name should be sign_up', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'sign_up', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'sign_up', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-16', + name: 'ga4', + description: 'Track event call for spend_virtual_currency event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all spend_virtual_currency event properties and event name should be spend_virtual_currency', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'spend_virtual_currency', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + value: 5, + item_name: 'Starter Boost', + virtual_currency_name: 'Gems', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'spend_virtual_currency', + params: { + value: 5, + item_name: 'Starter Boost', + virtual_currency_name: 'Gems', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-17', + name: 'ga4', + description: 'Track event call for spend_virtual_currency without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no spend_virtual_currency event properties and event name should be spend_virtual_currency', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'spend_virtual_currency', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'spend_virtual_currency', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-18', + name: 'ga4', + description: 'Track event call for tutorial_begin without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no tutorial_begin event properties and event name should be tutorial_begin', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'tutorial_begin', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'tutorial_begin', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-19', + name: 'ga4', + description: 'Track event call for tutorial_complete without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no tutorial_complete event properties and event name should be tutorial_complete', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'tutorial_complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'tutorial_complete', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-20', + name: 'ga4', + description: 'Track event call for unlock_achievement event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all unlock_achievement event properties and event name should be unlock_achievement', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'unlock_achievement', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + achievement_id: 'A_12345', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'unlock_achievement', + params: { + achievement_id: 'A_12345', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-21', + name: 'ga4', + description: 'Track event call for rudderstack event event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all rudderstack event event properties and event name should be rudderstack_event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'rudderstack event', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total, + timezone, + engagementTimeMsec, + sessionId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'rudderstack_event', + params: { + total, + timezone_name: timezone.name, + session_id: sessionId, + engagement_time_msec: engagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-22', + name: 'ga4', + description: + 'Track event call for rudderstack event event with all event properties and user properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all rudderstack event event properties and user properties and event name should be rudderstack_event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'rudderstack event', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total, + user_properties: { + price: '19', + }, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + user_properties: { + price: { + value: '19', + }, + }, + events: [ + { + name: 'rudderstack_event', + params: { + total, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-23', + name: 'ga4', + description: 'Scenario to test custom events by passing reserved property names in payload', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all custom event properties and reserved properties should gets filtered from transformed payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'rudderstack event', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total, + firebase_conversion: 'firebase_conversion', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'rudderstack_event', + params: { + total, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-24', + name: 'ga4', + description: + 'Scenario to test custom events by passing reserved properties and reserved user properties in payload', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all custom event properties and reserved properties and reserved user properties should gets filtered from transformed payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'rudderstack event', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total, + firebase_conversion: 'firebase_conversion', + user_properties: { + first_open_time: 'first_open_time', + user_id: userId, + firebase_value: 'firebase_value', + }, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'rudderstack_event', + params: { + total, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-25', + name: 'ga4', + description: 'Track event call for custom event without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no custom event properties and event name should be custom event name', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'rudderstack event', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'rudderstack_event', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-26', + name: 'ga4', + description: 'Track event call for taking client_id from anonymousId', + scenario: 'Business', + successCriteria: 'Response status code should be 200 and client_id should be anonymousId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'tutotial_complete', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-27', + name: 'ga4', + description: 'Track event call for tutorial_complete without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no tutorial_complete event properties and event name should be tutorial_complete', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'tutorial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'tutorial_complete', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-28', + name: 'ga4', + description: + 'Scenario to test client_id is not sent from the path defined in the webapp config, falling back to default values i.e here it is anonymousId', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, client_id value should be equal to anonymousId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'tutorial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'tutorial_complete', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-29', + name: 'ga4', + description: 'Scenario to test extract session_id from context.sessionId', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, session_id value should be equal to context.sessionId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'tutorial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + sessionId: 16678456735, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + non_personalized_ads, + events: [ + { + name: 'tutorial_complete', + params: { + session_id: 16678456735, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-30', + name: 'ga4', + description: 'Scenario to test integer userId', + scenario: 'Business', + successCriteria: 'Response status code should be 200, user_id value should get stringified', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'tutorial complete', + userId: 1234, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + sessionId: 16678456735, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: '1234', + non_personalized_ads, + events: [ + { + name: 'tutorial_complete', + params: { + session_id: 16678456735, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-31', + name: 'ga4', + description: 'Scenario to test login event with user_properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain all user_properties passed with payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'login', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + traits: { + campaign: 'advertizing', + }, + }, + properties: { + method: 'facebook', + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + user_properties: { + campaign: { + value: 'advertizing', + }, + }, + events: [ + { + name: 'login', + params: { + method: 'facebook', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-32', + name: 'ga4', + description: 'Scenario to test track call with page information such as url, title, referrer', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain all page related parameters', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'generate_lead', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + page, + }, + properties: { + value, + source: 'instagram', + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'generate_lead', + params: { + value, + currency: 'USD', + source: 'instagram', + page_location: 'https://www.rudderstack.com/', + page_referrer: '$direct', + page_title: 'Document', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-33', + name: 'ga4', + description: + 'Scenario to test track event with hybrid connection mode using buffer cloud mode event approach', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain track event properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, connectionMode: 'hybrid' }, + }, + message: { + type: 'track', + event: 'generate_lead', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + properties: { + value, + source: 'instagram', + }, + integrations: { + All: true, + 'Google Analytics 4 (GA4)': { + clientId: '554581488.1683172875', + sessionId: '1683172875', + }, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: '554581488.1683172875', + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'generate_lead', + params: { + value, + currency: 'USD', + source: 'instagram', + session_id: '1683172875', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-34', + name: 'ga4', + description: + 'Scenario to test track event with hybrid connection mode using override client_id and session_id approach', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain track event properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, connectionMode: 'hybrid' }, + }, + message: { + type: 'track', + event: 'generate_lead', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + sessionId: 1683172874065, + }, + properties: { + value, + source: 'instagram', + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'generate_lead', + params: { + value, + currency: 'USD', + source: 'instagram', + session_id: 1683172874065, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-35', + name: 'ga4', + description: 'Scenario to test sign_up event with all data types of user_properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain all user_properties sent in payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, connectionMode: 'hybrid' }, + }, + message: { + type: 'track', + event: 'sign_up', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + sessionId: 1683172874065, + traits: { + campaign: 'advertizing', + name: 'rudder', + age: 45, + hobby: ['dancing', 'singing', 'reading'], + enableEURegion: false, + isEnterpriseUser: { + value: false, + }, + }, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + user_properties: { + age: { + value: 45, + }, + name: { + value: 'rudder', + }, + campaign: { + value: 'advertizing', + }, + enableEURegion: { + value: false, + }, + }, + events: [ + { + name: 'sign_up', + params: { + session_id: 1683172874065, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-36', + name: 'ga4', + description: 'Scenario to test event having multiple empty array and object parameters', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and in final response empty array and object should gets filtered out', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'login', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + properties: { + user_interest: 'Moderate', + company_interest: '', + profile: [ + { + is_6qa: true, + product: null, + product_fit: 'Moderate', + product_stage: 'Purchase', + intent_score: 89, + profile_score: 52, + product_display$name: 'rudderstack', + }, + ], + user_company: 'Analytics consulting', + user_account: '1', + user_id_mappings: '330098|245252|461224|282599', + company_naics_6sense: '5173', + usr_consent: null, + firebase_user_id: 'kdgMnP', + google_user_id: 'G-123456', + company_domain: 'consulting.net', + company_region: 'New Zealand', + user_product_interests: { + ids: [], + list: [ + { + id: 330098, + name: [], + }, + { + id: 245252, + name: {}, + }, + ], + names: [], + }, + company_country: {}, + company_industry: 'Business Analytics', + company_revenue: '$5M - $10M', + company_annual_revenue: '5568000', + company_sic_description: '', + company_naics_description: [], + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'login', + params: { + company_annual_revenue: '5568000', + company_domain: 'consulting.net', + company_industry: 'Business Analytics', + company_naics_6sense: '5173', + company_region: 'New Zealand', + company_revenue: '$5M - $10M', + engagement_time_msec: defaultEngagementTimeMsec, + profile_0_intent_score: 89, + profile_0_is_6qa: true, + profile_0_product_display$name: 'rudderstack', + profile_0_product_fit: 'Moderate', + profile_0_product_stage: 'Purchase', + profile_0_profile_score: 52, + user_account: '1', + user_company: 'Analytics consulting', + user_id_mappings: '330098|245252|461224|282599', + user_interest: 'Moderate', + user_product_interests_list_0_id: 330098, + user_product_interests_list_1_id: 245252, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-37', + name: 'ga4', + description: "Teack event call with campaign_details custom event and it's properties", + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and resonse should contain all campaign_details event properties and event name should be campaign_details', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'campaign_details', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + campaign, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'campaign_details', + params: { + ...expectedCampaignOutputParams, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-38', + name: 'ga4', + description: 'Send consents setting to ga4 with login event', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain all consent settings', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'login', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + campaign, + }, + originalTimestamp, + integrations: { + All: true, + GA4: { + consents: { + ad_personalization: 'GRANTED', + ad_user_data: 'GRANTED', + }, + }, + }, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + consent: { + ad_personalization: 'GRANTED', + ad_user_data: 'GRANTED', + }, + events: [ + { + name: 'login', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-39', + name: 'ga4', + description: 'Send invalid consents settings to ga4 with login event', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain only valid consent setting', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'login', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + campaign, + }, + originalTimestamp, + integrations: { + All: true, + GA4: { + consents: { + ad_personalization: 'NOT_SPECIFIED', + ad_user_data: 'DENIED', + }, + }, + }, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + consent: { + ad_user_data: 'DENIED', + }, + events: [ + { + name: 'login', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, +]; diff --git a/test/integrations/destinations/ga4/processor/validationTestData.ts b/test/integrations/destinations/ga4/processor/validationTestData.ts new file mode 100644 index 0000000000..4e5d09bfcf --- /dev/null +++ b/test/integrations/destinations/ga4/processor/validationTestData.ts @@ -0,0 +1,947 @@ +import { defaultMockFns } from '../mocks'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, generateSimplifiedTrackPayload } from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'GA4', + DestinationDefinition: { + ID: '123', + Name: 'GA4', + DisplayName: 'Google Analytics 4 (GA4)', + Config: {}, + }, + Config: { + apiSecret: 'dummyApiSecret', + measurementId: 'dummyMeasurementId', + firebaseAppId: '', + blockPageViewEvent: false, + typesOfClient: 'gtag', + extendPageViewParams: false, + sendUserId: false, + eventFilteringOption: 'disable', + blacklistedEvents: [ + { + eventName: '', + }, + ], + whitelistedEvents: [ + { + eventName: '', + }, + ], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const deviceInfo = { + adTrackingEnabled: 'false', + advertisingId: 'dummyAdvertisingId', + id: 'device@1', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, +}; + +const anonymousId = 'dummyAnonId'; +const userId = 'default-user-id'; +const externalId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, +]; +const externalIdWithClientId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, + { + type: 'ga4ClientId', + id: 'dummyClientId', + }, +]; +const sentAt = '2022-04-20T15:20:57Z'; +const originalTimestamp = '2022-04-26T05:17:09Z'; + +const commonProductInfo = [ + { + coupon: 'SUMMER_FUN', + category: 'Apparel', + brand: 'Google', + variant: 'green', + price: '19', + quantity: '2', + position: '1', + affiliation: 'Google Merchandise Store', + currency: 'USD', + discount: 2.22, + item_category2: 'Adult', + item_category3: 'Shirts', + item_category4: 'Crew', + item_category5: 'Short sleeve', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + }, +]; + +const reservedEventsErrorMessage = 'track:: Reserved event names are not allowed'; +const expectedStatTags = { + destType: 'GA4', + destinationId: 'default-destinationId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const validationTestData: ProcessorTestData[] = [ + { + id: 'ga4-validation-test-1', + name: 'ga4', + description: 'Scenario to test reserved event name to GA4', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'ad_click', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total: 10, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: reservedEventsErrorMessage, + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-2', + name: 'ga4', + description: 'Scenario to test reserved event name along with reserved event properties to GA4', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'app_remove', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + firebase_conversion: 'firebase_conversion', + google_id: 'ga_id', + ga_value: 'ga_value', + value: 10, + user_properties: { + first_open_time: 'first_open_time', + user_id: userId, + firebase_value: 'firebase_value', + }, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: reservedEventsErrorMessage, + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-3', + name: 'ga4', + description: 'Scenario to test reserved prefix names to GA4', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'firebase_event1', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + value: 10, + google_id: 'ga_id', + ga_value: 'ga_value', + firebase_conversion: 'firebase_conversion', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Reserved custom prefix names are not allowed', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-4', + name: 'ga4', + description: + 'Scenario to pass event name to GA4 with missing fields i.e required in products: [..]', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product added', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total: 7.77, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'One of product_id or name is required', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-5', + name: 'ga4', + description: + 'Scenario to pass event name to GA4 with missing fields i.e required in products: [..]', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'Product Viewed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total: 7.77, + currency: 'USD', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'One of product_id or name is required', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-6', + name: 'ga4', + description: 'Scenario to test missing API Secret', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw configuration error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, apiSecret: null }, + Enabled: true, + }, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'API Secret not found. Aborting ', + statTags: { ...expectedStatTags, errorType: 'configuration' }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-7', + name: 'ga4', + description: 'Scenario to test missing measurementId', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw configuration error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, measurementId: null }, + Enabled: true, + }, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'measurementId must be provided. Aborting', + statTags: { ...expectedStatTags, errorType: 'configuration' }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-8', + name: 'ga4', + description: 'Scenario to test missing message type', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Message Type is not present. Aborting message.', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-9', + name: 'ga4', + description: 'Scenario to test missing message event name', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Event name is required', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-10', + name: 'ga4', + description: 'Scenario to test missing ga4AppInstanceId for firebase client type', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { + ...destination.Config, + typesOfClient: 'firebase', + firebaseAppId: 'dummyFirebaseAppId', + }, + }, + message: { + type: 'track', + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'ga4AppInstanceId must be provided under externalId', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-11', + name: 'ga4', + description: 'Scenario to test reserved event name for firebase client type', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { + ...destination.Config, + typesOfClient: 'firebase', + firebaseAppId: 'dummyFirebaseAppId', + }, + }, + message: { + type: 'track', + event: 'app_store_subscription_cancel', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Reserved custom event names are not allowed', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-12', + name: 'ga4', + description: 'Scenario to test missing firebaseAppId for firebase client type', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw configuration error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { + ...destination.Config, + typesOfClient: 'firebase', + firebaseAppId: null, + }, + }, + message: { + type: 'track', + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'firebaseAppId must be provided. Aborting', + statTags: { ...expectedStatTags, errorType: 'configuration' }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-13', + name: 'ga4', + description: 'Scenario to test timestamp more than 72 hours into the past', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp: '2022-04-20T05:17:09Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Allowed timestamp is [72 hours] into the past', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'ga4-validation-test-14', + name: 'ga4', + description: 'Scenario to test timestamp more than 15 min into the future', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp: '2022-05-05T15:47:57Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Allowed timestamp is [15 minutes] into the future', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'ga4-validation-test-15', + name: 'ga4', + description: 'Scenario to test event value other then string', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: { + name: 'promotion_viewed', + }, + properties: { + products: commonProductInfo, + }, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp: '2022-05-05T15:47:57Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'track:: event name should be string', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'ga4-validation-test-16', + name: 'ga4', + description: 'Scenario to test client_id not found in payload', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw configuration error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'product added', + properties: { + products: commonProductInfo, + }, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'ga4ClientId, anonymousId or messageId must be provided', + statTags: { ...expectedStatTags, errorType: 'configuration' }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'ga4-validation-test-17', + name: 'ga4', + description: 'Scenario to test event name starts with numbers', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: '1234_sign_up', + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Event name must start with a letter and can only contain letters, numbers, and underscores', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/testTypes.ts b/test/integrations/testTypes.ts index 1c5a989f44..3df732d84f 100644 --- a/test/integrations/testTypes.ts +++ b/test/integrations/testTypes.ts @@ -78,6 +78,7 @@ export type ProcessorTestData = { body: ProcessorTransformationResponse[]; }; }; + mockFns?: (mockAdapter: MockAdapter) => {}; }; export type RouterTestData = { id: string; diff --git a/test/integrations/testUtils.ts b/test/integrations/testUtils.ts index 0a2727f4d0..694f224859 100644 --- a/test/integrations/testUtils.ts +++ b/test/integrations/testUtils.ts @@ -243,6 +243,7 @@ export const generateSimplifiedTrackPayload: any = (parametersOverride: any) => context: removeUndefinedAndNullValues({ externalId: parametersOverride.context.externalId, traits: parametersOverride.context.traits, + device: parametersOverride.context.device, }), anonymousId: parametersOverride.anonymousId || 'default-anonymousId', originalTimestamp: parametersOverride.originalTimestamp || '2021-01-03T17:02:53.193Z', @@ -286,6 +287,7 @@ export const generatePageOrScreenPayload: any = (parametersOverride: any, eventT screen: { density: 2, }, + page: parametersOverride.context.page, traits: parametersOverride.context.traits, externalId: parametersOverride.externalId, userAgent: @@ -351,8 +353,9 @@ export const generateGroupPayload: any = (parametersOverride: any) => { screen: { density: 2, }, + device: parametersOverride.context.device, traits: parametersOverride.context.traits, - externalId: parametersOverride.externalId, + externalId: parametersOverride.context.externalId, }), messageId: parametersOverride.messageId || generateAlphanumericId(36), session_id: parametersOverride.session_id || generateAlphanumericId(36), From 31e6460ccc0c18014ebf67eab23b59abe5d81ef6 Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Wed, 15 May 2024 18:55:50 +0530 Subject: [PATCH 159/240] fix: minor mapping issue in conversions --- src/cdk/v2/destinations/koddi/data/ConversionsConfig.json | 2 +- src/cdk/v2/destinations/koddi/utils.test.js | 2 +- test/integrations/destinations/koddi/processor/conversions.ts | 2 +- test/integrations/destinations/koddi/router/data.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json b/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json index 0d49e39c32..495574f198 100644 --- a/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json +++ b/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json @@ -25,7 +25,7 @@ "sourceKeys": "userId", "sourceFromGenericMap": true, "required": true, - "destKey": "userGuid" + "destKey": "user_guid" }, { "sourceKeys": "context.device.type", diff --git a/src/cdk/v2/destinations/koddi/utils.test.js b/src/cdk/v2/destinations/koddi/utils.test.js index ed12ecd5dd..59a02b62a0 100644 --- a/src/cdk/v2/destinations/koddi/utils.test.js +++ b/src/cdk/v2/destinations/koddi/utils.test.js @@ -265,7 +265,7 @@ describe('constructFullPayload', () => { currency: 'USD', transaction_id: '123', unixtime: 1709405952, - userGuid: '1234', + user_guid: '1234', user_ip: '127.0.0.1', bidders: [ { diff --git a/test/integrations/destinations/koddi/processor/conversions.ts b/test/integrations/destinations/koddi/processor/conversions.ts index 35d1670b95..7c8494d258 100644 --- a/test/integrations/destinations/koddi/processor/conversions.ts +++ b/test/integrations/destinations/koddi/processor/conversions.ts @@ -70,7 +70,7 @@ export const conversions: ProcessorTestData[] = [ currency: 'USD', transaction_id: 'ABC123', unixtime: 1709566376, - userGuid: 'userId123', + user_guid: 'userId123', user_ip: '127.0.0.1', bidders: bidders, }, diff --git a/test/integrations/destinations/koddi/router/data.ts b/test/integrations/destinations/koddi/router/data.ts index f5fae9bfd4..85f1319e39 100644 --- a/test/integrations/destinations/koddi/router/data.ts +++ b/test/integrations/destinations/koddi/router/data.ts @@ -202,7 +202,7 @@ export const data = [ currency: 'USD', transaction_id: 'ABC123', unixtime: 1709566376, - userGuid: 'userId123', + user_guid: 'userId123', user_ip: '127.0.0.1', bidders: bidders, }, From 71c3d46236fff9209625cfb0737c21db2d275345 Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Fri, 17 May 2024 13:40:39 +0530 Subject: [PATCH 160/240] fix: move af_currency outside properties in eventValue (#3316) * fix: move af_currency outside properties in eventValue * chore: add af_currency to eventValue root via config --------- Co-authored-by: Sankeerth --- src/v0/destinations/af/transform.js | 17 ++++++++--------- .../destinations/af/processor/data.ts | 3 ++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/v0/destinations/af/transform.js b/src/v0/destinations/af/transform.js index d6c41937a1..72ba47a227 100644 --- a/src/v0/destinations/af/transform.js +++ b/src/v0/destinations/af/transform.js @@ -113,13 +113,9 @@ function getEventValueForUnIdentifiedTrackEvent(message) { return { eventValue }; } -function getEventValueMapFromMappingJson( - message, - mappingJson, - isMultiSupport, - addPropertiesAtRoot, -) { +function getEventValueMapFromMappingJson(message, mappingJson, isMultiSupport, config) { let eventValue = {}; + const { addPropertiesAtRoot, afCurrencyAtRoot } = config; if (addPropertiesAtRoot) { eventValue = message.properties; @@ -152,6 +148,9 @@ function getEventValueMapFromMappingJson( af_price: prices, }; } + if (afCurrencyAtRoot) { + eventValue.af_currency = message.properties.currency; + } eventValue = removeUndefinedValues(eventValue); if (Object.keys(eventValue).length > 0) { eventValue = JSON.stringify(eventValue); @@ -171,7 +170,7 @@ function processNonTrackEvents(message, eventName) { return payload; } -function processEventTypeTrack(message, addPropertiesAtRoot) { +function processEventTypeTrack(message, config) { let isMultiSupport = true; const evType = message.event && message.event.toLowerCase(); let category = ConfigCategory.DEFAULT; @@ -195,7 +194,7 @@ function processEventTypeTrack(message, addPropertiesAtRoot) { message, mappingConfig[category.name], isMultiSupport, - addPropertiesAtRoot, + config, ); payload.eventName = message.event; payload.eventCurrency = message?.properties?.currency; @@ -208,7 +207,7 @@ function processSingleMessage(message, destination) { let payload; switch (messageType) { case EventType.TRACK: { - payload = processEventTypeTrack(message, destination.Config.addPropertiesAtRoot); + payload = processEventTypeTrack(message, destination.Config); break; } case EventType.SCREEN: { diff --git a/test/integrations/destinations/af/processor/data.ts b/test/integrations/destinations/af/processor/data.ts index d0fd29b089..210f04331d 100644 --- a/test/integrations/destinations/af/processor/data.ts +++ b/test/integrations/destinations/af/processor/data.ts @@ -1057,6 +1057,7 @@ export const data = [ groupValueTrait: 'age', trackProductsOnce: false, trackRevenuePerProduct: false, + afCurrencyAtRoot: true, }, }, }, @@ -1079,7 +1080,7 @@ export const data = [ body: { JSON: { eventValue: - '{"properties":{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"sku":"45790-32","url":"https://www.example.com/product/path","name":"Monopoly: 3rd Edition","price":19,"category":"Games","quantity":1,"image_url":"https:///www.example.com/product/path.jpg","product_id":"507f1f77bcf86cd799439011"},{"sku":"46493-32","name":"Uno Card Game","price":3,"category":"Games","quantity":2,"product_id":"505bd76785ebb509fc183733"}],"shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f"},"af_revenue":48,"af_quantity":2,"af_price":25}', + '{"properties":{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"sku":"45790-32","url":"https://www.example.com/product/path","name":"Monopoly: 3rd Edition","price":19,"category":"Games","quantity":1,"image_url":"https:///www.example.com/product/path.jpg","product_id":"507f1f77bcf86cd799439011"},{"sku":"46493-32","name":"Uno Card Game","price":3,"category":"Games","quantity":2,"product_id":"505bd76785ebb509fc183733"}],"shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f"},"af_revenue":48,"af_quantity":2,"af_price":25,"af_currency":"ZAR"}', eventName: 'normal track event', eventTime: '2020-08-14T05:30:30.118Z', eventCurrency: 'ZAR', From e7678cbdae4c06449ea9352ce3db390d2a29da14 Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Fri, 17 May 2024 15:51:31 +0530 Subject: [PATCH 161/240] fix: gaoc store sales batching transform contract (#3384) --- .../transform.js | 3 +- .../router/data.ts | 283 ++++++++++++++++++ 2 files changed, 284 insertions(+), 2 deletions(-) diff --git a/src/v0/destinations/google_adwords_offline_conversions/transform.js b/src/v0/destinations/google_adwords_offline_conversions/transform.js index c3be0f7cab..76b12587cd 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/transform.js +++ b/src/v0/destinations/google_adwords_offline_conversions/transform.js @@ -170,9 +170,8 @@ const batchEvents = (storeSalesEvents) => { storeSalesEvent.message?.body?.JSON?.addConversionPayload?.operations, ); batchEventResponse.metadatas.push(storeSalesEvent.metadata); - batchEventResponse.destination = storeSalesEvent.destination; }); - + batchEventResponse.destination = storeSalesEvents[0].destination; return [ getSuccessRespEvents( batchEventResponse.batchedRequest, diff --git a/test/integrations/destinations/google_adwords_offline_conversions/router/data.ts b/test/integrations/destinations/google_adwords_offline_conversions/router/data.ts index 596e7550e5..9d1ba220c8 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/router/data.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/router/data.ts @@ -999,4 +999,287 @@ export const data = [ }, mockFns: timestampMock, }, + { + name: 'google_adwords_offline_conversions', + description: 'Test 1 should include destination when single store sales event is sent', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2024-05-09T00:02:48.319Z', + userId: '7fe0de4847f6dafb0cba694ef725404a', + channel: 'sources', + context: { + banner: { + key: 'CS', + domain: 'www.champssports.com', + }, + traits: { + email: 'johnwick@gmail.com', + address: { + city: 'homestead', + state: 'fl', + country: 'usa', + postalCode: '33032', + }, + lastName: 'wick', + firstName: 'john', + }, + privacy: { + ccpa: true, + }, + sources: { + job_id: '123344545565466', + version: 'v1.48.7', + job_run_id: 'cou1407gdjb6rkrrtv5g', + task_run_id: 'cou1407gdjb6rkrrtv6g', + }, + snowflake: { + ID: '44acd2006efb6b7d1a0eaf0da2b05b69', + TAX: 8.05, + NAME: 'johnwick', + PHONE: '', + TOTAL: 115, + email: 'JONBOBBYwick@GMAIL.COM', + BANNER: 'CS', + COUPON: '[null]', + REVENUE: 123.05, + CATEGORY: 'Retail', + CURRENCY: 'USD', + DISCOUNT: 0, + LASTNAME: 'wick', + ORDER_ID: '12343-4886-294995', + PRODUCTS: + '[{"sku":"C2302100","product_id":816827,"category":"1","size":"10.5","name":"NIKE AF1 \'07 AN21-WH/BK","brand":"NIKE INC","variant":"WHITE/BLACK","class":"MENS","price":"115.0","division":"CASUAL-ATHLETIC","quantity":"1","discountFlag":"false"}]', + RUDDERID: 'UNAVAILABLE', + SHIPPING: 'n/a', + STORE_ID: '14540', + SUBTOTAL: 115, + FIRSTNAME: 'john', + MESSAGEID: 'UNAVAILABLE', + TIMESTAMP: '2024-05-07T17:27:28.262Z', + TOTAL_VAT: 123.05, + EVENT_DATE: '2024-05-07T00:00:00Z', + STORE_NAME: 'CHAMPS ', + DISCOUNT_VAT: 0, + IS_E_RECEIPT: '1', + SUBTOTAL_VAT: 123.05, + USER_ADDRESS: '', + FLX_CARDNUMBER: '99000002697409', + PAYMENT_METHOD: null, + ACCOUNT_ADDRESS: null, + CM_PHONE_NUMBER: '7868007626', + SHIPPING_METHOD: 'n/a', + STORE_ADDR_CITY: 'CUTLER BAY ', + CM_BILL_ADDRESS1: '13020 SW 256TH ST', + STORE_ADDR_STATE: 'FL', + STORE_ADDR_STREET: + 'SOUTHLAND MALL 20505 SOUTH DIXIE HWY SPACE 1401 ', + STORE_ADDR_COUNTRY: 'UNITED STATES', + STORE_ADDR_ZIPCODE: '33189 ', + ACCOUNT_ADDRESS_CITY: 'HOMESTEAD', + BILLING_ADDRESS_CITY: 'HOMESTEAD', + SHIP_TO_ADDRESS_CITY: 'UNAVAILABLE', + ACCOUNT_ADDRESS_STATE: 'FL', + BILLING_ADDRESS_STATE: 'FL', + SHIP_TO_ADDRESS_STATE: 'UNAVAILABLE', + SHIP_TO_ADDRESS_STREET: 'UNAVAILABLE', + ACCOUNT_ADDRESS_COUNTRY: 'US', + BILLING_ADDRESS_COUNTRY: 'USA', + SHIP_TO_ADDRESS_COUNTRY: 'UNAVAILABLE', + SHIP_TO_ADDRESS_POSTALCODE: 'UNAVAILABLE', + ACCOUNT_ADDRESS_POSTAL_CODE: '33032', + BILLING_ADDRESS_POSTAL_CODE: '33032', + }, + account_id: 'xxxxxxxxxx', + account_mcc: '1234556775', + }, + recordId: '1230', + rudderId: '35d5060a-2756-45d1-9808-cae9aec19166', + messageId: '23d5060b-2756-45c1-9108-c229aec19126', + timestamp: '2024-05-07 17:27:28-00:00', + properties: { + value: 123.05, + currency: 'USD', + order_id: '12343-4886-294995', + products: [ + { + sku: 'C2302100', + price: 115, + quantity: '1', + }, + ], + conversionDateTime: '2024-05-07 17:27:28-00:00', + }, + receivedAt: '2024-05-09T00:02:43.864Z', + request_ip: '10.7.208.179', + originalTimestamp: '2024-05-09T00:02:48.319Z', + }, + metadata: { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + jobId: 1, + userId: 'u1', + }, + destination: { + Config: { + // customerId: '962-581-2972', + customerId: '{{ event.context.account_mcc || "1234556775" }}', + subAccount: false, + loginCustomerId: '{{ event.context.account_mcc || "1234556775" }}', + eventsToOfflineConversionsTypeMapping: [ + { + from: 'Order Completed', + to: 'store', + }, + ], + eventsToConversionsNamesMapping: [ + { + from: 'Order Completed', + to: 'Store sales', + }, + ], + UserIdentifierSource: 'FIRST_PARTY', + conversionEnvironment: 'none', + defaultUserIdentifier: 'email', + hashUserIdentifier: true, + validateOnly: false, + oneTrustCookieCategories: [], + eventDelivery: false, + eventDeliveryTS: 1715104236592, + rudderAccountId: '25u5whFH7gVTnCiAjn4ykoCLGoC', + }, + }, + }, + ], + destType: 'google_adwords_offline_conversions', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://googleads.googleapis.com/v16/customers/1234556775/offlineUserDataJobs', + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + }, + params: { event: 'Store sales', customerId: '1234556775' }, + body: { + JSON: { + event: '1234556775', + isStoreConversion: true, + createJobPayload: { + job: { + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + storeSalesMetadata: { + loyaltyFraction: '1', + transaction_upload_fraction: '1', + }, + }, + }, + addConversionPayload: { + operations: [ + { + create: { + transaction_attribute: { + transaction_amount_micros: '123050000', + order_id: '12343-4886-294995', + currency_code: 'USD', + transaction_date_time: '2019-10-14 16:45:18+05:30', + }, + userIdentifiers: [ + { + hashedEmail: + 'cd54e8f2e90e2a092a153f7e27e7b47a8ad29adb7943a05d749f0f9836935a2f', + }, + ], + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, + }, + }, + ], + enable_partial_failure: false, + enable_warnings: false, + validate_only: false, + }, + executeJobPayload: { validate_only: false }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [ + { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + jobId: 1, + userId: 'u1', + }, + ], + destination: { + Config: { + // customerId: '962-581-2972', + customerId: '1234556775', + subAccount: false, + loginCustomerId: '1234556775', + eventsToOfflineConversionsTypeMapping: [ + { + from: 'Order Completed', + to: 'store', + }, + ], + eventsToConversionsNamesMapping: [ + { + from: 'Order Completed', + to: 'Store sales', + }, + ], + UserIdentifierSource: 'FIRST_PARTY', + conversionEnvironment: 'none', + defaultUserIdentifier: 'email', + hashUserIdentifier: true, + validateOnly: false, + oneTrustCookieCategories: [], + eventDelivery: false, + eventDeliveryTS: 1715104236592, + rudderAccountId: '25u5whFH7gVTnCiAjn4ykoCLGoC', + }, + }, + batched: true, + statusCode: 200, + }, + ], + }, + }, + }, + mockFns: timestampMock, + }, ]; From e9409fde6063d7eaa8558396b85b5fdf99f964e1 Mon Sep 17 00:00:00 2001 From: Paul K <59660521+zenpaul@users.noreply.github.com> Date: Mon, 20 May 2024 01:22:48 -0500 Subject: [PATCH 162/240] feat(integrations/auth0): include Auth0 event type in Rudderstack message (#3370) Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> --- src/v0/sources/auth0/mapping.json | 4 +++ test/integrations/sources/auth0/data.ts | 33 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/v0/sources/auth0/mapping.json b/src/v0/sources/auth0/mapping.json index 45dcf939ad..bc5869a19b 100644 --- a/src/v0/sources/auth0/mapping.json +++ b/src/v0/sources/auth0/mapping.json @@ -62,5 +62,9 @@ { "sourceKeys": "date", "destKeys": ["originalTimestamp", "sentAt"] + }, + { + "sourceKeys": "type", + "destKeys": "source_type" } ] diff --git a/test/integrations/sources/auth0/data.ts b/test/integrations/sources/auth0/data.ts index 66aab00182..b115f0e05e 100644 --- a/test/integrations/sources/auth0/data.ts +++ b/test/integrations/sources/auth0/data.ts @@ -254,6 +254,7 @@ export const data = [ batch: [ { type: 'identify', + source_type: 'ss', sentAt: '2022-10-31T05:57:06.859Z', traits: { connection: 'Username-Password-Authentication', @@ -304,6 +305,7 @@ export const data = [ batch: [ { type: 'track', + source_type: 'sapi', event: 'Success API Operation', sentAt: '2022-10-31T05:57:06.874Z', userId: 'auth0|dummyPassword', @@ -594,6 +596,7 @@ export const data = [ batch: [ { type: 'group', + source_type: 'sapi', sentAt: '2022-10-31T06:09:59.135Z', userId: 'google-oauth2|123456', anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', @@ -936,6 +939,7 @@ export const data = [ batch: [ { type: 'track', + source_type: 'sapi', event: 'Success API Operation', sentAt: '2022-10-31T06:15:25.201Z', userId: 'google-oauth2|123456', @@ -1157,6 +1161,7 @@ export const data = [ batch: [ { type: 'track', + source_type: 'gd_tenant_update', event: 'Guardian tenant update', sentAt: '2022-10-31T06:15:25.196Z', userId: 'google-oauth2|123456', @@ -1285,6 +1290,7 @@ export const data = [ batch: [ { type: 'identify', + source_type: 'ss', sentAt: '2022-10-31T05:57:06.859Z', traits: { connection: 'Username-Password-Authentication', @@ -1404,6 +1410,7 @@ export const data = [ batch: [ { type: 'identify', + source_type: 'ss', userId: '', anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', sentAt: '2022-10-31T05:57:06.859Z', @@ -1454,6 +1461,7 @@ export const data = [ batch: [ { type: 'track', + source_type: 'sapi', event: 'Success API Operation', sentAt: '2022-10-31T05:57:06.874Z', anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', @@ -1490,4 +1498,29 @@ export const data = [ defaultMockFns(); }, }, + { + name: 'auth0', + description: 'empty batch', + module: 'source', + version: 'v0', + input: { + request: { + body: [], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, ]; From fbb0811aa0e417b0cffcea4ecc103979afccfe74 Mon Sep 17 00:00:00 2001 From: Sudip Paul <67197965+ItsSudip@users.noreply.github.com> Date: Mon, 20 May 2024 12:57:31 +0530 Subject: [PATCH 163/240] fix: remove default traits from ortto (#3389) --- src/cdk/v2/destinations/ortto/procWorkflow.yaml | 6 +++--- .../destinations/ortto/processor/data.ts | 12 ++---------- test/integrations/destinations/ortto/router/data.ts | 7 +------ 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/src/cdk/v2/destinations/ortto/procWorkflow.yaml b/src/cdk/v2/destinations/ortto/procWorkflow.yaml index a8fb9b2984..27c3749cc8 100644 --- a/src/cdk/v2/destinations/ortto/procWorkflow.yaml +++ b/src/cdk/v2/destinations/ortto/procWorkflow.yaml @@ -48,9 +48,9 @@ steps: "str::ei": {{{{$.getGenericPaths("userId")}}}}, "str::language": .context.traits.language || .context.locale, "phn::phone": phone ? {"n": phone}, - "bol::gdpr": .context.traits.gdpr ?? true, - "bol::p": .context.traits.emailConsent || false, - "bol::sp": .context.traits.smsConsent || false, + "bol::gdpr": .context.traits.gdpr, + "bol::p": .context.traits.emailConsent, + "bol::sp": .context.traits.smsConsent, }, "location": {"source_ip": .context.ip} }); diff --git a/test/integrations/destinations/ortto/processor/data.ts b/test/integrations/destinations/ortto/processor/data.ts index 0afdb05318..ff84f5dbbd 100644 --- a/test/integrations/destinations/ortto/processor/data.ts +++ b/test/integrations/destinations/ortto/processor/data.ts @@ -158,6 +158,8 @@ export const data = [ people: [ { fields: { + 'bol::p': true, + 'bol::sp': true, 'str::first': 'John', 'str::last': 'Sparrow', 'str::email': 'identify@test.com', @@ -172,9 +174,6 @@ export const data = [ 'phn::phone': { n: '9112340345', }, - 'bol::gdpr': true, - 'bol::p': true, - 'bol::sp': true, 'str:cm:ortto-attirbute0': 'abc', 'str:cm:ortto-attirbute1': 'def', }, @@ -1036,8 +1035,6 @@ export const data = [ n: '9112340345', }, 'bol::gdpr': false, - 'bol::p': false, - 'bol::sp': false, 'str:cm:ortto-attirbute0': 'abc', 'str:cm:ortto-attirbute1': 'def', }, @@ -1250,8 +1247,6 @@ export const data = [ 'str::language': 'en-US', 'str::ei': 'sample_user_id', 'bol::gdpr': false, - 'bol::p': false, - 'bol::sp': false, 'str:cm:ortto-attirbute0': 'abc', 'str:cm:ortto-attirbute1': 'def', }, @@ -1623,9 +1618,6 @@ export const data = [ 'geo::region': {}, 'str::ei': 'XxXxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx', 'str::language': 'en-MU', - 'bol::gdpr': true, - 'bol::p': false, - 'bol::sp': false, }, activity_id: 'act:cm:liked-a-set', attributes: { diff --git a/test/integrations/destinations/ortto/router/data.ts b/test/integrations/destinations/ortto/router/data.ts index cf5731be80..8999637a66 100644 --- a/test/integrations/destinations/ortto/router/data.ts +++ b/test/integrations/destinations/ortto/router/data.ts @@ -298,9 +298,6 @@ export const data = [ 'phn::phone': { n: '9112340345', }, - 'bol::gdpr': true, - 'bol::p': false, - 'bol::sp': false, 'str:cm:ortto-attirbute0': 'abc', 'str:cm:ortto-attirbute1': 'def', }, @@ -385,6 +382,7 @@ export const data = [ activities: [ { fields: { + 'bol::gdpr': false, 'str::first': 'John', 'str::last': 'Sparrow', 'str::email': 'identify@test.com', @@ -404,9 +402,6 @@ export const data = [ 'phn::phone': { n: '9112340345', }, - 'bol::gdpr': false, - 'bol::p': false, - 'bol::sp': false, 'str:cm:ortto-attirbute0': 'abc', 'str:cm:ortto-attirbute1': 'def', }, From 755073c4341a454785050d835021d9f17e0b9d3f Mon Sep 17 00:00:00 2001 From: Sudip Paul <67197965+ItsSudip@users.noreply.github.com> Date: Mon, 20 May 2024 13:30:31 +0530 Subject: [PATCH 164/240] fix: add validation for null/undefined traits in slack (#3382) --- src/v0/destinations/slack/util.js | 2 +- .../destinations/slack/processor/data.ts | 155 ++++++++++++++++++ 2 files changed, 156 insertions(+), 1 deletion(-) diff --git a/src/v0/destinations/slack/util.js b/src/v0/destinations/slack/util.js index f5d407018b..2267aa0bcd 100644 --- a/src/v0/destinations/slack/util.js +++ b/src/v0/destinations/slack/util.js @@ -83,7 +83,7 @@ const buildDefaultTraitTemplate = (traitsList, traits, template) => { generatedStringFromTemplate += `${trait}: {{"${trait}"}} `; }); // else with all traits - if (traitsList.length === 0) { + if (traitsList.length === 0 && !!traits) { Object.keys(traits).forEach((traitKey) => { generatedStringFromTemplate += `${traitKey}: {{"${traitKey}"}} `; }); diff --git a/test/integrations/destinations/slack/processor/data.ts b/test/integrations/destinations/slack/processor/data.ts index 7deb555fa9..1fcbb2ca03 100644 --- a/test/integrations/destinations/slack/processor/data.ts +++ b/test/integrations/destinations/slack/processor/data.ts @@ -2017,4 +2017,159 @@ export const data = [ }, }, }, + { + name: 'slack', + description: + 'Test 12-> Identify -> Default template with some whiteListed traits and traits as null', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ID: '1ZQVSU9SXNg6KYgZALaqjAO3PIL', + Name: 'test-slack', + DestinationDefinition: { + ID: '1ZQUiJVMlmF7lfsdfXg7KXQnlLV', + Name: 'SLACK', + DisplayName: 'Slack', + Config: { + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + eventChannelSettings: [ + { + eventChannelWebhook: 'https://hooks.slack.com/services/example/test/demo', + eventName: 'is', + eventRegex: true, + }, + ], + eventTemplateSettings: [ + { + eventName: 'is', + eventRegex: true, + eventTemplate: + '{{name}} performed {{event}} with {{properties.key1}} {{properties.key2}}', + }, + { + eventName: '', + eventRegex: false, + eventTemplate: '', + }, + ], + webhookUrl: 'https://hooks.slack.com/services/THZM86VSS/BV9HZ2UN6/demo', + whitelistedTraitsSettings: [], + }, + Enabled: true, + Transformations: [], + IsProcessorEnabled: true, + }, + message: { + anonymousId: '4de817fb-7f8e-4e23-b9be-f6736dbda20f', + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.1.1-rc.1', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.1.1-rc.1', + }, + locale: 'en-US', + os: { + name: '', + version: '', + }, + page: { + path: '/tests/html/script-test.html', + referrer: 'http://localhost:1111/tests/html/', + search: '', + title: '', + url: 'http://localhost:1111/tests/html/script-test.html', + }, + screen: { + density: 1.7999999523162842, + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36', + }, + integrations: { + All: true, + }, + messageId: '9ecc0183-89ed-48bd-87eb-b2d8e1ca6780', + originalTimestamp: '2020-03-23T03:46:30.916Z', + properties: { + path: '/tests/html/script-test.html', + referrer: 'http://localhost:1111/tests/html/', + search: '', + title: '', + url: 'http://localhost:1111/tests/html/script-test.html', + }, + receivedAt: '2020-03-23T09:16:31.041+05:30', + request_ip: '[::1]:52056', + sentAt: '2020-03-23T03:46:30.916Z', + timestamp: '2020-03-23T09:16:31.041+05:30', + type: 'identify', + userId: '12345', + }, + metadata: { + anonymousId: '4de817fb-7f8e-4e23-b9be-f6736dbda20f', + destinationId: '1ZQVSU9SXNg6KYgZALaqjAO3PIL', + destinationType: 'SLACK', + jobId: 126, + messageId: '9ecc0183-89ed-48bd-87eb-b2d8e1ca6780', + sourceId: '1YhwKyDcKstudlGxkeN5p2wgsrp', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://hooks.slack.com/services/THZM86VSS/BV9HZ2UN6/demo', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + params: {}, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: { + payload: + '{"text":"Identified User 12345","username":"RudderStack","icon_url":"https://cdn.rudderlabs.com/rudderstack.png"}', + }, + }, + files: {}, + userId: '12345', + statusCode: 200, + }, + metadata: { + anonymousId: '4de817fb-7f8e-4e23-b9be-f6736dbda20f', + destinationId: '1ZQVSU9SXNg6KYgZALaqjAO3PIL', + destinationType: 'SLACK', + jobId: 126, + messageId: '9ecc0183-89ed-48bd-87eb-b2d8e1ca6780', + sourceId: '1YhwKyDcKstudlGxkeN5p2wgsrp', + }, + statusCode: 200, + }, + ], + }, + }, + }, ]; From 3ad78506446915ada8bdc5f5594dc2710e6b0646 Mon Sep 17 00:00:00 2001 From: Sudip Paul <67197965+ItsSudip@users.noreply.github.com> Date: Mon, 20 May 2024 13:59:11 +0530 Subject: [PATCH 165/240] fix: update validation of event duration (#3376) * fix: update validation of event duration * chore: address comment * chore: add test case --- .../facebook_conversions/transform.js | 16 +--- .../destinations/facebook_pixel/transform.js | 21 +---- src/v0/destinations/facebook_pixel/utils.js | 26 ++++++ .../destinations/facebook_pixel/utils.test.js | 79 +++++++++++++++- .../facebook_conversions/processor/data.ts | 2 +- .../processor/validationTestData.ts | 90 ++++++++++++++++++- 6 files changed, 198 insertions(+), 36 deletions(-) diff --git a/src/v0/destinations/facebook_conversions/transform.js b/src/v0/destinations/facebook_conversions/transform.js index 1bb97b2672..e4aee9c503 100644 --- a/src/v0/destinations/facebook_conversions/transform.js +++ b/src/v0/destinations/facebook_conversions/transform.js @@ -1,6 +1,5 @@ /* eslint-disable no-param-reassign */ const get = require('get-value'); -const moment = require('moment'); const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); const { CONFIG_CATEGORIES, @@ -33,6 +32,7 @@ const { fetchUserData, formingFinalResponse, } = require('../../util/facebookUtils'); +const { verifyEventDuration } = require('../facebook_pixel/utils'); const responseBuilderSimple = (message, category, destination) => { const { Config, ID } = destination; @@ -121,19 +121,7 @@ const processEvent = (message, destination) => { } const timeStamp = getFieldValueFromMessage(message, 'timestamp'); - if (timeStamp) { - const start = moment.unix(moment(timeStamp).format('X')); - const current = moment.unix(moment().format('X')); - // calculates past event in days - const deltaDay = Math.ceil(moment.duration(current.diff(start)).asDays()); - // calculates future event in minutes - const deltaMin = Math.ceil(moment.duration(start.diff(current)).asMinutes()); - if (deltaDay > 7 || deltaMin > 1) { - throw new InstrumentationError( - 'Events must be sent within seven days of their occurrence or up to one minute in the future.', - ); - } - } + verifyEventDuration(message, destination, timeStamp); const { datasetId, accessToken } = destination.Config; if (!datasetId) { diff --git a/src/v0/destinations/facebook_pixel/transform.js b/src/v0/destinations/facebook_pixel/transform.js index 8a63998b45..d44a38aa69 100644 --- a/src/v0/destinations/facebook_pixel/transform.js +++ b/src/v0/destinations/facebook_pixel/transform.js @@ -1,8 +1,6 @@ /* eslint-disable no-param-reassign */ const get = require('get-value'); -const moment = require('moment'); const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); -const stats = require('../../../util/stats'); const { VERSION, CONFIG_CATEGORIES, @@ -31,6 +29,7 @@ const { handleOrder, populateCustomDataBasedOnCategory, getCategoryFromEvent, + verifyEventDuration, } = require('./utils'); const { @@ -170,23 +169,7 @@ const processEvent = (message, destination) => { } const timeStamp = message.timestamp || message.originalTimestamp; - if (timeStamp) { - const start = moment.unix(moment(timeStamp).format('X')); - const current = moment.unix(moment().format('X')); - // calculates past event in days - const deltaDay = Math.ceil(moment.duration(current.diff(start)).asDays()); - // calculates future event in minutes - const deltaMin = Math.ceil(moment.duration(start.diff(current)).asMinutes()); - if (deltaDay > 7 || deltaMin > 1) { - // TODO: Remove after testing in mirror transformer - stats.increment('fb_pixel_timestamp_error', { - destinationId: destination.ID, - }); - throw new InstrumentationError( - 'Events must be sent within seven days of their occurrence or up to one minute in the future.', - ); - } - } + verifyEventDuration(message, destination, timeStamp); let eventsToEvents; if (Array.isArray(destination.Config.eventsToEvents)) { diff --git a/src/v0/destinations/facebook_pixel/utils.js b/src/v0/destinations/facebook_pixel/utils.js index cfa625ee3d..74d5240f2a 100644 --- a/src/v0/destinations/facebook_pixel/utils.js +++ b/src/v0/destinations/facebook_pixel/utils.js @@ -1,4 +1,6 @@ const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const get = require('get-value'); +const moment = require('moment'); const { isObject } = require('../../util'); const { ACTION_SOURCES_VALUES, @@ -339,6 +341,29 @@ const getCategoryFromEvent = (eventName) => { return category; }; +const verifyEventDuration = (message, destination, timeStamp) => { + const actionSource = + get(message, 'traits.action_source') || + get(message, 'context.traits.action_source') || + get(message, 'properties.action_source'); + + const start = moment.unix(moment(timeStamp).format('X')); + const current = moment.unix(moment().format('X')); + // calculates past event in days + const deltaDay = Math.ceil(moment.duration(current.diff(start)).asDays()); + // calculates future event in minutes + const deltaMin = Math.ceil(moment.duration(start.diff(current)).asMinutes()); + let defaultSupportedDelta = 7; + if (actionSource === 'physical_store') { + defaultSupportedDelta = 62; + } + if (deltaDay > defaultSupportedDelta || deltaMin > 1) { + throw new InstrumentationError( + `Events must be sent within ${defaultSupportedDelta} days of their occurrence or up to one minute in the future.`, + ); + } +}; + module.exports = { formatRevenue, getActionSource, @@ -348,4 +373,5 @@ module.exports = { handleOrder, populateCustomDataBasedOnCategory, getCategoryFromEvent, + verifyEventDuration, }; diff --git a/src/v0/destinations/facebook_pixel/utils.test.js b/src/v0/destinations/facebook_pixel/utils.test.js index f32d7d7024..fa17aebd76 100644 --- a/src/v0/destinations/facebook_pixel/utils.test.js +++ b/src/v0/destinations/facebook_pixel/utils.test.js @@ -1,7 +1,13 @@ const { InstrumentationError } = require('@rudderstack/integrations-lib'); -const { getActionSource, formatRevenue, getCategoryFromEvent } = require('./utils'); +const { + getActionSource, + formatRevenue, + getCategoryFromEvent, + verifyEventDuration, +} = require('./utils'); const { CONFIG_CATEGORIES, OTHER_STANDARD_EVENTS } = require('./config'); +Date.now = jest.fn(() => new Date('2022-01-20T00:00:00Z')); describe('Test Facebook Pixel Utils', () => { describe('getActionSource', () => { // Returns 'other' if payload.action_source is not defined and channel is neither 'web' nor 'mobile' @@ -151,4 +157,75 @@ describe('Test Facebook Pixel Utils', () => { expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); }); }); + + describe('verifyEventDuration', () => { + it('should not throw an InstrumentationError when event duration is less than 8 days after the event occurred', () => { + const message = { + traits: { + action_source: 'some_action_source', + }, + context: { + traits: { + action_source: 'some_action_source', + }, + }, + properties: { + action_source: 'some_action_source', + }, + }; + const destination = { + ID: 'some_destination_id', + }; + const timeStamp = '2022-01-20T00:00:00Z'; + expect(() => { + verifyEventDuration(message, destination, timeStamp); + }).not.toThrow(InstrumentationError); + }); + it('should throw an InstrumentationError when event duration is exactly 8 days after the event occurred', () => { + const message = { + traits: { + action_source: 'some_action_source', + }, + context: { + traits: { + action_source: 'some_action_source', + }, + }, + properties: { + action_source: 'some_action_source', + }, + }; + const destination = { + ID: 'some_destination_id', + }; + const timeStamp = '2022-01-12T00:00:00Z'; + + expect(() => { + verifyEventDuration(message, destination, timeStamp); + }).toThrow(InstrumentationError); + }); + it('should not throw an InstrumentationError when event duration is greater than 8 days after the event occurred and action_source is physical_store', () => { + const message = { + traits: { + action_source: 'physical_store', + }, + context: { + traits: { + action_source: 'some_action_source', + }, + }, + properties: { + action_source: 'some_action_source', + }, + }; + const destination = { + ID: 'some_destination_id', + }; + const timeStamp = '2022-01-12T00:00:00Z'; + + expect(() => { + verifyEventDuration(message, destination, timeStamp); + }).not.toThrow(InstrumentationError); + }); + }); }); diff --git a/test/integrations/destinations/facebook_conversions/processor/data.ts b/test/integrations/destinations/facebook_conversions/processor/data.ts index bfa35bc22b..3224a15771 100644 --- a/test/integrations/destinations/facebook_conversions/processor/data.ts +++ b/test/integrations/destinations/facebook_conversions/processor/data.ts @@ -94,7 +94,7 @@ export const data = [ body: [ { error: - 'Events must be sent within seven days of their occurrence or up to one minute in the future.', + 'Events must be sent within 7 days of their occurrence or up to one minute in the future.', statusCode: 400, statTags: { destType: 'FACEBOOK_CONVERSIONS', diff --git a/test/integrations/destinations/facebook_pixel/processor/validationTestData.ts b/test/integrations/destinations/facebook_pixel/processor/validationTestData.ts index 8e24801464..a0f85e45e3 100644 --- a/test/integrations/destinations/facebook_pixel/processor/validationTestData.ts +++ b/test/integrations/destinations/facebook_pixel/processor/validationTestData.ts @@ -267,7 +267,7 @@ export const validationTestData = [ { statusCode: 400, error: - 'Events must be sent within seven days of their occurrence or up to one minute in the future.', + 'Events must be sent within 7 days of their occurrence or up to one minute in the future.', statTags: commonStatTags, }, ], @@ -370,4 +370,92 @@ export const validationTestData = [ }, }, }, + { + id: 'fbPixel-validation-test-5', + name: 'facebook_pixel', + description: '[Error]: validate event date and time', + scenario: 'Framework + business', + successCriteria: + 'Response should contain error message and status code should be 400, as we are sending an event which is older than 7 days and the error message should be Events must be sent within seven days of their occurrence or up to one minute in the future.', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedTrackPayload({ + // Sun Oct 15 2023 + type: 'track', + event: 'TestEven001', + sentAt: '2023-08-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + action_source: 'physical_store', + email: 'test@rudderstack.com', + phone: '9112340375', + event_id: 'x9lk3gfte768o1oy08cyaylx5t2j9q2wwfl2', + plan_details: { + plan_type: 'gold', + duration: '3 months', + }, + }, + }, + properties: { + revenue: 400, + additional_bet_index: 0, + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-08-25T15:32:56.409Z', + }), + destination: overrideDestination(commonDestination, { + accessToken: '09876', + pixelId: 'dummyPixelId', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 200, + output: { + body: { + FORM: { + data: [ + JSON.stringify({ + user_data: { + em: '1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd', + ph: '820c46baccd33a1664f583b4505a7e39e033197e06e0bd7c87109e33c57c5497', + }, + event_name: 'TestEven001', + event_time: 1692977576, + event_id: 'x9lk3gfte768o1oy08cyaylx5t2j9q2wwfl2', + action_source: 'physical_store', + custom_data: { additional_bet_index: 0, value: 400 }, + }), + ], + }, + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + }, + endpoint: 'https://graph.facebook.com/v18.0/dummyPixelId/events?access_token=09876', + files: {}, + headers: {}, + method: 'POST', + params: {}, + type: 'REST', + userId: '', + version: '1', + }, + }, + ], + }, + }, + }, ]; From d54f6ad794fc3a0f2356e0bec561ce51acaf8964 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 20 May 2024 08:33:02 +0000 Subject: [PATCH 166/240] chore(release): 1.66.1 --- CHANGELOG.md | 11 +++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5db5ec047a..4258fdfcc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [1.66.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.66.0...v1.66.1) (2024-05-20) + + +### Bug Fixes + +* add validation for null/undefined traits in slack ([#3382](https://github.com/rudderlabs/rudder-transformer/issues/3382)) ([755073c](https://github.com/rudderlabs/rudder-transformer/commit/755073c4341a454785050d835021d9f17e0b9d3f)) +* gaoc store sales batching transform contract ([#3384](https://github.com/rudderlabs/rudder-transformer/issues/3384)) ([e7678cb](https://github.com/rudderlabs/rudder-transformer/commit/e7678cbdae4c06449ea9352ce3db390d2a29da14)) +* move af_currency outside properties in eventValue ([#3316](https://github.com/rudderlabs/rudder-transformer/issues/3316)) ([71c3d46](https://github.com/rudderlabs/rudder-transformer/commit/71c3d46236fff9209625cfb0737c21db2d275345)) +* remove default traits from ortto ([#3389](https://github.com/rudderlabs/rudder-transformer/issues/3389)) ([fbb0811](https://github.com/rudderlabs/rudder-transformer/commit/fbb0811aa0e417b0cffcea4ecc103979afccfe74)) +* update validation of event duration ([#3376](https://github.com/rudderlabs/rudder-transformer/issues/3376)) ([3ad7850](https://github.com/rudderlabs/rudder-transformer/commit/3ad78506446915ada8bdc5f5594dc2710e6b0646)) + ## [1.66.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.65.1...v1.66.0) (2024-05-13) diff --git a/package-lock.json b/package-lock.json index 14ab853b82..f51f3ccd8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.66.0", + "version": "1.66.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.66.0", + "version": "1.66.1", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index 168f49da5e..50a276ce42 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.66.0", + "version": "1.66.1", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From 7c0d963d3ee87a3ed5712492300dc50768c529de Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Mon, 20 May 2024 14:20:40 +0530 Subject: [PATCH 167/240] fix: resolving comments --- src/cdk/v2/destinations/koddi/config.js | 10 ++- .../v2/destinations/koddi/procWorkflow.yaml | 14 ++-- src/cdk/v2/destinations/koddi/utils.js | 40 +++++----- src/cdk/v2/destinations/koddi/utils.test.js | 78 +++++++++---------- .../destinations/koddi/processor/clicks.ts | 2 +- .../koddi/processor/conversions.ts | 4 +- .../koddi/processor/impressions.ts | 2 +- .../destinations/koddi/router/data.ts | 38 ++++++++- 8 files changed, 110 insertions(+), 78 deletions(-) diff --git a/src/cdk/v2/destinations/koddi/config.js b/src/cdk/v2/destinations/koddi/config.js index fa595dc627..927e1858fc 100644 --- a/src/cdk/v2/destinations/koddi/config.js +++ b/src/cdk/v2/destinations/koddi/config.js @@ -1,6 +1,12 @@ const { getMappingConfig } = require('../../../../v0/util'); -const EVENT_NAMES = { +/** + * ref :- https://developers.koddi.com/reference/winning-ads + * impressions - https://developers.koddi.com/reference/impressions-1 + * clicks - https://developers.koddi.com/reference/clicks-1 + * conversions - https://developers.koddi.com/reference/conversions-1 + */ +const EVENT_TYPES = { IMPRESSIONS: 'impressions', CLICKS: 'clicks', CONVERSIONS: 'conversions', @@ -24,7 +30,7 @@ const CONFIG_CATEGORIES = { const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); module.exports = { - EVENT_NAMES, + EVENT_TYPES, CONFIG_CATEGORIES, MAPPING_CONFIG, IMPRESSIONS_CONFIG: MAPPING_CONFIG[CONFIG_CATEGORIES.IMPRESSIONS.name], diff --git a/src/cdk/v2/destinations/koddi/procWorkflow.yaml b/src/cdk/v2/destinations/koddi/procWorkflow.yaml index 28d062d4a4..cc3f0166dc 100644 --- a/src/cdk/v2/destinations/koddi/procWorkflow.yaml +++ b/src/cdk/v2/destinations/koddi/procWorkflow.yaml @@ -11,25 +11,23 @@ steps: - name: messageType template: | .message.type.toLowerCase(); - - name: eventName + - name: eventType template: | - .message.integrations.koddi.eventName.toLowerCase(); + .message.integrations.koddi.eventType.toLowerCase(); - name: validateInput template: | let messageType = $.outputs.messageType; - let eventName = $.outputs.eventName; + let eventType = $.outputs.eventType; $.assert(messageType, "message Type is not present. Aborting message."); $.assert(messageType in {{$.EventType.([.TRACK])}}, "message type " + messageType + " is not supported"); - $.assert(eventName in {{$.EVENT_NAMES.([.IMPRESSIONS, .CLICKS, .CONVERSIONS])}}, "event name " + eventName + " is not supported"); - $.assert(.message.event, "Event name is not present. Aborting"); - $.assert(typeof .message.event === "string", "event name should be a string"); + $.assert(eventType in {{$.EVENT_TYPES.([.IMPRESSIONS, .CLICKS, .CONVERSIONS])}}, "event type " + eventType + " is not supported"); $.assertConfig(.destination.Config.apiBaseUrl, "API Base URL is not present. Aborting"); $.assertConfig(.destination.Config.clientName, "Client Name is not present. Aborting"); - name: preparePayload template: | - const payload = $.constructFullPayload($.outputs.eventName, .message, .destination.Config); + const payload = $.constructFullPayload($.outputs.eventType, .message, .destination.Config); $.context.payload = $.removeUndefinedAndNullValues(payload); - name: buildResponse template: | - const response = $.constructResponse($.outputs.eventName, .destination.Config, $.context.payload); + const response = $.constructResponse($.outputs.eventType, .destination.Config, $.context.payload); response diff --git a/src/cdk/v2/destinations/koddi/utils.js b/src/cdk/v2/destinations/koddi/utils.js index f6521d1a66..13014e2e7c 100644 --- a/src/cdk/v2/destinations/koddi/utils.js +++ b/src/cdk/v2/destinations/koddi/utils.js @@ -1,5 +1,5 @@ const { InstrumentationError } = require('@rudderstack/integrations-lib'); -const { EVENT_NAMES, IMPRESSIONS_CONFIG, CLICKS_CONFIG, CONVERSIONS_CONFIG } = require('./config'); +const { EVENT_TYPES, IMPRESSIONS_CONFIG, CLICKS_CONFIG, CONVERSIONS_CONFIG } = require('./config'); const { constructPayload, defaultRequestConfig, @@ -31,19 +31,19 @@ const validateBidders = (bidders) => { /** * This function constructs payloads based upon mappingConfig for all calls. - * @param {*} eventName + * @param {*} eventType * @param {*} message * @param {*} Config * @returns */ -const constructFullPayload = (eventName, message, Config) => { +const constructFullPayload = (eventType, message, Config) => { let payload; - switch (eventName) { - case EVENT_NAMES.IMPRESSIONS: + switch (eventType) { + case EVENT_TYPES.IMPRESSIONS: payload = constructPayload(message, IMPRESSIONS_CONFIG); payload.clientName = Config.clientName; break; - case EVENT_NAMES.CLICKS: + case EVENT_TYPES.CLICKS: payload = constructPayload(message, CLICKS_CONFIG); payload.clientName = Config.clientName; if (!Config.testVersionOverride) { @@ -53,53 +53,53 @@ const constructFullPayload = (eventName, message, Config) => { payload.overrides = null; } break; - case EVENT_NAMES.CONVERSIONS: + case EVENT_TYPES.CONVERSIONS: payload = constructPayload(message, CONVERSIONS_CONFIG); payload.client_name = Config.clientName; payload.unixtime = toUnixTimestamp(payload.unixtime); validateBidders(payload.bidders); break; default: - throw new InstrumentationError(`event name ${eventName} is not supported.`); + throw new InstrumentationError(`event type ${eventType} is not supported.`); } return payload; }; -const getEndpoint = (eventName, Config) => { +const getEndpoint = (eventType, Config) => { let endpoint = stripTrailingSlash(Config.apiBaseUrl); - switch (eventName) { - case EVENT_NAMES.IMPRESSIONS: + switch (eventType) { + case EVENT_TYPES.IMPRESSIONS: endpoint += '?action=impression'; break; - case EVENT_NAMES.CLICKS: + case EVENT_TYPES.CLICKS: endpoint += '?action=click'; break; - case EVENT_NAMES.CONVERSIONS: + case EVENT_TYPES.CONVERSIONS: endpoint += '/conversion'; break; default: - throw new InstrumentationError(`event name ${eventName} is not supported.`); + throw new InstrumentationError(`event type ${eventType} is not supported.`); } return endpoint; }; /** * This function constructs response based upon event. - * @param {*} eventName + * @param {*} eventType * @param {*} Config * @param {*} payload * @returns */ -const constructResponse = (eventName, Config, payload) => { - if (!Object.values(EVENT_NAMES).includes(eventName)) { - throw new InstrumentationError(`event name ${eventName} is not supported.`); +const constructResponse = (eventType, Config, payload) => { + if (!Object.values(EVENT_TYPES).includes(eventType)) { + throw new InstrumentationError(`event type ${eventType} is not supported.`); } const response = defaultRequestConfig(); - response.endpoint = getEndpoint(eventName, Config); + response.endpoint = getEndpoint(eventType, Config); response.headers = { accept: 'application/json', }; - if (eventName === EVENT_NAMES.CONVERSIONS) { + if (eventType === EVENT_TYPES.CONVERSIONS) { response.body.JSON = payload; response.method = 'POST'; response.headers = { diff --git a/src/cdk/v2/destinations/koddi/utils.test.js b/src/cdk/v2/destinations/koddi/utils.test.js index 59a02b62a0..2c1f660f70 100644 --- a/src/cdk/v2/destinations/koddi/utils.test.js +++ b/src/cdk/v2/destinations/koddi/utils.test.js @@ -8,43 +8,43 @@ const { InstrumentationError } = require('@rudderstack/integrations-lib'); describe('getEndpoint', () => { it('returns the correct endpoint for IMPRESSIONS event', () => { - const eventName = 'impressions'; + const eventType = 'impressions'; const Config = { apiBaseUrl: 'https://www.test-client.com/', clientName: 'test-client', }; - const result = getEndpoint(eventName, Config); + const result = getEndpoint(eventType, Config); expect(result).toEqual('https://www.test-client.com?action=impression'); }); it('returns the correct endpoint for CLICKS event', () => { - const eventName = 'clicks'; + const eventType = 'clicks'; const Config = { apiBaseUrl: 'https://www.test-client.com', clientName: 'test-client', }; - const result = getEndpoint(eventName, Config); + const result = getEndpoint(eventType, Config); expect(result).toEqual('https://www.test-client.com?action=click'); }); it('returns the correct endpoint for IMPRESSIONS event', () => { - const eventName = 'conversions'; + const eventType = 'conversions'; const Config = { apiBaseUrl: 'https://www.test-client.com', clientName: 'test-client', }; - const result = getEndpoint(eventName, Config); + const result = getEndpoint(eventType, Config); expect(result).toEqual('https://www.test-client.com/conversion'); }); it('should throw error for unsupported event', () => { - const eventName = 'test'; + const eventType = 'test'; const Config = { apiBaseUrl: 'https://www.test-client.com', clientName: 'test-client', }; - expect(() => getEndpoint(eventName, Config)).toThrow(InstrumentationError); - expect(() => getEndpoint(eventName, Config)).toThrow('event name test is not supported.'); + expect(() => getEndpoint(eventType, Config)).toThrow(InstrumentationError); + expect(() => getEndpoint(eventType, Config)).toThrow('event type test is not supported.'); }); }); @@ -84,9 +84,7 @@ describe('validateBidders', () => { }); it('should throw error if base_price is not present', () => { - const bidders = [ - { bidder: 'bidder1', alternate_bidder: 'alternate1', count: 1 }, // Missing base_price - ]; + const bidders = [{ bidder: 'bidder1', alternate_bidder: 'alternate1', count: 1 }]; expect(() => validateBidders(bidders)).toThrow(InstrumentationError); expect(() => validateBidders(bidders)).toThrow('base_price is not present. Aborting.'); }); @@ -102,7 +100,7 @@ describe('validateBidders', () => { describe('constructFullPayload', () => { it('should construct payload for IMPRESSIONS event', () => { - const eventName = 'impressions'; + const eventType = 'impressions'; const message = { type: 'track', event: 'Impressions Event', @@ -124,11 +122,11 @@ describe('constructFullPayload', () => { trackingData: 'dummy-tracking-data', ts: '2024-03-03T00:29:12.117+05:30', }; - const payload = constructFullPayload(eventName, message, Config); + const payload = constructFullPayload(eventType, message, Config); expect(payload).toEqual(expectedPayload); }); it('should throw error if required value is missing for IMPRESSIONS event', () => { - const eventName = 'impressions'; + const eventType = 'impressions'; const message = { type: 'track', event: 'Impressions Event', @@ -144,14 +142,14 @@ describe('constructFullPayload', () => { clientName: 'test-client', }; try { - const payload = constructFullPayload(eventName, message, Config); + const payload = constructFullPayload(eventType, message, Config); } catch (error) { expect(error.message).toEqual('Missing required value from "properties.tracking_data"'); } }); it('should construct payload for CLICKS event', () => { - const eventName = 'clicks'; + const eventType = 'clicks'; const message = { type: 'track', event: 'Clicks Event', @@ -175,11 +173,11 @@ describe('constructFullPayload', () => { overrides: null, testVersionOverride: null, }; - const payload = constructFullPayload(eventName, message, Config); + const payload = constructFullPayload(eventType, message, Config); expect(payload).toEqual(expectedPayload); }); it('should construct payload with non-null value if overrides and testVersionOverride are enable and values for these are provided for CLICKS event ', () => { - const eventName = 'clicks'; + const eventType = 'clicks'; const message = { type: 'track', event: 'Clicks Event', @@ -207,11 +205,11 @@ describe('constructFullPayload', () => { overrides: 'overridden-value', testVersionOverride: null, }; - const payload = constructFullPayload(eventName, message, Config); + const payload = constructFullPayload(eventType, message, Config); expect(payload).toEqual(expectedPayload); }); it('should throw error if required value is missing for CLICKS event', () => { - const eventName = 'clicks'; + const eventType = 'clicks'; const message = { type: 'track', event: 'Clicks Event', @@ -226,14 +224,14 @@ describe('constructFullPayload', () => { clientName: 'test-client', }; try { - const payload = constructFullPayload(eventName, message, Config); + const payload = constructFullPayload(eventType, message, Config); } catch (error) { expect(error.message).toEqual('Missing required value from "userId"'); } }); it('should construct payload for CONVERSIONS event', () => { - const eventName = 'conversions'; + const eventType = 'conversions'; const message = { type: 'track', event: 'Conversions Event', @@ -275,11 +273,11 @@ describe('constructFullPayload', () => { }, ], }; - const payload = constructFullPayload(eventName, message, Config); + const payload = constructFullPayload(eventType, message, Config); expect(payload).toEqual(expectedPayload); }); it('should throw error if required value is missing for CONVERSIONS event', () => { - const eventName = 'conversions'; + const eventType = 'conversions'; const message = { type: 'track', event: 'Conversions Event', @@ -305,26 +303,26 @@ describe('constructFullPayload', () => { clientName: 'test-client', }; try { - const payload = constructFullPayload(eventName, message, Config); + const payload = constructFullPayload(eventType, message, Config); } catch (error) { expect(error.message).toEqual('Missing required value from "context.locale"'); } }); it('should throw error for unsupported event', () => { - const eventName = 'test'; + const eventType = 'test'; const message = {}; const Config = {}; - expect(() => constructFullPayload(eventName, message, Config)).toThrow(InstrumentationError); - expect(() => constructFullPayload(eventName, message, Config)).toThrow( - 'event name test is not supported.', + expect(() => constructFullPayload(eventType, message, Config)).toThrow(InstrumentationError); + expect(() => constructFullPayload(eventType, message, Config)).toThrow( + 'event type test is not supported.', ); }); }); describe('constructResponse', () => { it('should construct response for IMPRESSIONS event', () => { - const eventName = 'impressions'; + const eventType = 'impressions'; const Config = { apiBaseUrl: 'https://www.test-client.com', clientName: 'test-client', @@ -344,12 +342,12 @@ describe('constructResponse', () => { method: 'GET', params: payload, }; - const response = constructResponse(eventName, Config, payload); + const response = constructResponse(eventType, Config, payload); expect(response).toMatchObject(expectedResponse); }); it('should construct response for CLICKS event', () => { - const eventName = 'clicks'; + const eventType = 'clicks'; const Config = { apiBaseUrl: 'https://www.test-client.com', clientName: 'test-client', @@ -369,12 +367,12 @@ describe('constructResponse', () => { method: 'GET', params: payload, }; - const response = constructResponse(eventName, Config, payload); + const response = constructResponse(eventType, Config, payload); expect(response).toMatchObject(expectedResponse); }); it('should construct response for CONVERSIONS event', () => { - const eventName = 'conversions'; + const eventType = 'conversions'; const Config = { apiBaseUrl: 'https://www.test-client.com', clientName: 'test-client', @@ -407,17 +405,17 @@ describe('constructResponse', () => { JSON: payload, }, }; - const response = constructResponse(eventName, Config, payload); + const response = constructResponse(eventType, Config, payload); expect(response).toMatchObject(expectedResponse); }); it('should throw error for unsupported event', () => { - const eventName = 'test'; + const eventType = 'test'; const Config = {}; const payload = {}; - expect(() => constructResponse(eventName, Config, payload)).toThrow(InstrumentationError); - expect(() => constructResponse(eventName, Config, payload)).toThrow( - 'event name test is not supported.', + expect(() => constructResponse(eventType, Config, payload)).toThrow(InstrumentationError); + expect(() => constructResponse(eventType, Config, payload)).toThrow( + 'event type test is not supported.', ); }); }); diff --git a/test/integrations/destinations/koddi/processor/clicks.ts b/test/integrations/destinations/koddi/processor/clicks.ts index 6270c0468d..6101e9bafe 100644 --- a/test/integrations/destinations/koddi/processor/clicks.ts +++ b/test/integrations/destinations/koddi/processor/clicks.ts @@ -32,7 +32,7 @@ export const clicks: ProcessorTestData[] = [ integrations: { All: true, koddi: { - eventName: 'Clicks', + eventType: 'Clicks', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', diff --git a/test/integrations/destinations/koddi/processor/conversions.ts b/test/integrations/destinations/koddi/processor/conversions.ts index 7c8494d258..1647ffed7d 100644 --- a/test/integrations/destinations/koddi/processor/conversions.ts +++ b/test/integrations/destinations/koddi/processor/conversions.ts @@ -44,7 +44,7 @@ export const conversions: ProcessorTestData[] = [ integrations: { All: true, koddi: { - eventName: 'Conversions', + eventType: 'Conversions', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', @@ -114,7 +114,7 @@ export const conversions: ProcessorTestData[] = [ integrations: { All: true, koddi: { - eventName: 'Conversions', + eventType: 'Conversions', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', diff --git a/test/integrations/destinations/koddi/processor/impressions.ts b/test/integrations/destinations/koddi/processor/impressions.ts index 35ffcad22b..840ed9139f 100644 --- a/test/integrations/destinations/koddi/processor/impressions.ts +++ b/test/integrations/destinations/koddi/processor/impressions.ts @@ -32,7 +32,7 @@ export const impressions: ProcessorTestData[] = [ integrations: { All: true, koddi: { - eventName: 'Impressions', + eventType: 'Impressions', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', diff --git a/test/integrations/destinations/koddi/router/data.ts b/test/integrations/destinations/koddi/router/data.ts index 85f1319e39..1601a481e5 100644 --- a/test/integrations/destinations/koddi/router/data.ts +++ b/test/integrations/destinations/koddi/router/data.ts @@ -28,7 +28,7 @@ const routerRequest: RouterTransformationRequest = { integrations: { All: true, koddi: { - eventName: 'Impressions', + eventType: 'Impressions', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', @@ -51,7 +51,7 @@ const routerRequest: RouterTransformationRequest = { integrations: { All: true, koddi: { - eventName: 'Clicks', + eventType: 'Clicks', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', @@ -78,7 +78,7 @@ const routerRequest: RouterTransformationRequest = { integrations: { All: true, koddi: { - eventName: 'Conversions', + eventType: 'Conversions', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', @@ -100,13 +100,35 @@ const routerRequest: RouterTransformationRequest = { integrations: { All: true, koddi: { - eventName: 'Impressions', + eventType: 'Impressions', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', }, metadata: generateMetadata(4), }, + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventType: 'Unknown', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(5), + }, ], destType, }; @@ -224,6 +246,14 @@ export const data = [ statTags: RouterInstrumentationErrorStatTags, statusCode: 400, }, + { + batched: false, + error: 'event type unknown is not supported', + destination, + metadata: [generateMetadata(5)], + statTags: RouterInstrumentationErrorStatTags, + statusCode: 400, + }, ], }, }, From ffd4958dd3d2859e9eb29afd7a6f7260d185e6a0 Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Tue, 21 May 2024 10:06:21 +0530 Subject: [PATCH 168/240] chore: fetch and checkout the pull request head commit instead of merge commit (#3388) --- .github/workflows/build-push-docker-image.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build-push-docker-image.yml b/.github/workflows/build-push-docker-image.yml index 7ddae0a3ae..0d3494e8d1 100644 --- a/.github/workflows/build-push-docker-image.yml +++ b/.github/workflows/build-push-docker-image.yml @@ -38,6 +38,7 @@ jobs: - name: Checkout uses: actions/checkout@v4.1.1 with: + ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 1 - name: Setup Docker Buildx @@ -88,6 +89,7 @@ jobs: - name: Checkout uses: actions/checkout@v4.1.1 with: + ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 1 - name: Setup Docker Buildx From 0196f20cc79e1f470d96a649dd9404c3c9284329 Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Tue, 21 May 2024 17:10:05 +0530 Subject: [PATCH 169/240] feat: add json-data type support in redis (#3336) --- src/v0/destinations/redis/transform.js | 37 +++- .../destinations/redis/processor/data.ts | 205 ++++++++++++++++++ test/integrations/destinations/redis/types.ts | 39 ++++ 3 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 test/integrations/destinations/redis/types.ts diff --git a/src/v0/destinations/redis/transform.js b/src/v0/destinations/redis/transform.js index 23c73f0ba4..ec0e858d3e 100644 --- a/src/v0/destinations/redis/transform.js +++ b/src/v0/destinations/redis/transform.js @@ -2,7 +2,7 @@ const lodash = require('lodash'); const flatten = require('flat'); const { InstrumentationError } = require('@rudderstack/integrations-lib'); -const { isEmpty, isObject } = require('../../util'); +const { isEmpty, isObject, getFieldValueFromMessage } = require('../../util'); const { EventType } = require('../../../constants'); // processValues: @@ -46,6 +46,19 @@ const transformSubEventTypeProfiles = (message, workspaceId, destinationId) => { }; }; +const getJSONValue = (message) => { + const eventType = message.type.toLowerCase(); + if (eventType === EventType.IDENTIFY) { + return getFieldValueFromMessage(message, 'traits'); + } + return {}; +}; + +const getTransformedPayloadForJSON = ({ key, path, value, userId }) => ({ + message: { key, path, value }, + userId, +}); + const process = (event) => { const { message, destination, metadata } = event; const messageType = message && message.type && message.type.toLowerCase(); @@ -58,15 +71,35 @@ const process = (event) => { throw new InstrumentationError('Blank userId passed in identify event'); } - const { prefix } = destination.Config; + const { prefix, useJSONModule } = destination.Config; const destinationId = destination.ID; const keyPrefix = isEmpty(prefix) ? '' : `${prefix.trim()}:`; + const jsonValue = getJSONValue(message); + if (isSubEventTypeProfiles(message)) { const { workspaceId } = metadata; + if (useJSONModule) { + // If redis should store information as JSON type + return getTransformedPayloadForJSON({ + key: `${workspaceId}:${destinationId}:${message.context.sources.profiles_entity}:${message.context.sources.profiles_id_type}:${message.userId}`, + path: message.context.sources.profiles_model, + value: jsonValue, + userId: message.userId, + }); + } return transformSubEventTypeProfiles(message, workspaceId, destinationId); } + if (useJSONModule) { + // If redis should store information as JSON type + return getTransformedPayloadForJSON({ + key: `${keyPrefix}user:${lodash.toString(message.userId)}`, + value: jsonValue, + userId: message.userId, + }); + } + const hmap = { key: `${keyPrefix}user:${lodash.toString(message.userId)}`, fields: {}, diff --git a/test/integrations/destinations/redis/processor/data.ts b/test/integrations/destinations/redis/processor/data.ts index 72c78a1ad4..999a954b59 100644 --- a/test/integrations/destinations/redis/processor/data.ts +++ b/test/integrations/destinations/redis/processor/data.ts @@ -1,3 +1,207 @@ +import { RedisTc, SupportedEventTypes, TraitsLocation } from '../types'; + +const getRedisTestCase = (tc: RedisTc) => { + const context = { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.5', + }, + ip: '0.0.0.0', + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.5', + }, + locale: 'en-GB', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + traits: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + }; + return { + name: 'redis', + description: tc.description, + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: { + workspaceId: tc.workspaceId || 'wspId', + }, + destination: { + Config: { + address: 'localhost:6379', + database: 'test', + prefix: ' ', + ...tc.destinationConfig, + }, + DestinationDefinition: { + DisplayName: 'Redis', + ID: '1WhbSZ6uA3H5ChVifHpfL2H6sie', + Name: 'REDIS', + }, + Enabled: true, + ID: tc.destId || '1WhcOCGgj9asZu850HvugU2C3Aq', + Name: 'Redis', + + Transformations: [], + }, + message: { + anonymousId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', + channel: 'web', + ...(tc.traitsLocation === TraitsLocation.Traits ? { traits: tc.traits } : {}), + context: { + ...context, + ...(tc.traitsLocation === TraitsLocation.ContextTraits + ? { traits: tc.traits } + : {}), + ...(tc.profilesContext ?? {}), + }, + integrations: { + All: true, + }, + messageId: '2536eda4-d638-4c93-8014-8ffe3f083214', + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53708', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2020-01-24T11:59:02.402+05:30', + type: tc.eventType, + userId: tc.userId || 'myUserId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + metadata: { + workspaceId: tc.workspaceId || 'wspId', + }, + output: tc.expectedResponse, + statusCode: tc.expectedStatusCode, + }, + ], + }, + }, + }; +}; + +const jsonTestCases: RedisTc[] = [ + { + description: + 'Test 6: [Event-stream] when useJSONModule is enabled with traits in context.traits, should transform successfully', + destinationConfig: { + useJSONModule: true, + }, + traitsLocation: TraitsLocation.ContextTraits, + traits: { + prop1: 'k', + prop2: 2, + prop3: { + p4: 'a', + }, + }, + eventType: SupportedEventTypes.Identify, + userId: 'user-1', + expectedResponse: { + message: { + key: 'user:user-1', + value: { + prop1: 'k', + prop2: 2, + prop3: { + p4: 'a', + }, + }, + }, + userId: 'user-1', + }, + expectedStatusCode: 200, + }, + { + description: + 'Test 7: [Event-stream] when useJSONModule is enabled with traits in traits, should transform successfully', + destinationConfig: { + useJSONModule: true, + }, + traitsLocation: TraitsLocation.Traits, + traits: { + prop1: 'k', + prop2: 2, + prop3: { + p4: 'a', + }, + }, + eventType: SupportedEventTypes.Identify, + userId: 'user-1', + expectedResponse: { + message: { + key: 'user:user-1', + value: { + prop1: 'k', + prop2: 2, + prop3: { + p4: 'a', + }, + }, + }, + userId: 'user-1', + }, + expectedStatusCode: 200, + }, + { + description: + 'Test 8: [Profiles] when useJSONModule is enabled with traits in traits, should transform successfully', + destinationConfig: { + useJSONModule: true, + }, + destId: 'd1', + workspaceId: 'w1', + traitsLocation: TraitsLocation.Traits, + traits: { + field: 'k', + model_name: 'analytics_app', + }, + profilesContext: { + sources: { + profiles_entity: 'e1', + profiles_model: 'model1', + profiles_id_type: 'id-1', + }, + }, + eventType: SupportedEventTypes.Identify, + userId: 'user-2', + expectedResponse: { + message: { + key: 'w1:d1:e1:id-1:user-2', + path: 'model1', + value: { + field: 'k', + model_name: 'analytics_app', + }, + }, + userId: 'user-2', + }, + expectedStatusCode: 200, + }, +]; + +const jsonTcs = jsonTestCases.map((tc) => getRedisTestCase(tc)); + export const data = [ { name: 'redis', @@ -658,4 +862,5 @@ export const data = [ }, }, }, + ...jsonTcs, ]; diff --git a/test/integrations/destinations/redis/types.ts b/test/integrations/destinations/redis/types.ts new file mode 100644 index 0000000000..f4970b3afa --- /dev/null +++ b/test/integrations/destinations/redis/types.ts @@ -0,0 +1,39 @@ +export type RedisConfig = Partial<{ + address: string; + database: string; + prefix: string; + useJSONModule: boolean; +}>; + +type RedisJSONOutput = { + message: Record; + userId: string; +}; + +export enum TraitsLocation { + ContextTraits = 'context.traits', + Traits = 'traits', +} +export enum SupportedEventTypes { + Identify = 'identify', +} + +export type RedisTc = { + description: string; + destId?: string; + workspaceId?: string; + destinationConfig: RedisConfig; + traitsLocation: TraitsLocation; + traits: Record; + eventType: SupportedEventTypes; + userId?: string; + expectedResponse: RedisJSONOutput; + expectedStatusCode: number; + profilesContext?: { + sources: { + profiles_entity: string; + profiles_id_type: string; + profiles_model: string; + }; + }; +}; From 90e60a5a28b1f868ecb50ce56406f2e706b10e7f Mon Sep 17 00:00:00 2001 From: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Date: Wed, 22 May 2024 15:40:03 +0530 Subject: [PATCH 170/240] refactor: deprecate mixpanel /track endpoint (#2833) (#3399) * refactor: deprecate mixpanel /track endpoint (#2833) * refactor: deprecate mixpanel /track endpoint * fix: used token in place of apiSecret for merge call * fix: no-case-declarations * refactor: responseBuilderSimple --- src/util/prometheus.js | 6 - src/v0/destinations/mp/config.js | 2 - src/v0/destinations/mp/transform.js | 83 ++-- src/v0/destinations/mp/util.js | 8 +- src/v0/destinations/mp/util.test.js | 14 - test/integrations/destinations/mp/common.ts | 2 +- .../destinations/mp/processor/data.ts | 363 ++++++++++-------- .../destinations/mp/router/data.ts | 12 +- 8 files changed, 249 insertions(+), 241 deletions(-) diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 882dff9e75..a46eae12c9 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -587,12 +587,6 @@ class Prometheus { type: 'gauge', labelNames: ['destination_id'], }, - { - name: 'mixpanel_batch_track_pack_size', - help: 'mixpanel_batch_track_pack_size', - type: 'gauge', - labelNames: ['destination_id'], - }, { name: 'mixpanel_batch_import_pack_size', help: 'mixpanel_batch_import_pack_size', diff --git a/src/v0/destinations/mp/config.js b/src/v0/destinations/mp/config.js index 35b40294f5..3abdf2eebb 100644 --- a/src/v0/destinations/mp/config.js +++ b/src/v0/destinations/mp/config.js @@ -49,7 +49,6 @@ const MP_IDENTIFY_EXCLUSION_LIST = [ ]; const GEO_SOURCE_ALLOWED_VALUES = [null, 'reverse_geocoding']; -const TRACK_MAX_BATCH_SIZE = 50; const IMPORT_MAX_BATCH_SIZE = 2000; const ENGAGE_MAX_BATCH_SIZE = 2000; const GROUPS_MAX_BATCH_SIZE = 200; @@ -68,7 +67,6 @@ module.exports = { MP_IDENTIFY_EXCLUSION_LIST, getCreateDeletionTaskEndpoint, DISTINCT_ID_MAX_BATCH_SIZE, - TRACK_MAX_BATCH_SIZE, IMPORT_MAX_BATCH_SIZE, ENGAGE_MAX_BATCH_SIZE, GROUPS_MAX_BATCH_SIZE, diff --git a/src/v0/destinations/mp/transform.js b/src/v0/destinations/mp/transform.js index 09a7862f9a..2065764b98 100644 --- a/src/v0/destinations/mp/transform.js +++ b/src/v0/destinations/mp/transform.js @@ -24,7 +24,6 @@ const { mappingConfig, BASE_ENDPOINT, BASE_ENDPOINT_EU, - TRACK_MAX_BATCH_SIZE, IMPORT_MAX_BATCH_SIZE, ENGAGE_MAX_BATCH_SIZE, GROUPS_MAX_BATCH_SIZE, @@ -47,21 +46,19 @@ const mPEventPropertiesConfigJson = mappingConfig[ConfigCategory.EVENT_PROPERTIE const setImportCredentials = (destConfig) => { const endpoint = destConfig.dataResidency === 'eu' ? `${BASE_ENDPOINT_EU}/import/` : `${BASE_ENDPOINT}/import/`; - const headers = { 'Content-Type': 'application/json' }; const params = { strict: destConfig.strictMode ? 1 : 0 }; - const { apiSecret, serviceAccountUserName, serviceAccountSecret, projectId } = destConfig; - if (apiSecret) { - headers.Authorization = `Basic ${base64Convertor(`${apiSecret}:`)}`; + const { serviceAccountUserName, serviceAccountSecret, projectId, token } = destConfig; + let credentials; + if (token) { + credentials = `${token}:`; } else if (serviceAccountUserName && serviceAccountSecret && projectId) { - headers.Authorization = `Basic ${base64Convertor( - `${serviceAccountUserName}:${serviceAccountSecret}`, - )}`; + credentials = `${serviceAccountUserName}:${serviceAccountSecret}`; params.projectId = projectId; - } else { - throw new InstrumentationError( - 'Event timestamp is older than 5 days and no API secret or service account credentials (i.e. username, secret and projectId) are provided in destination configuration', - ); } + const headers = { + 'Content-Type': 'application/json', + Authorization: `Basic ${base64Convertor(credentials)}`, + }; return { endpoint, headers, params }; }; @@ -70,46 +67,34 @@ const responseBuilderSimple = (payload, message, eventType, destConfig) => { response.method = defaultPostRequestConfig.requestMethod; response.userId = message.userId || message.anonymousId; response.body.JSON_ARRAY = { batch: JSON.stringify([removeUndefinedValues(payload)]) }; - const { apiSecret, serviceAccountUserName, serviceAccountSecret, projectId, dataResidency } = - destConfig; + const { dataResidency } = destConfig; const duration = getTimeDifference(message.timestamp); + + const setCredentials = () => { + const credentials = setImportCredentials(destConfig); + response.endpoint = credentials.endpoint; + response.headers = credentials.headers; + response.params = { + project_id: credentials.params?.projectId, + strict: credentials.params.strict, + }; + }; + switch (eventType) { case EventType.ALIAS: case EventType.TRACK: case EventType.SCREEN: - case EventType.PAGE: - if ( - !apiSecret && - !(serviceAccountUserName && serviceAccountSecret && projectId) && - duration.days <= 5 - ) { - response.endpoint = - dataResidency === 'eu' ? `${BASE_ENDPOINT_EU}/track/` : `${BASE_ENDPOINT}/track/`; - response.headers = {}; - } else if (duration.years > 5) { + case EventType.PAGE: { + if (duration.years > 5) { throw new InstrumentationError('Event timestamp should be within last 5 years'); - } else { - const credentials = setImportCredentials(destConfig); - response.endpoint = credentials.endpoint; - response.headers = credentials.headers; - response.params = { - project_id: credentials.params?.projectId, - strict: credentials.params.strict, - }; - break; } + setCredentials(); break; - case 'merge': - // eslint-disable-next-line no-case-declarations - const credentials = setImportCredentials(destConfig); - response.endpoint = credentials.endpoint; - response.headers = credentials.headers; - response.params = { - project_id: credentials.params?.projectId, - strict: credentials.params.strict, - }; + } + case 'merge': { + setCredentials(); break; - + } default: response.endpoint = dataResidency === 'eu' ? `${BASE_ENDPOINT_EU}/engage/` : `${BASE_ENDPOINT}/engage/`; @@ -484,7 +469,6 @@ const processRouterDest = async (inputs, reqMetadata) => { const batchSize = { engage: 0, groups: 0, - track: 0, import: 0, }; @@ -516,23 +500,16 @@ const processRouterDest = async (inputs, reqMetadata) => { ); transformedPayloads = lodash.flatMap(transformedPayloads); - const { engageEvents, groupsEvents, trackEvents, importEvents, batchErrorRespList } = + const { engageEvents, groupsEvents, importEvents, batchErrorRespList } = groupEventsByEndpoint(transformedPayloads); const engageRespList = batchEvents(engageEvents, ENGAGE_MAX_BATCH_SIZE, reqMetadata); const groupsRespList = batchEvents(groupsEvents, GROUPS_MAX_BATCH_SIZE, reqMetadata); - const trackRespList = batchEvents(trackEvents, TRACK_MAX_BATCH_SIZE, reqMetadata); const importRespList = batchEvents(importEvents, IMPORT_MAX_BATCH_SIZE, reqMetadata); - const batchSuccessRespList = [ - ...engageRespList, - ...groupsRespList, - ...trackRespList, - ...importRespList, - ]; + const batchSuccessRespList = [...engageRespList, ...groupsRespList, ...importRespList]; batchSize.engage += engageRespList.length; batchSize.groups += groupsRespList.length; - batchSize.track += trackRespList.length; batchSize.import += importRespList.length; return [...batchSuccessRespList, ...batchErrorRespList]; diff --git a/src/v0/destinations/mp/util.js b/src/v0/destinations/mp/util.js index d564e805ad..b2807d6e11 100644 --- a/src/v0/destinations/mp/util.js +++ b/src/v0/destinations/mp/util.js @@ -136,7 +136,7 @@ const createIdentifyResponse = (message, type, destination, responseBuilderSimpl * @returns */ const isImportAuthCredentialsAvailable = (destination) => - destination.Config.apiSecret || + destination.Config.token || (destination.Config.serviceAccountSecret && destination.Config.serviceAccountUserName && destination.Config.projectId); @@ -179,7 +179,6 @@ const groupEventsByEndpoint = (events) => { const eventMap = { engage: [], groups: [], - track: [], import: [], }; const batchErrorRespList = []; @@ -204,7 +203,6 @@ const groupEventsByEndpoint = (events) => { return { engageEvents: eventMap.engage, groupsEvents: eventMap.groups, - trackEvents: eventMap.track, importEvents: eventMap.import, batchErrorRespList, }; @@ -349,7 +347,6 @@ const generatePageOrScreenCustomEventName = (message, userDefinedEventTemplate) * @param {Object} batchSize - The object containing the batch size for different endpoints. * @param {number} batchSize.engage - The batch size for engage endpoint. * @param {number} batchSize.groups - The batch size for group endpoint. - * @param {number} batchSize.track - The batch size for track endpoint. * @param {number} batchSize.import - The batch size for import endpoint. * @param {string} destinationId - The ID of the destination. * @returns {void} @@ -361,9 +358,6 @@ const recordBatchSizeMetrics = (batchSize, destinationId) => { stats.gauge('mixpanel_batch_group_pack_size', batchSize.groups, { destination_id: destinationId, }); - stats.gauge('mixpanel_batch_track_pack_size', batchSize.track, { - destination_id: destinationId, - }); stats.gauge('mixpanel_batch_import_pack_size', batchSize.import, { destination_id: destinationId, }); diff --git a/src/v0/destinations/mp/util.test.js b/src/v0/destinations/mp/util.test.js index 40cdb34649..3666081f59 100644 --- a/src/v0/destinations/mp/util.test.js +++ b/src/v0/destinations/mp/util.test.js @@ -18,7 +18,6 @@ describe('Unit test cases for groupEventsByEndpoint', () => { expect(result).toEqual({ engageEvents: [], groupsEvents: [], - trackEvents: [], importEvents: [], batchErrorRespList: [], }); @@ -122,19 +121,6 @@ describe('Unit test cases for groupEventsByEndpoint', () => { }, }, ], - trackEvents: [ - { - message: { - endpoint: '/track', - body: { - JSON_ARRAY: { - batch: '[{prop:4}]', - }, - }, - userId: 'user1', - }, - }, - ], importEvents: [ { message: { diff --git a/test/integrations/destinations/mp/common.ts b/test/integrations/destinations/mp/common.ts index 82f0e3202b..d40afa0c02 100644 --- a/test/integrations/destinations/mp/common.ts +++ b/test/integrations/destinations/mp/common.ts @@ -7,7 +7,7 @@ const defaultMockFns = () => { const sampleDestination: Destination = { Config: { apiKey: 'dummyApiKey', - token: 'dummyApiKey', + token: 'test_api_token', prefixProperties: true, useNativeSDK: false, }, diff --git a/test/integrations/destinations/mp/processor/data.ts b/test/integrations/destinations/mp/processor/data.ts index 2d70d15384..db5bc840c2 100644 --- a/test/integrations/destinations/mp/processor/data.ts +++ b/test/integrations/destinations/mp/processor/data.ts @@ -12,7 +12,7 @@ export const data = [ request: { body: [ { - destination: overrideDestination(sampleDestination, { token: 'dummyApiKey' }), + destination: overrideDestination(sampleDestination, { token: 'test_api_token' }), message: { anonymousId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', channel: 'web', @@ -87,14 +87,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","campaign_id":"test_name","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","campaign_id":"test_name","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -191,14 +194,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Viewed a Contact Us page","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us","category":"Contact","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Viewed a Contact Us page","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us","category":"Contact","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -272,14 +278,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Screen","properties":{"category":"communication","ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', + '[{"event":"Loaded a Screen","properties":{"category":"communication","ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', }, XML: {}, FORM: {}, @@ -360,14 +369,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Screen","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","ip":"0.0.0.0","$user_id":"hjiklmk","$screen_dpi":2,"mp_lib":"RudderLabs Android SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjiklmk","time":1579847342402,"name":"Contact Us","category":"Contact"}}]', + '[{"event":"Loaded a Screen","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","ip":"0.0.0.0","$user_id":"hjiklmk","$screen_dpi":2,"mp_lib":"RudderLabs Android SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjiklmk","time":1579847342402,"name":"Contact Us","category":"Contact"}}]', }, XML: {}, FORM: {}, @@ -440,14 +452,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Screen","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', + '[{"event":"Loaded a Screen","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', }, XML: {}, FORM: {}, @@ -550,7 +565,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -664,7 +679,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":45.89}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":45.89}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -686,7 +701,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$add":{"counter":1,"item_purchased":"2"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$add":{"counter":1,"item_purchased":"2"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -701,14 +716,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":45.89,"counter":1,"item_purchased":"2","number_of_logins":"","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","campaign_id":"test_name","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342403,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":45.89,"counter":1,"item_purchased":"2","number_of_logins":"","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","campaign_id":"test_name","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342403,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -796,14 +814,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"dummyApiKey"}}]', + '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"test_api_token"}}]', }, XML: {}, FORM: {}, @@ -933,7 +954,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -948,14 +969,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1089,7 +1113,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":34}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":34}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -1104,14 +1128,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","revenue":34,"key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","first_name":"Mickey","lastName":"Mouse","name":"Mickey Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","revenue":34,"key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","first_name":"Mickey","lastName":"Mouse","name":"Mickey Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1235,14 +1262,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":" new Order Completed totally","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":" new Order Completed totally","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1366,14 +1396,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":" Order Completed ","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"Billing Amount":"77","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":" Order Completed ","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"Billing Amount":"77","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1541,7 +1574,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -1628,7 +1661,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', + '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -1650,7 +1683,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', + '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', }, XML: {}, FORM: {}, @@ -1737,7 +1770,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"company":["testComp","testComp1"]},"$ip":"0.0.0.0"}]', + '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"company":["testComp","testComp1"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -1759,7 +1792,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp","$set":{"company":["testComp","testComp1"]}}]', + '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":["testComp","testComp1"]}}]', }, XML: {}, FORM: {}, @@ -1781,7 +1814,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp1","$set":{"company":["testComp","testComp1"]}}]', + '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp1","$set":{"company":["testComp","testComp1"]}}]', }, XML: {}, FORM: {}, @@ -1869,7 +1902,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', + '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -1891,7 +1924,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', + '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', }, XML: {}, FORM: {}, @@ -2019,7 +2052,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -2034,14 +2067,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api-eu.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api-eu.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstname":"Mickey","lastname":"Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstname":"Mickey","lastname":"Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -2129,7 +2165,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","$android_devices":["test_device_token"],"$os":"Android","$android_model":"Android SDK built for x86","$android_os_version":"8.1.0","$android_manufacturer":"Google","$android_app_version":"1.0","$android_app_version_code":"1.0","$android_brand":"Google"},"$token":"dummyApiKey","$distinct_id":"5094f5704b9cf2b3","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","$android_devices":["test_device_token"],"$os":"Android","$android_model":"Android SDK built for x86","$android_os_version":"8.1.0","$android_manufacturer":"Google","$android_app_version":"1.0","$android_app_version_code":"1.0","$android_brand":"Google"},"$token":"test_api_token","$distinct_id":"5094f5704b9cf2b3","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -2216,7 +2252,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"dummyApiKey","$distinct_id":"test_user_id","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"test_api_token","$distinct_id":"test_user_id","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -2233,7 +2269,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -2241,7 +2277,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"dummyApiKey"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"test_api_token"}}]', }, XML: {}, FORM: {}, @@ -2328,14 +2364,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Page","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","category":"communication","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Loaded a Page","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","category":"communication","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -2423,14 +2462,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"dummyApiKey"}}]', + '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"test_api_token"}}]', }, XML: {}, FORM: {}, @@ -2523,7 +2565,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","createdat":"2020-01-23T08:54:02.362Z","$ios_devices":["test_device_token"],"$ios_device_model":"Android SDK built for x86","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"dummyApiKey","$distinct_id":"test_user_id","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","createdat":"2020-01-23T08:54:02.362Z","$ios_devices":["test_device_token"],"$ios_device_model":"Android SDK built for x86","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"test_api_token","$distinct_id":"test_user_id","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -2540,7 +2582,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -2548,7 +2590,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"dummyApiKey"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"test_api_token"}}]', }, XML: {}, FORM: {}, @@ -2641,7 +2683,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -2733,7 +2775,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -2826,7 +2868,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -2924,7 +2966,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3020,7 +3062,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3115,7 +3157,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3209,7 +3251,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3304,7 +3346,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3405,7 +3447,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3502,17 +3544,29 @@ export const data = [ status: 200, body: [ { - error: - 'Event timestamp is older than 5 days and no API secret or service account credentials (i.e. username, secret and projectId) are provided in destination configuration', - statTags: { - destType: 'MP', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, + body: { + JSON: {}, + JSON_ARRAY: { + batch: + '[{"event":"FirstTrackCall12","properties":{"foo":"bar","$deviceId":"nkasdnkasd","anonymousId":"ea776ad0-3136-44fb-9216-5b1578609a2b","userId":"as09sufa09usaf09as0f9uasf","id":"as09sufa09usaf09as0f9uasf","firstName":"Bob","lastName":"Marley","name":"Bob Marley","age":43,"email":"bob@marleymail.com","phone":"+447748544123","birthday":"1987-01-01T20:08:59+0000","createdAt":"2022-01-21T14:10:12+0000","address":"51,B.L.T road, Kolkata-700060","description":"I am great","gender":"male","title":"Founder","username":"bobm","website":"https://bobm.com","randomProperty":"randomValue","$user_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$current_url":"http://127.0.0.1:7307/Testing/App_for_testingTool/","$referrer":"http://127.0.0.1:7307/Testing/","$screen_height":900,"$screen_width":1440,"$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.1.18","$insert_id":"0d5c1a4a-27e4-41da-a246-4d01f44e74bd","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1632986123523,"$browser":"Chrome","$browser_version":"93.0.4577.82"}}]', + }, + XML: {}, + FORM: {}, + }, + files: {}, + userId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', }, - statusCode: 400, + statusCode: 200, }, ], }, @@ -3686,7 +3740,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -3694,7 +3748,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"MainActivity","properties":{"name":"MainActivity","automatic":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$user_id":"test_user_id","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"dummyApiKey","distinct_id":"test_user_id","time":1520845503421}}]', + '[{"event":"MainActivity","properties":{"name":"MainActivity","automatic":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$user_id":"test_user_id","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"test_api_token","distinct_id":"test_user_id","time":1520845503421}}]', }, XML: {}, FORM: {}, @@ -3965,7 +4019,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402,"$ignore_time":true}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402,"$ignore_time":true}]', }, XML: {}, FORM: {}, @@ -4071,7 +4125,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4276,7 +4330,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"user1234","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"user1234","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4294,18 +4348,14 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: - 'Basic cnVkZGVyLmQyYTNmMS5tcC1zZXJ2aWNlLWFjY291bnQ6amF0cFF4Y2pNaDhlZXRrMXhySDNLalFJYnp5NGlYOGI=', - }, - params: { - project_id: '123456', - strict: 0, + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["user1234","e6ab2c5e-2cda-44a9-a962-e2f67df78bca"],"token":"dummyApiKey"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["user1234","e6ab2c5e-2cda-44a9-a962-e2f67df78bca"],"token":"test_api_token"}}]', }, XML: {}, FORM: {}, @@ -4390,7 +4440,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic ZHVtbXlBcGlLZXk6', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -4398,7 +4448,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', + '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', }, XML: {}, FORM: {}, @@ -4480,7 +4530,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic ZHVtbXlBcGlLZXk6', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -4488,7 +4538,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Opened","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', + '[{"event":"Application Opened","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', }, XML: {}, FORM: {}, @@ -4576,7 +4626,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"groupId":["testGroupId"]},"$ip":"0.0.0.0"}]', + '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"groupId":["testGroupId"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -4598,7 +4648,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"groupId","$group_id":"testGroupId","$set":{"company":"testComp","groupId":"groupIdInTraits"}}]', + '[{"$token":"test_api_token","$group_key":"groupId","$group_id":"testGroupId","$set":{"company":"testComp","groupId":"groupIdInTraits"}}]', }, XML: {}, FORM: {}, @@ -4625,7 +4675,7 @@ export const data = [ description: 'Track: set device id and user id when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { - token: 'dummyApiKey', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -4681,14 +4731,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Product Viewed","properties":{"name":"T-Shirt","$user_id":"userId01","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"dummyApiKey","distinct_id":"userId01","time":1579847342402,"$device_id":"anonId01"}}]', + '[{"event":"Product Viewed","properties":{"name":"T-Shirt","$user_id":"userId01","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"test_api_token","distinct_id":"userId01","time":1579847342402,"$device_id":"anonId01"}}]', }, XML: {}, FORM: {}, @@ -4717,7 +4770,7 @@ export const data = [ { description: 'Identify: skip merge event when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { - token: 'dummyApiKey', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -4798,7 +4851,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"userId01","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"userId01","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4826,7 +4879,7 @@ export const data = [ 'Identify: append $device: to deviceId while creating the user when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'dummyApiKey', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -4906,7 +4959,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"$device:anonId01","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"$device:anonId01","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4933,7 +4986,7 @@ export const data = [ description: 'Unsupported alias call when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'dummyApiKey', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -5022,7 +5075,7 @@ export const data = [ 'Track revenue event: set device id and user id when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'dummyApiKey', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -5093,7 +5146,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":18.9}},"$token":"dummyApiKey","$distinct_id":"userId01"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":18.9}},"$token":"test_api_token","$distinct_id":"userId01"}]', }, XML: {}, FORM: {}, @@ -5108,14 +5161,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":18.9,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$user_id":"userId01","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"dummyApiKey","distinct_id":"userId01","time":1579847342403,"$device_id":"anonId01","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":18.9,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$user_id":"userId01","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"test_api_token","distinct_id":"userId01","time":1579847342403,"$device_id":"anonId01","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -5145,7 +5201,7 @@ export const data = [ description: 'Page with anonymous user when simplified api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'dummyApiKey', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -5212,14 +5268,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"$device:anonId01","time":1579847342402,"$device_id":"anonId01","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"$device:anonId01","time":1579847342402,"$device_id":"anonId01","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -5249,7 +5308,7 @@ export const data = [ description: 'Group call with anonymous user when simplified api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'dummyApiKey', + token: 'test_api_token', identityMergeApi: 'simplified', groupKeySettings: [{ groupKey: 'company' }], }), @@ -5312,7 +5371,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$distinct_id":"$device:anonId01","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', + '[{"$token":"test_api_token","$distinct_id":"$device:anonId01","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -5334,7 +5393,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', + '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', }, XML: {}, FORM: {}, @@ -5360,7 +5419,7 @@ export const data = [ { destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'dummyApiKey', + token: 'test_api_token', identityMergeApi: 'simplified', groupKeySettings: [{ groupKey: 'company' }], }), @@ -5479,7 +5538,7 @@ export const data = [ }, destination: overrideDestination(sampleDestination, { apiKey: 'dummyApiKey', - token: 'dummyApiKey', + token: 'test_api_token', apiSecret: 'dummyApiKey', useNewMapping: true, }), @@ -5505,7 +5564,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":12.13}},"$token":"dummyApiKey","$distinct_id":"39da706ec83d0e90"}]', + '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":12.13}},"$token":"test_api_token","$distinct_id":"39da706ec83d0e90"}]', }, XML: {}, FORM: {}, @@ -5522,7 +5581,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic ZHVtbXlBcGlLZXk6', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -5530,7 +5589,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":12.13,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', + '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":12.13,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', }, XML: {}, FORM: {}, @@ -5594,7 +5653,7 @@ export const data = [ }, destination: overrideDestination(sampleDestination, { apiKey: 'dummyApiKey', - token: 'dummyApiKey', + token: 'test_api_token', apiSecret: 'dummyApiKey', useNewMapping: true, }), @@ -5620,7 +5679,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":23.45}},"$token":"dummyApiKey","$distinct_id":"39da706ec83d0e90"}]', + '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":23.45}},"$token":"test_api_token","$distinct_id":"39da706ec83d0e90"}]', }, XML: {}, FORM: {}, @@ -5637,7 +5696,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic ZHVtbXlBcGlLZXk6', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -5645,7 +5704,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":23.45,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":null}}]', + '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":23.45,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":null}}]', }, XML: {}, FORM: {}, @@ -5672,7 +5731,7 @@ export const data = [ description: 'Track: with strict mode enabled', destination: overrideDestination(sampleDestination, { apiKey: 'dummyApiKey', - token: 'dummyApiKey', + token: 'test_api_token', apiSecret: 'some_api_secret', dataResidency: 'eu', strictMode: true, @@ -5736,7 +5795,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"dummyApiKey","$distinct_id":"test_user_id","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"test_api_token","$distinct_id":"test_user_id","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -5753,7 +5812,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 1 }, @@ -5761,7 +5820,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"dummyApiKey"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"test_api_token"}}]', }, XML: {}, FORM: {}, diff --git a/test/integrations/destinations/mp/router/data.ts b/test/integrations/destinations/mp/router/data.ts index 059e222e92..8716c9daa0 100644 --- a/test/integrations/destinations/mp/router/data.ts +++ b/test/integrations/destinations/mp/router/data.ts @@ -442,7 +442,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { @@ -509,7 +509,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { @@ -577,7 +577,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { @@ -1166,7 +1166,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { @@ -1232,7 +1232,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { @@ -1299,7 +1299,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { From c249a694d735f6d241a35b6e21f493c54890ac84 Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Wed, 22 May 2024 18:52:08 +0530 Subject: [PATCH 171/240] fix: standardise hashing for all CAPI integrations (#3379) * fix: standardize hashing for all CAPI integrations * fix: use trim before hashing for GAOC,GAEC,impact,YDSP,fb and fbconv --- .../v2/destinations/reddit/procWorkflow.yaml | 10 +++++----- .../facebook_offline_conversions/utils.js | 6 +++--- src/v0/destinations/fb/transform.js | 6 +++--- .../data/trackConfig.json | 10 +++++----- .../transform.js | 2 +- .../utils.js | 18 +++++++++++------- src/v0/destinations/impact/transform.js | 4 ++-- src/v0/destinations/pinterest_tag/utils.js | 6 +++--- src/v0/destinations/yahoo_dsp/util.js | 2 +- .../processor/data.ts | 2 +- .../destinations/fb/processor/data.ts | 2 +- .../processor/data.ts | 2 +- .../processor/data.ts | 2 +- .../destinations/impact/processor/data.ts | 2 +- .../pinterest_tag/processor/data.ts | 4 ++-- .../destinations/reddit/processor/data.ts | 8 ++++---- .../destinations/yahoo_dsp/processor/data.ts | 2 +- 17 files changed, 46 insertions(+), 42 deletions(-) diff --git a/src/cdk/v2/destinations/reddit/procWorkflow.yaml b/src/cdk/v2/destinations/reddit/procWorkflow.yaml index 7b989f15e4..06d2c95f25 100644 --- a/src/cdk/v2/destinations/reddit/procWorkflow.yaml +++ b/src/cdk/v2/destinations/reddit/procWorkflow.yaml @@ -36,13 +36,13 @@ steps: const os = (.message.context.os.name)? .message.context.os.name.toLowerCase(): null; const hashData = .destination.Config.hashData; let user = .message.().({ - "email": hashData ? $.SHA256({{{{$.getGenericPaths("email")}}}}) : ({{{{$.getGenericPaths("email")}}}}), - "external_id": hashData ? $.SHA256({{{{$.getGenericPaths("userId")}}}}) : ({{{{$.getGenericPaths("userId")}}}}), - "ip_address": hashData? $.SHA256(.context.ip || .request_ip) : (.context.ip || .request_ip), + "email": hashData ? $.SHA256({{{{$.getGenericPaths("email")}}}}.trim()) : ({{{{$.getGenericPaths("email")}}}}), + "external_id": hashData ? $.SHA256({{{{$.getGenericPaths("userId")}}}}.trim()) : ({{{{$.getGenericPaths("userId")}}}}), + "ip_address": hashData? $.SHA256(.context.ip.trim() || .request_ip.trim()) : (.context.ip || .request_ip), "uuid": .properties.uuid, "user_agent": .context.userAgent, - "idfa": $.isAppleFamily(os)? (hashData? $.SHA256(.context.device.advertisingId): .context.device.advertisingId): null, - "aaid": os === "android" && .context.device ? (hashData? $.SHA256(.context.device.advertisingId): .context.device.advertisingId): null, + "idfa": $.isAppleFamily(os)? (hashData? $.SHA256(.context.device.advertisingId.trim()): .context.device.advertisingId): null, + "aaid": os === "android" && .context.device ? (hashData? $.SHA256(.context.device.advertisingId.trim()): .context.device.advertisingId): null, "opt_out": .properties.optOut, "screen_dimensions": {"width": .context.screen.width, "height": .context.screen.height}, }); diff --git a/src/v0/destinations/facebook_offline_conversions/utils.js b/src/v0/destinations/facebook_offline_conversions/utils.js index c48de4e0b9..460ef71176 100644 --- a/src/v0/destinations/facebook_offline_conversions/utils.js +++ b/src/v0/destinations/facebook_offline_conversions/utils.js @@ -396,7 +396,7 @@ const preparePayload = (facebookOfflineConversionsPayload, destination) => { const keys = Object.keys(facebookOfflineConversionsPayload); keys.forEach((key) => { if (isHashRequired && HASHING_REQUIRED_KEYS.includes(key)) { - payload[key] = sha256(facebookOfflineConversionsPayload[key]); + payload[key] = sha256(facebookOfflineConversionsPayload[key].trim()); } else { payload[key] = facebookOfflineConversionsPayload[key]; } @@ -407,8 +407,8 @@ const preparePayload = (facebookOfflineConversionsPayload, destination) => { ? facebookOfflineConversionsPayload.name.split(' ') : null; if (split !== null && Array.isArray(split) && split.length === 2) { - payload.fn = isHashRequired ? sha256(split[0]) : split[0]; - payload.ln = isHashRequired ? sha256(split[1]) : split[1]; + payload.fn = isHashRequired ? sha256(split[0].trim()) : split[0]; + payload.ln = isHashRequired ? sha256(split[1].trim()) : split[1]; } delete payload.name; } diff --git a/src/v0/destinations/fb/transform.js b/src/v0/destinations/fb/transform.js index e6f8e986cf..1160cef407 100644 --- a/src/v0/destinations/fb/transform.js +++ b/src/v0/destinations/fb/transform.js @@ -90,7 +90,7 @@ function sanityCheckPayloadForTypesAndModifications(updatedEvent) { clonedUpdatedEvent[prop] !== '' ) { isUDSet = true; - clonedUpdatedEvent[prop] = sha256(clonedUpdatedEvent[prop].toLowerCase()); + clonedUpdatedEvent[prop] = sha256(clonedUpdatedEvent[prop].trim().toLowerCase()); } break; case 'ud[zp]': @@ -113,7 +113,7 @@ function sanityCheckPayloadForTypesAndModifications(updatedEvent) { } else { isUDSet = true; clonedUpdatedEvent[prop] = sha256( - clonedUpdatedEvent[prop].toLowerCase() === 'female' ? 'f' : 'm', + clonedUpdatedEvent[prop].trim().toLowerCase() === 'female' ? 'f' : 'm', ); } } @@ -128,7 +128,7 @@ function sanityCheckPayloadForTypesAndModifications(updatedEvent) { if (clonedUpdatedEvent[prop] && clonedUpdatedEvent[prop] !== '') { isUDSet = true; clonedUpdatedEvent[prop] = sha256( - clonedUpdatedEvent[prop].toLowerCase().replace(/ /g, ''), + clonedUpdatedEvent[prop].trim().toLowerCase().replace(/ /g, ''), ); } break; diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/data/trackConfig.json b/src/v0/destinations/google_adwords_enhanced_conversions/data/trackConfig.json index bf5485270b..c38b24598d 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/data/trackConfig.json +++ b/src/v0/destinations/google_adwords_enhanced_conversions/data/trackConfig.json @@ -55,7 +55,7 @@ "sourceFromGenericMap": true, "required": false, "metadata": { - "type": "hashToSha256" + "type": ["trim", "hashToSha256"] } }, { @@ -64,7 +64,7 @@ "sourceFromGenericMap": true, "required": false, "metadata": { - "type": "hashToSha256" + "type": ["trim", "hashToSha256"] } }, { @@ -73,7 +73,7 @@ "sourceFromGenericMap": true, "required": false, "metadata": { - "type": "hashToSha256" + "type": ["trim", "hashToSha256"] } }, { @@ -82,7 +82,7 @@ "sourceFromGenericMap": true, "required": false, "metadata": { - "type": "hashToSha256" + "type": ["trim", "hashToSha256"] } }, { @@ -127,7 +127,7 @@ "sourceKeys": ["context.traits.streetAddress", "context.traits.address"], "required": false, "metadata": { - "type": "hashToSha256" + "type": ["trim", "hashToSha256"] } } ] diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/transform.js b/src/v0/destinations/google_adwords_enhanced_conversions/transform.js index 0be7c3f0ee..55d0c16c8c 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/transform.js +++ b/src/v0/destinations/google_adwords_enhanced_conversions/transform.js @@ -24,7 +24,7 @@ const { JSON_MIME_TYPE } = require('../../util/constant'); const updateMappingJson = (mapping) => { const newMapping = []; mapping.forEach((element) => { - if (get(element, 'metadata.type') && element.metadata.type === 'hashToSha256') { + if (get(element, 'metadata.type') && element.metadata.type.includes('hashToSha256')) { element.metadata.type = 'toString'; } newMapping.push(element); diff --git a/src/v0/destinations/google_adwords_offline_conversions/utils.js b/src/v0/destinations/google_adwords_offline_conversions/utils.js index 70b42e2157..dfa892a769 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/utils.js +++ b/src/v0/destinations/google_adwords_offline_conversions/utils.js @@ -140,17 +140,17 @@ const buildAndGetAddress = (message, hashUserIdentifier) => { const address = constructPayload(message, trackAddStoreAddressConversionsMapping); if (address.hashed_last_name) { address.hashed_last_name = hashUserIdentifier - ? sha256(address.hashed_last_name).toString() + ? sha256(address.hashed_last_name.trim()).toString() : address.hashed_last_name; } if (address.hashed_first_name) { address.hashed_first_name = hashUserIdentifier - ? sha256(address.hashed_first_name).toString() + ? sha256(address.hashed_first_name.trim()).toString() : address.hashed_first_name; } if (address.hashed_street_address) { address.hashed_street_address = hashUserIdentifier - ? sha256(address.hashed_street_address).toString() + ? sha256(address.hashed_street_address.trim()).toString() : address.hashed_street_address; } return Object.keys(address).length > 0 ? address : null; @@ -269,8 +269,10 @@ const getAddConversionPayload = (message, Config) => { const phone = getFieldValueFromMessage(message, 'phone'); const userIdentifierInfo = { - email: hashUserIdentifier && isDefinedAndNotNull(email) ? sha256(email).toString() : email, - phone: hashUserIdentifier && isDefinedAndNotNull(phone) ? sha256(phone).toString() : phone, + email: + hashUserIdentifier && isDefinedAndNotNull(email) ? sha256(email.trim()).toString() : email, + phone: + hashUserIdentifier && isDefinedAndNotNull(phone) ? sha256(phone.trim()).toString() : phone, address: buildAndGetAddress(message, hashUserIdentifier), }; @@ -363,8 +365,10 @@ const getClickConversionPayloadAndEndpoint = ( // Ref - https://developers.google.com/google-ads/api/rest/reference/rest/v11/customers/uploadClickConversions#ClickConversion const userIdentifierInfo = { - email: hashUserIdentifier && isDefinedAndNotNull(email) ? sha256(email).toString() : email, - phone: hashUserIdentifier && isDefinedAndNotNull(phone) ? sha256(phone).toString() : phone, + email: + hashUserIdentifier && isDefinedAndNotNull(email) ? sha256(email.trim()).toString() : email, + phone: + hashUserIdentifier && isDefinedAndNotNull(phone) ? sha256(phone.trim()).toString() : phone, }; const keyName = getExisitingUserIdentifier(userIdentifierInfo, defaultUserIdentifier); diff --git a/src/v0/destinations/impact/transform.js b/src/v0/destinations/impact/transform.js index 2eefdf7992..729f988938 100644 --- a/src/v0/destinations/impact/transform.js +++ b/src/v0/destinations/impact/transform.js @@ -59,7 +59,7 @@ const buildPageLoadPayload = (message, campaignId, impactAppId, enableEmailHashi let payload = constructPayload(message, MAPPING_CONFIG[CONFIG_CATEGORIES.PAGELOAD.name]); if (isDefinedAndNotNull(payload.CustomerEmail)) { payload.CustomerEmail = enableEmailHashing - ? sha1(payload?.CustomerEmail) + ? sha1(payload?.CustomerEmail.trim()) : payload?.CustomerEmail; } payload.CampaignId = campaignId; @@ -155,7 +155,7 @@ const processTrackEvent = (message, Config) => { payload.ImpactAppId = impactAppId; if (isDefinedAndNotNull(payload.CustomerEmail)) { payload.CustomerEmail = enableEmailHashing - ? sha1(payload?.CustomerEmail) + ? sha1(payload?.CustomerEmail.trim()) : payload?.CustomerEmail; } diff --git a/src/v0/destinations/pinterest_tag/utils.js b/src/v0/destinations/pinterest_tag/utils.js index 340fba498e..57d595571f 100644 --- a/src/v0/destinations/pinterest_tag/utils.js +++ b/src/v0/destinations/pinterest_tag/utils.js @@ -41,8 +41,8 @@ const getHashedValue = (key, value) => { case 'fn': case 'ge': value = Array.isArray(value) - ? value.map((val) => val.toString().toLowerCase()) - : value.toString().toLowerCase(); + ? value.map((val) => val.toString().trim().toLowerCase()) + : value.toString().trim().toLowerCase(); break; case 'ph': // phone numbers should only contain digits & should not contain leading zeros @@ -53,7 +53,7 @@ const getHashedValue = (key, value) => { case 'zp': // zip fields should only contain digits value = Array.isArray(value) - ? value.map((val) => val.toString().replace(/\D/g, '')) + ? value.map((val) => val.toString().trim().replace(/\D/g, '')) : value.toString().replace(/\D/g, ''); break; case 'hashed_maids': diff --git a/src/v0/destinations/yahoo_dsp/util.js b/src/v0/destinations/yahoo_dsp/util.js index 255f84d1c9..54002a3bce 100644 --- a/src/v0/destinations/yahoo_dsp/util.js +++ b/src/v0/destinations/yahoo_dsp/util.js @@ -51,7 +51,7 @@ const populateIdentifiers = (audienceList, Config) => { } // here, hashing the data if is not hashed and pushing in the seedList array. if (hashRequired) { - seedList.push(sha256(userTraits[audienceAttribute])); + seedList.push(sha256(userTraits[audienceAttribute].trim())); } else { seedList.push(userTraits[audienceAttribute]); } diff --git a/test/integrations/destinations/facebook_offline_conversions/processor/data.ts b/test/integrations/destinations/facebook_offline_conversions/processor/data.ts index 26d9a5e2f9..90709b67a2 100644 --- a/test/integrations/destinations/facebook_offline_conversions/processor/data.ts +++ b/test/integrations/destinations/facebook_offline_conversions/processor/data.ts @@ -1436,7 +1436,7 @@ export const data = [ traits: { email: 'test@rudderstack.com', birthday: '2005-01-01T23:28:56.782Z', - firstName: 'test', + firstName: ' test', name: 'test rudderlabs', address: { city: 'kalkata', diff --git a/test/integrations/destinations/fb/processor/data.ts b/test/integrations/destinations/fb/processor/data.ts index a437b90855..b0b4ba9ecf 100644 --- a/test/integrations/destinations/fb/processor/data.ts +++ b/test/integrations/destinations/fb/processor/data.ts @@ -497,7 +497,7 @@ export const data = [ traits: { email: 'abc@gmail.com', anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - firstName: 'test', + firstName: ' test', lastName: 'last', gender: 1234, phone: '+91-9831311135', diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/processor/data.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/processor/data.ts index 0a9542a5d5..13b2609bf8 100644 --- a/test/integrations/destinations/google_adwords_enhanced_conversions/processor/data.ts +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/processor/data.ts @@ -1255,7 +1255,7 @@ export const data = [ }, traits: { phone: '912382193', - firstName: 'John', + firstName: ' John', lastName: 'Gomes', city: 'London', state: 'UK', diff --git a/test/integrations/destinations/google_adwords_offline_conversions/processor/data.ts b/test/integrations/destinations/google_adwords_offline_conversions/processor/data.ts index decb1e58c7..ab3e19dc2f 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/processor/data.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/processor/data.ts @@ -321,7 +321,7 @@ export const data = [ advertisingId: '44c97318-9040-4361-8bc7-4eb30f665ca8', }, traits: { - email: 'alex@example.com', + email: ' alex@example.com', phone: '+1-202-555-0146', firstName: 'John', lastName: 'Gomes', diff --git a/test/integrations/destinations/impact/processor/data.ts b/test/integrations/destinations/impact/processor/data.ts index 1e4e91e7ad..0841f44c62 100644 --- a/test/integrations/destinations/impact/processor/data.ts +++ b/test/integrations/destinations/impact/processor/data.ts @@ -26,7 +26,7 @@ export const data = [ namespace: 'com.rudderlabs.javascript', }, traits: { - email: 'user123@email.com', + email: ' user123@email.com', phone: '+917836362334', userId: 'user123', }, diff --git a/test/integrations/destinations/pinterest_tag/processor/data.ts b/test/integrations/destinations/pinterest_tag/processor/data.ts index 65b33e7740..48b624645f 100644 --- a/test/integrations/destinations/pinterest_tag/processor/data.ts +++ b/test/integrations/destinations/pinterest_tag/processor/data.ts @@ -19,8 +19,8 @@ export const data = [ userAgent: 'chrome', traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', - email: 'abc@gmail.com', - phone: '+1234589947', + email: ' abc@gmail.com', + phone: '+1234589947 ', gender: 'non-binary', db: '19950715', lastname: 'Rudderlabs', diff --git a/test/integrations/destinations/reddit/processor/data.ts b/test/integrations/destinations/reddit/processor/data.ts index a97ae23d2a..e7b36f56ff 100644 --- a/test/integrations/destinations/reddit/processor/data.ts +++ b/test/integrations/destinations/reddit/processor/data.ts @@ -12,13 +12,13 @@ export const data = [ message: { context: { traits: { - email: 'testone@gmail.com', + email: 'testone@gmail.com ', }, userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - ip: '54.100.200.255', + ip: ' 54.100.200.255', device: { - advertisingId: 'asfds7fdsihf734b34j43f', + advertisingId: ' asfds7fdsihf734b34j43f', }, os: { name: 'android', @@ -29,7 +29,7 @@ export const data = [ originalTimestamp: '2019-10-14T09:03:17.562Z', anonymousId: '123456', event: 'Order Completed', - userId: 'testuserId1', + userId: ' testuserId1', properties: { checkout_id: '12345', order_id: '1234', diff --git a/test/integrations/destinations/yahoo_dsp/processor/data.ts b/test/integrations/destinations/yahoo_dsp/processor/data.ts index eb607d60fb..3d04f9aa5c 100644 --- a/test/integrations/destinations/yahoo_dsp/processor/data.ts +++ b/test/integrations/destinations/yahoo_dsp/processor/data.ts @@ -51,7 +51,7 @@ export const data = [ }, { ipAddress: 'fdffddf', - email: 'van@abc.com', + email: 'van@abc.com ', deviceId: 'djfdjfkdjf', phone: '@09876543210', firstName: 'test', From 6e7b5a0d8bf2c859dfb15b9cad7ed6070bd0892b Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Wed, 22 May 2024 19:15:05 +0530 Subject: [PATCH 172/240] fix: tiktok_v2 remove default value for content-type for custom events (#3383) --- .../tiktok_ads/data/TikTokTrackV2.json | 5 +---- src/v0/destinations/tiktok_ads/transformV2.js | 16 ++++++++++++---- .../destinations/tiktok_ads/processor/data.ts | 4 +--- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/v0/destinations/tiktok_ads/data/TikTokTrackV2.json b/src/v0/destinations/tiktok_ads/data/TikTokTrackV2.json index 530d6e392a..2910f1b44c 100644 --- a/src/v0/destinations/tiktok_ads/data/TikTokTrackV2.json +++ b/src/v0/destinations/tiktok_ads/data/TikTokTrackV2.json @@ -25,10 +25,7 @@ }, { "destKey": "properties.content_type", - "sourceKeys": ["properties.contentType", "properties.content_type"], - "metadata": { - "defaultValue": "product" - } + "sourceKeys": ["properties.contentType", "properties.content_type"] }, { "destKey": "properties.shop_id", diff --git a/src/v0/destinations/tiktok_ads/transformV2.js b/src/v0/destinations/tiktok_ads/transformV2.js index 3bd8699e3a..4624ec9181 100644 --- a/src/v0/destinations/tiktok_ads/transformV2.js +++ b/src/v0/destinations/tiktok_ads/transformV2.js @@ -31,7 +31,7 @@ const { JSON_MIME_TYPE } = require('../../util/constant'); * @param {*} event * @returns track payload */ -const getTrackResponsePayload = (message, destConfig, event) => { +const getTrackResponsePayload = (message, destConfig, event, setDefaultForContentType = true) => { const payload = constructPayload(message, trackMappingV2); // if contents is not an array converting it into array @@ -53,6 +53,10 @@ const getTrackResponsePayload = (message, destConfig, event) => { if (destConfig.hashUserProperties && isDefinedAndNotNullAndNotEmpty(payload.user)) { payload.user = hashUserField(payload.user); } + // setting content-type default value in case of all standard event except `page-view` + if (!payload.properties?.content_type && setDefaultForContentType) { + payload.properties.content_type = 'product'; + } payload.event = event; // add partner name and return payload return removeUndefinedAndNullValues(payload); @@ -90,13 +94,17 @@ const trackResponseBuilder = async (message, { Config }) => { }); } }); - } else { + } else if (!eventNameMapping[event]) { /* + Custom Event Case -> if there exists no event mapping we will build payload with custom event recieved For custom event we do not want to lower case the event or trim it we just want to send those as it is Doc https://ads.tiktok.com/help/article/standard-events-parameters?lang=en */ - event = eventNameMapping[event] || message.event; - // if there exists no event mapping we will build payload with custom event recieved + event = message.event; + responseList.push(getTrackResponsePayload(message, Config, event, false)); + } else { + // incoming event name is already a standard event name + event = eventNameMapping[event]; responseList.push(getTrackResponsePayload(message, Config, event)); } // set event source and event_source_id diff --git a/test/integrations/destinations/tiktok_ads/processor/data.ts b/test/integrations/destinations/tiktok_ads/processor/data.ts index 429024b8a9..af58b66302 100644 --- a/test/integrations/destinations/tiktok_ads/processor/data.ts +++ b/test/integrations/destinations/tiktok_ads/processor/data.ts @@ -5224,7 +5224,6 @@ export const data = [ event_id: '1616318632825_357', event_time: 1600372167, properties: { - content_type: 'product', contents: [ { price: 8, @@ -6872,7 +6871,7 @@ export const data = [ }, { name: 'tiktok_ads', - description: 'Test 46 -> V2 -> Event with no properties', + description: 'Test 46 -> V2 -> Custom Event with no properties', feature: 'processor', module: 'destination', version: 'v0', @@ -6947,7 +6946,6 @@ export const data = [ event: 'customEvent', event_id: '84e26acc-56a5-4835-8233-591137fca468', event_time: 1600372167, - properties: { content_type: 'product' }, user: { locale: 'en-US', email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', From d842da87a34cb63023eba288e0c5258e29997dcf Mon Sep 17 00:00:00 2001 From: Manish Kumar <144022547+manish339k@users.noreply.github.com> Date: Thu, 23 May 2024 11:40:05 +0530 Subject: [PATCH 173/240] feat: filtering unknown events in awin (#3392) * feat: filtering unknown events in awin * fix: apply suggestions from code review Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> --------- Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> --- src/v0/destinations/awin/transform.js | 7 ++++--- test/integrations/destinations/awin/data.ts | 6 ++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/v0/destinations/awin/transform.js b/src/v0/destinations/awin/transform.js index 68dd9d62e1..0e1e220548 100644 --- a/src/v0/destinations/awin/transform.js +++ b/src/v0/destinations/awin/transform.js @@ -3,6 +3,7 @@ const { BASE_URL, ConfigCategory, mappingConfig } = require('./config'); const { defaultRequestConfig, constructPayload, simpleProcessRouterDest } = require('../../util'); const { getParams, trackProduct, populateCustomTransactionProperties } = require('./utils'); +const { FilteredEventsError } = require('../../util/errorTypes'); const responseBuilder = (message, { Config }) => { const { advertiserId, eventsToTrack, customFieldMap } = Config; @@ -33,9 +34,9 @@ const responseBuilder = (message, { Config }) => { ...customTransactionProperties, }; } else { - throw new InstrumentationError( - "Event is not present in 'Events to Track' list. Aborting message.", - 400, + throw new FilteredEventsError( + "Event is not present in 'Events to Track' list. Dropping the event.", + 298, ); } } diff --git a/test/integrations/destinations/awin/data.ts b/test/integrations/destinations/awin/data.ts index 5a7fcfb50f..821b55637e 100644 --- a/test/integrations/destinations/awin/data.ts +++ b/test/integrations/destinations/awin/data.ts @@ -828,16 +828,14 @@ export const data = [ status: 200, body: [ { - error: "Event is not present in 'Events to Track' list. Aborting message.", + error: "Event is not present in 'Events to Track' list. Dropping the event.", statTags: { destType: 'AWIN', - errorCategory: 'dataValidation', - errorType: 'instrumentation', feature: 'processor', implementation: 'native', module: 'destination', }, - statusCode: 400, + statusCode: 298, }, ], }, From 732fbf47ca0043d38535afc3c45b191d73b474c6 Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Thu, 23 May 2024 15:33:24 +0530 Subject: [PATCH 174/240] chore: added credentials to javascript transformations --- src/types/index.ts | 7 +++++++ src/util/customTransformer-v1.js | 2 ++ src/util/customTransformer.js | 2 ++ src/util/customTransformerFactory.js | 10 ++++++++-- src/util/ivmFactory.js | 19 ++++++++++++++++--- 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/types/index.ts b/src/types/index.ts index 68dfe3870d..25b9beae01 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -137,12 +137,19 @@ type UserTransformationLibrary = { VersionID: string; }; +type Credential = { + Key: string; + Value: string; + isPublic: boolean; +}; + type ProcessorTransformationRequest = { request?: object; message: object; metadata: Metadata; destination: Destination; libraries?: UserTransformationLibrary[]; + credentials?: Credential[]; }; type RouterTransformationRequestData = { diff --git a/src/util/customTransformer-v1.js b/src/util/customTransformer-v1.js index 7e854a3714..1b3e270f52 100644 --- a/src/util/customTransformer-v1.js +++ b/src/util/customTransformer-v1.js @@ -51,6 +51,7 @@ function calculateMsFromIvmTime(value) { async function userTransformHandlerV1( events, userTransformation, + credentials, libraryVersionIds, testMode = false, ) { @@ -62,6 +63,7 @@ async function userTransformHandlerV1( userTransformation.code, libraryVersionIds, userTransformation.versionId, + credentials, userTransformation.secrets || {}, testMode, ); diff --git a/src/util/customTransformer.js b/src/util/customTransformer.js index a87c12dd6e..8094119ef9 100644 --- a/src/util/customTransformer.js +++ b/src/util/customTransformer.js @@ -285,12 +285,14 @@ async function userTransformHandler( events.forEach((ev) => { eventsMetadata[ev.message.messageId] = ev.metadata; }); + const credentials = events[0].credentials; let userTransformedEvents = []; let result; if (res.codeVersion && res.codeVersion === '1') { result = await UserTransformHandlerFactory(res).runUserTransfrom( events, + credentials, testMode, libraryVersionIDs, ); diff --git a/src/util/customTransformerFactory.js b/src/util/customTransformerFactory.js index ee53531946..f6bbbc492c 100644 --- a/src/util/customTransformerFactory.js +++ b/src/util/customTransformerFactory.js @@ -13,14 +13,20 @@ const UserTransformHandlerFactory = (userTransformation) => { } }, - runUserTransfrom: async (events, testMode, libraryVersionIds) => { + runUserTransfrom: async (events, credentials, testMode, libraryVersionIds) => { switch (userTransformation.language) { case 'pythonfaas': case 'python': return runOpenFaasUserTransform(events, userTransformation, libraryVersionIds, testMode); default: - return userTransformHandlerV1(events, userTransformation, libraryVersionIds, testMode); + return userTransformHandlerV1( + events, + userTransformation, + credentials, + libraryVersionIds, + testMode, + ); } }, }; diff --git a/src/util/ivmFactory.js b/src/util/ivmFactory.js index 9a6419295d..62da76295d 100644 --- a/src/util/ivmFactory.js +++ b/src/util/ivmFactory.js @@ -30,7 +30,7 @@ async function loadModule(isolateInternal, contextInternal, moduleName, moduleCo return module; } -async function createIvm(code, libraryVersionIds, versionId, secrets, testMode) { +async function createIvm(code, libraryVersionIds, versionId, credentials, secrets, testMode) { const createIvmStartTime = new Date(); const logs = []; const libraries = await Promise.all( @@ -243,6 +243,11 @@ async function createIvm(code, libraryVersionIds, versionId, secrets, testMode) }), ); + await jail.set('_credentials', function (...args) { + if (args.length == 0 || !credentials || !credentials[args[0]]) return 'ERROR'; + return credentials[args[0]]; + }); + await jail.set('_rsSecrets', function (...args) { if (args.length == 0 || !secrets || !secrets[args[0]]) return 'ERROR'; return secrets[args[0]]; @@ -321,6 +326,14 @@ async function createIvm(code, libraryVersionIds, versionId, secrets, testMode) ]); }; + let credentials = _credentials; + delete _credentials; + global.credentials = function(...args) { + return credentials([ + ...args.map(arg => new ivm.ExternalCopy(arg).copyInto()) + ]); + }; + return new ivm.Reference(function forwardMainPromise( fnRef, resolve, @@ -411,10 +424,10 @@ async function compileUserLibrary(code) { return evaluateModule(isolate, context, code); } -async function getFactory(code, libraryVersionIds, versionId, secrets, testMode) { +async function getFactory(code, libraryVersionIds, versionId, credentials, secrets, testMode) { const factory = { create: async () => { - return createIvm(code, libraryVersionIds, versionId, secrets, testMode); + return createIvm(code, libraryVersionIds, versionId, credentials, secrets, testMode); }, destroy: async (client) => { client.fnRef.release(); From be20dc26ade2fa0212dc91126cf42087a84a07c9 Mon Sep 17 00:00:00 2001 From: Dhawal Sanghvi <43755122+dhawal1248@users.noreply.github.com> Date: Thu, 23 May 2024 16:51:11 +0530 Subject: [PATCH 175/240] feat: sre 456 ut move high cardinality histogram metrics to summaries cp (#3409) * chore(user-transformer): move some high cardinality metrics to summaries * chore(user-transformer): move some high cardinality metrics to summaries * chore: add flag to disable summary metric collection * chore: minor fix * chore: minor fix --- src/legacy/router.js | 3 + src/services/userTransform.ts | 12 ++- src/util/customTransformer-v1.js | 1 + src/util/customTransformer.js | 1 + src/util/customTransforrmationsStore-v1.js | 3 + src/util/customTransforrmationsStore.js | 1 + src/util/openfaas/index.js | 1 + src/util/prometheus.js | 97 +++++++++++++++++++++- src/util/stats.js | 24 +++++- src/util/statsd.js | 5 ++ 10 files changed, 141 insertions(+), 7 deletions(-) diff --git a/src/legacy/router.js b/src/legacy/router.js index 9dd83b5988..afc8c1a797 100644 --- a/src/legacy/router.js +++ b/src/legacy/router.js @@ -649,6 +649,9 @@ if (startDestTransformer) { stats.timing('user_transform_request_latency', startTime, { processSessions, }); + stats.timingSummary('user_transform_request_latency_summary', startTime, { + processSessions, + }); stats.increment('user_transform_requests', { processSessions }); stats.histogram('user_transform_output_events', transformedEvents.length, { processSessions, diff --git a/src/services/userTransform.ts b/src/services/userTransform.ts index 18c47ddc83..62980a935a 100644 --- a/src/services/userTransform.ts +++ b/src/services/userTransform.ts @@ -173,7 +173,17 @@ export class UserTransformService { ...getTransformationMetadata(eventsToProcess[0]?.metadata), }); - stats.histogram('user_transform_batch_size', requestSize, { + stats.timing('user_transform_batch_size', requestSize, { + ...metaTags, + ...getTransformationMetadata(eventsToProcess[0]?.metadata), + }); + + stats.timingSummary('user_transform_request_latency_summary', userFuncStartTime, { + ...metaTags, + ...getTransformationMetadata(eventsToProcess[0]?.metadata), + }); + + stats.timingSummary('user_transform_batch_size_summary', requestSize, { ...metaTags, ...getTransformationMetadata(eventsToProcess[0]?.metadata), }); diff --git a/src/util/customTransformer-v1.js b/src/util/customTransformer-v1.js index 7e854a3714..e9877a614d 100644 --- a/src/util/customTransformer-v1.js +++ b/src/util/customTransformer-v1.js @@ -93,6 +93,7 @@ async function userTransformHandlerV1( }; stats.counter('user_transform_function_input_events', events.length, tags); stats.timing('user_transform_function_latency', invokeTime, tags); + stats.timingSummary('user_transform_function_latency_summary', invokeTime, tags); } return { transformedEvents, logs }; diff --git a/src/util/customTransformer.js b/src/util/customTransformer.js index a87c12dd6e..37364ef5d0 100644 --- a/src/util/customTransformer.js +++ b/src/util/customTransformer.js @@ -260,6 +260,7 @@ async function runUserTransform( stats.counter('user_transform_function_input_events', events.length, tags); stats.timing('user_transform_function_latency', invokeTime, tags); + stats.timingSummary('user_transform_function_latency_summary', invokeTime, tags); } return { diff --git a/src/util/customTransforrmationsStore-v1.js b/src/util/customTransforrmationsStore-v1.js index 3263049b6f..6e2d799f3a 100644 --- a/src/util/customTransforrmationsStore-v1.js +++ b/src/util/customTransforrmationsStore-v1.js @@ -31,6 +31,7 @@ async function getTransformationCodeV1(versionId) { responseStatusHandler(response.status, 'Transformation', versionId, url); stats.increment('get_transformation_code', { success: 'true', ...tags }); stats.timing('get_transformation_code_time', startTime, tags); + stats.timingSummary('get_transformation_code_time_summary', startTime, tags); const myJson = await response.json(); transformationCache[versionId] = myJson; return myJson; @@ -56,6 +57,7 @@ async function getLibraryCodeV1(versionId) { responseStatusHandler(response.status, 'Transformation Library', versionId, url); stats.increment('get_libraries_code', { success: 'true', ...tags }); stats.timing('get_libraries_code_time', startTime, tags); + stats.timingSummary('get_libraries_code_time_summary', startTime, tags); const myJson = await response.json(); libraryCache[versionId] = myJson; return myJson; @@ -83,6 +85,7 @@ async function getRudderLibByImportName(importName) { responseStatusHandler(response.status, 'Rudder Library', importName, url); stats.increment('get_libraries_code', { success: 'true', ...tags }); stats.timing('get_libraries_code_time', startTime, tags); + stats.timingSummary('get_libraries_code_time_summary', startTime, tags); const myJson = await response.json(); rudderLibraryCache[importName] = myJson; return myJson; diff --git a/src/util/customTransforrmationsStore.js b/src/util/customTransforrmationsStore.js index 08d417c07c..2c5a7b446d 100644 --- a/src/util/customTransforrmationsStore.js +++ b/src/util/customTransforrmationsStore.js @@ -24,6 +24,7 @@ async function getTransformationCode(versionId) { responseStatusHandler(response.status, 'Transformation', versionId, url); stats.increment('get_transformation_code', { versionId, success: 'true' }); stats.timing('get_transformation_code_time', startTime, { versionId }); + stats.timingSummary('get_transformation_code_time_summary', startTime, { versionId }); const myJson = await response.json(); myCache.set(versionId, myJson); return myJson; diff --git a/src/util/openfaas/index.js b/src/util/openfaas/index.js index 7a1fce3cfa..3cf3525e6f 100644 --- a/src/util/openfaas/index.js +++ b/src/util/openfaas/index.js @@ -305,6 +305,7 @@ const executeFaasFunction = async ( stats.counter('user_transform_function_input_events', events.length, tags); stats.timing('user_transform_function_latency', startTime, tags); + stats.timingSummary('user_transform_function_latency_summary', startTime, tags); } }; diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 882dff9e75..a618d35068 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -11,7 +11,7 @@ function appendPrefix(name) { } class Prometheus { - constructor() { + constructor(enableSummaryMetrics = true) { this.prometheusRegistry = new prometheusClient.Registry(); this.prometheusRegistry.setDefaultLabels(defaultLabels); prometheusClient.collectDefaultMetrics({ @@ -21,7 +21,7 @@ class Prometheus { prometheusClient.AggregatorRegistry.setRegistries(this.prometheusRegistry); this.aggregatorRegistry = new prometheusClient.AggregatorRegistry(); - this.createMetrics(); + this.createMetrics(enableSummaryMetrics); } async metricsController(ctx) { @@ -56,11 +56,22 @@ class Prometheus { return gauge; } - newSummaryStat(name, help, labelNames) { + newSummaryStat( + name, + help, + labelNames, + percentiles = [0.5, 0.9, 0.99], + maxAgeSeconds = 300, + ageBuckets = 5, + ) { + // we enable a 5 minute sliding window and calculate the 50th, 90th, and 99th percentiles by default const summary = new prometheusClient.Summary({ name, help, labelNames, + percentiles, + maxAgeSeconds, + ageBuckets, }); this.prometheusRegistry.registerMetric(summary); return summary; @@ -117,6 +128,21 @@ class Prometheus { } } + timingSummary(name, start, tags = {}) { + try { + let metric = this.prometheusRegistry.getSingleMetric(appendPrefix(name)); + if (!metric) { + logger.warn( + `Prometheus: summary metric ${name} not found in the registry. Creating a new one`, + ); + metric = this.newSummaryStat(name, name, Object.keys(tags)); + } + metric.observe(tags, (new Date() - start) / 1000); + } catch (e) { + logger.error(`Prometheus: Summary metric ${name} failed with error ${e}`); + } + } + histogram(name, value, tags = {}) { try { let metric = this.prometheusRegistry.getSingleMetric(appendPrefix(name)); @@ -166,7 +192,7 @@ class Prometheus { } } - createMetrics() { + createMetrics(enableSummaryMetrics) { const metrics = [ // Counters { @@ -698,6 +724,18 @@ class Prometheus { 'k8_namespace', ], }, + { + name: 'user_transform_request_latency_summary', + help: 'user_transform_request_latency_summary', + type: 'summary', + labelNames: [ + 'workspaceId', + 'transformationId', + 'sourceType', + 'destinationType', + 'k8_namespace', + ], + }, { name: 'user_transform_batch_size', help: 'user_transform_batch_size', @@ -714,6 +752,18 @@ class Prometheus { 524288000, ], // 1KB, 100KB, 0.5MB, 1MB, 10MB, 20MB, 50MB, 100MB, 200MB, 500MB }, + { + name: 'user_transform_batch_size_summary', + help: 'user_transform_batch_size_summary', + type: 'summary', + labelNames: [ + 'workspaceId', + 'transformationId', + 'sourceType', + 'destinationType', + 'k8_namespace', + ], + }, { name: 'source_transform_request_latency', help: 'source_transform_request_latency', @@ -770,12 +820,24 @@ class Prometheus { type: 'histogram', labelNames: ['versionId', 'version'], }, + { + name: 'get_transformation_code_time_summary', + help: 'get_transformation_code_time_summary', + type: 'summary', + labelNames: ['versionId', 'version'], + }, { name: 'get_libraries_code_time', help: 'get_libraries_code_time', type: 'histogram', labelNames: ['libraryVersionId', 'versionId', 'type', 'version'], }, + { + name: 'get_libraries_code_time_summary', + help: 'get_libraries_code_time_summary', + type: 'summary', + labelNames: ['libraryVersionId', 'versionId', 'type', 'version'], + }, { name: 'isolate_cpu_time', help: 'isolate_cpu_time', @@ -1027,6 +1089,22 @@ class Prometheus { 'workspaceId', ], }, + { + name: 'user_transform_function_latency_summary', + help: 'user_transform_function_latency_summary', + type: 'summary', + labelNames: [ + 'identifier', + 'testMode', + 'sourceType', + 'destinationType', + 'k8_namespace', + 'errored', + 'statusCode', + 'transformationId', + 'workspaceId', + ], + }, ]; metrics.forEach((metric) => { @@ -1042,6 +1120,17 @@ class Prometheus { metric.labelNames, metric.buckets, ); + } else if (metric.type === 'summary') { + if (enableSummaryMetrics) { + this.newSummaryStat( + appendPrefix(metric.name), + metric.help, + metric.labelNames, + metric.percentiles, + metric.maxAge, + metric.ageBuckets, + ); + } } else { logger.error( `Prometheus: Metric creation failed. Name: ${metric.name}. Invalid type: ${metric.type}`, diff --git a/src/util/stats.js b/src/util/stats.js index 9a32fd1de3..0aa13fc85c 100644 --- a/src/util/stats.js +++ b/src/util/stats.js @@ -4,6 +4,8 @@ const logger = require('../logger'); const enableStats = process.env.ENABLE_STATS !== 'false'; const statsClientType = process.env.STATS_CLIENT || 'statsd'; +// summary metrics are enabled by default. To disable set ENABLE_SUMMARY_METRICS='false'. +const enableSummaryMetrics = process.env.ENABLE_SUMMARY_METRICS !== 'false'; let statsClient; function init() { @@ -19,7 +21,7 @@ function init() { case 'prometheus': logger.info('setting up prometheus client'); - statsClient = new prometheus.Prometheus(); + statsClient = new prometheus.Prometheus(enableSummaryMetrics); break; default: @@ -38,6 +40,15 @@ const timing = (name, start, tags = {}) => { statsClient.timing(name, start, tags); }; +// timingSummary is used to record observations for a summary metric +const timingSummary = (name, start, tags = {}) => { + if (!enableStats || !statsClient || !enableSummaryMetrics) { + return; + } + + statsClient.timingSummary(name, start, tags); +}; + const increment = (name, tags = {}) => { if (!enableStats || !statsClient) { return; @@ -88,4 +99,13 @@ async function metricsController(ctx) { init(); -module.exports = { init, timing, increment, counter, gauge, histogram, metricsController }; +module.exports = { + init, + timing, + timingSummary, + increment, + counter, + gauge, + histogram, + metricsController, +}; diff --git a/src/util/statsd.js b/src/util/statsd.js index a32a6f6f30..7613de7975 100644 --- a/src/util/statsd.js +++ b/src/util/statsd.js @@ -21,6 +21,11 @@ class Statsd { this.statsdClient.timing(name, start, tags); } + // timingSummary is just a wrapper around timing for statsd.For prometheus, we will have to implement a different function. + timingSummary(name, start, tags = {}) { + this.statsdClient.timing(name, start, tags); + } + increment(name, tags = {}) { this.statsdClient.increment(name, 1, tags); } From 9280aca17b2277bb1b2d717238b9f6b48e637f68 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 23 May 2024 12:12:17 +0000 Subject: [PATCH 176/240] chore(release): 1.67.0 --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4258fdfcc5..9c04835cd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.67.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.66.1...v1.67.0) (2024-05-23) + + +### Features + +* sre 456 ut move high cardinality histogram metrics to summaries cp ([#3409](https://github.com/rudderlabs/rudder-transformer/issues/3409)) ([be20dc2](https://github.com/rudderlabs/rudder-transformer/commit/be20dc26ade2fa0212dc91126cf42087a84a07c9)) + ### [1.66.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.66.0...v1.66.1) (2024-05-20) diff --git a/package-lock.json b/package-lock.json index f51f3ccd8e..bfeb00963a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.66.1", + "version": "1.67.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.66.1", + "version": "1.67.0", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index 50a276ce42..7fa3a7330c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.66.1", + "version": "1.67.0", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From e6a3dbb711bfb6567baa43f42dc33b6be6db62cd Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Thu, 23 May 2024 18:53:23 +0530 Subject: [PATCH 177/240] chore: added tests --- ...user_transformation_input_credentials.json | 57 +++++++++++++++++++ test/__tests__/user_transformation.test.js | 28 +++++++++ 2 files changed, 85 insertions(+) create mode 100644 test/__tests__/data/user_transformation_input_credentials.json diff --git a/test/__tests__/data/user_transformation_input_credentials.json b/test/__tests__/data/user_transformation_input_credentials.json new file mode 100644 index 0000000000..370938a909 --- /dev/null +++ b/test/__tests__/data/user_transformation_input_credentials.json @@ -0,0 +1,57 @@ +[ + { + "message": { + "channel": "web", + "context": { + "app": { + "build": "1.0.0", + "name": "RudderLabs JavaScript SDK", + "namespace": "com.rudderlabs.javascript", + "version": "1.0.0" + }, + "traits": { + "email": "test@rudderstack.com", + "anonymousId": "12345" + }, + "library": { + "name": "RudderLabs JavaScript SDK", + "version": "1.0.0" + }, + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", + "locale": "en-US", + "ip": "0.0.0.0", + "os": { + "name": "", + "version": "" + }, + "screen": { + "density": 2 + } + }, + "type": "track", + "messageId": "ec5481b6-a926-4d2e-b293-0b3a77c4d3be", + "originalTimestamp": "2019-10-14T11:15:18.300Z", + "anonymousId": "00000000000000000000000000", + "userId": "12345", + "event": "test track event GA3", + "properties": { + "user_actual_role": "system_admin, system_user", + "user_actual_id": 12345 + }, + "integrations": { + "All": true + }, + "sentAt": "2019-10-14T11:15:53.296Z" + }, + "destination": { + "Config": { + "trackingID": "UA-149602794-1" + }, + "Enabled": true + }, + "credentials": { + "key1": "value1", + "key2": "value2" + } + } +] diff --git a/test/__tests__/user_transformation.test.js b/test/__tests__/user_transformation.test.js index 924bf4f791..de6a587d1e 100644 --- a/test/__tests__/user_transformation.test.js +++ b/test/__tests__/user_transformation.test.js @@ -243,6 +243,34 @@ describe("User transformation", () => { expect(output[0].transformedEvent.dummy_key).toEqual(secrets.dummy_key); }); + it(`Simple ${name} Test with credentials for codeVersion 1`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformEvent(event, metadata) { + event.credentialValue = credentials("key1"); + return event; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].transformedEvent.credentialValue).toEqual("value1"); + }); + it(`Simple async ${name} FetchV2 Test for V0 transformation`, async () => { const versionId = randomID(); const inputData = require(`./data/${integration}_input.json`); From a7e4ba4f5826e835560b31d22e20447d0696d7bd Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Fri, 24 May 2024 11:48:05 +0530 Subject: [PATCH 178/240] chore: change in credentials structure --- src/util/customTransformer.js | 6 ++++-- .../user_transformation_input_credentials.json | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/util/customTransformer.js b/src/util/customTransformer.js index 8094119ef9..2573b82e6a 100644 --- a/src/util/customTransformer.js +++ b/src/util/customTransformer.js @@ -285,8 +285,10 @@ async function userTransformHandler( events.forEach((ev) => { eventsMetadata[ev.message.messageId] = ev.metadata; }); - const credentials = events[0].credentials; - + const credentials = {}; + events[0]?.credentials?.forEach((cred) => { + credentials[cred.key] = cred.value; + }); let userTransformedEvents = []; let result; if (res.codeVersion && res.codeVersion === '1') { diff --git a/test/__tests__/data/user_transformation_input_credentials.json b/test/__tests__/data/user_transformation_input_credentials.json index 370938a909..49b224e560 100644 --- a/test/__tests__/data/user_transformation_input_credentials.json +++ b/test/__tests__/data/user_transformation_input_credentials.json @@ -49,9 +49,17 @@ }, "Enabled": true }, - "credentials": { - "key1": "value1", - "key2": "value2" - } + "credentials": [ + { + "key": "key1", + "value": "value1", + "isPublic": true + }, + { + "key": "key2", + "value": "value2", + "isPublic": true + } + ] } ] From 668d3311aadacbb92b1873bf43919db7d341afbb Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Fri, 24 May 2024 14:38:58 +0530 Subject: [PATCH 179/240] feat: onboarding adjust source (#3395) * fix: onboard adjust source * feat: onboard adjust source * feat: small edit * feat: small edit --- src/v0/sources/adjust/config.ts | 16 +++ src/v0/sources/adjust/mapping.json | 52 ++++++++++ src/v0/sources/adjust/transform.js | 61 ++++++++++++ test/integrations/sources/adjust/data.ts | 122 +++++++++++++++++++++++ 4 files changed, 251 insertions(+) create mode 100644 src/v0/sources/adjust/config.ts create mode 100644 src/v0/sources/adjust/mapping.json create mode 100644 src/v0/sources/adjust/transform.js create mode 100644 test/integrations/sources/adjust/data.ts diff --git a/src/v0/sources/adjust/config.ts b/src/v0/sources/adjust/config.ts new file mode 100644 index 0000000000..d1c6ab8242 --- /dev/null +++ b/src/v0/sources/adjust/config.ts @@ -0,0 +1,16 @@ +export const excludedFieldList = [ + 'activity_kind', + 'event', + 'event_name', + 'gps_adid', + 'idfa', + 'idfv', + 'adid', + 'tracker', + 'tracker_name', + 'app_name', + 'ip_address', + 'tracking_enabled', + 'tracker_token', + 'created_at', +]; diff --git a/src/v0/sources/adjust/mapping.json b/src/v0/sources/adjust/mapping.json new file mode 100644 index 0000000000..60ea66281e --- /dev/null +++ b/src/v0/sources/adjust/mapping.json @@ -0,0 +1,52 @@ +[ + { + "sourceKeys": "activity_kind", + "destKeys": "properties.activity_kind" + }, + { + "sourceKeys": "event", + "destKeys": "properties.event_token" + }, + { + "sourceKeys": "event_name", + "destKeys": "event" + }, + { + "sourceKeys": "gps_adid", + "destKeys": "properties.gps_adid" + }, + { + "sourceKeys": "idfa", + "destKeys": "context.device.advertisingId" + }, + { + "sourceKeys": "idfv", + "destKeys": "context.device.id" + }, + { + "sourceKeys": "adid", + "destKeys": "context.device.id " + }, + { + "sourceKeys": "tracker", + "destKeys": "properties.tracker" + }, + { + "sourceKeys": "tracker_name", + "destKeys": "properties.tracker_name" + }, + { "sourceKeys": "tracker_token", "destKeys": "properties.tracker_token" }, + + { + "sourceKeys": "app_name", + "destKeys": "context.app.name" + }, + { + "sourceKeys": "ip_address", + "destKeys": ["context.ip", "request_ip"] + }, + { + "sourceKeys": "tracking_enabled", + "destKeys": "properties.tracking_enabled" + } +] diff --git a/src/v0/sources/adjust/transform.js b/src/v0/sources/adjust/transform.js new file mode 100644 index 0000000000..8568622aeb --- /dev/null +++ b/src/v0/sources/adjust/transform.js @@ -0,0 +1,61 @@ +const lodash = require('lodash'); +const path = require('path'); +const fs = require('fs'); +const { TransformationError, structuredLogger: logger } = require('@rudderstack/integrations-lib'); +const Message = require('../message'); +const { CommonUtils } = require('../../../util/common'); +const { excludedFieldList } = require('./config'); +const { extractCustomFields, generateUUID } = require('../../util'); + +// ref : https://help.adjust.com/en/article/global-callbacks#general-recommended-placeholders +// import mapping json using JSON.parse to preserve object key order +const mapping = JSON.parse(fs.readFileSync(path.resolve(__dirname, './mapping.json'), 'utf-8')); + +const formatProperties = (input) => { + const { query_parameters: qParams } = input; + logger.debug(`[Adjust] Input event: query_params: ${JSON.stringify(qParams)}`); + if (!qParams) { + throw new TransformationError('Query_parameters is missing'); + } + const formattedOutput = {}; + Object.entries(qParams).forEach(([key, [value]]) => { + formattedOutput[key] = value; + }); + return formattedOutput; +}; + +const processEvent = (inputEvent) => { + const message = new Message(`Adjust`); + const event = lodash.cloneDeep(inputEvent); + const formattedPayload = formatProperties(event); + // event type is always track + const eventType = 'track'; + message.setEventType(eventType); + message.setPropertiesV2(formattedPayload, mapping); + let customProperties = {}; + customProperties = extractCustomFields( + formattedPayload, + customProperties, + 'root', + excludedFieldList, + ); + message.properties = { ...message.properties, ...customProperties }; + + if (formattedPayload.created_at) { + const ts = new Date(formattedPayload.created_at * 1000).toISOString(); + message.setProperty('originalTimestamp', ts); + message.setProperty('timestamp', ts); + } + + // adjust does not has the concept of user but we need to set some random anonymousId in order to make the server accept the message + message.anonymousId = generateUUID(); + return message; +}; + +// This fucntion just converts the incoming payload to array of already not and sends it to processEvent +const process = (events) => { + const eventsArray = CommonUtils.toArray(events); + return eventsArray.map(processEvent); +}; + +module.exports = { process }; diff --git a/test/integrations/sources/adjust/data.ts b/test/integrations/sources/adjust/data.ts new file mode 100644 index 0000000000..975543fbec --- /dev/null +++ b/test/integrations/sources/adjust/data.ts @@ -0,0 +1,122 @@ +import utils from '../../../../src/v0/util'; + +const defaultMockFns = () => { + jest.spyOn(utils, 'generateUUID').mockReturnValue('97fcd7b2-cc24-47d7-b776-057b7b199513'); +}; + +export const data = [ + { + name: 'adjust', + description: 'Simple track call', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + id: 'adjust', + query_parameters: { + gps_adid: ['38400000-8cf0-11bd-b23e-10b96e40000d'], + adid: ['18546f6171f67e29d1cb983322ad1329'], + tracker_token: ['abc'], + custom: ['custom'], + tracker_name: ['dummy'], + created_at: ['1404214665'], + event_name: ['Click'], + }, + updated_at: '2023-02-10T12:16:07.251Z', + created_at: '2023-02-10T12:05:04.402Z', + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + integration: { + name: 'Adjust', + }, + device: { + 'id ': '18546f6171f67e29d1cb983322ad1329', + }, + }, + integrations: { + Adjust: false, + }, + type: 'track', + event: 'Click', + originalTimestamp: '2014-07-01T11:37:45.000Z', + timestamp: '2014-07-01T11:37:45.000Z', + properties: { + gps_adid: '38400000-8cf0-11bd-b23e-10b96e40000d', + tracker_token: 'abc', + custom: 'custom', + tracker_name: 'dummy', + }, + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'adjust', + description: 'Simple track call with no query parameters', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + id: 'adjust', + updated_at: '2023-02-10T12:16:07.251Z', + created_at: '2023-02-10T12:05:04.402Z', + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + error: 'Query_parameters is missing', + statTags: { + destinationId: 'Non determinable', + errorCategory: 'transformation', + implementation: 'native', + module: 'source', + workspaceId: 'Non determinable', + }, + statusCode: 400, + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, +]; From d1a2bd61468c75f944135cf61cbf2464f08404ed Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Mon, 27 May 2024 12:08:59 +0530 Subject: [PATCH 180/240] fix: fb custom audience html response (#3402) --- src/v0/util/facebookUtils/index.js | 6 ++ src/v0/util/facebookUtils/index.test.js | 51 ++++++++++++++++ src/v0/util/facebookUtils/networkHandler.js | 13 ++++ .../fb_custom_audience/dataDelivery/other.ts | 47 ++++++++++++++- .../fb_custom_audience/network.ts | 60 +++++++++++++++++++ 5 files changed, 175 insertions(+), 2 deletions(-) diff --git a/src/v0/util/facebookUtils/index.js b/src/v0/util/facebookUtils/index.js index c7753d255f..7462320cca 100644 --- a/src/v0/util/facebookUtils/index.js +++ b/src/v0/util/facebookUtils/index.js @@ -292,7 +292,13 @@ const formingFinalResponse = ( throw new TransformationError('Payload could not be constructed'); }; +const isHtmlFormat = (string) => { + const htmlTags = /<(?!(!doctype\s*html|html))\b[^>]*>[\S\s]*?<\/[^>]*>/i; + return htmlTags.test(string); +}; + module.exports = { + isHtmlFormat, getContentType, getContentCategory, transformedPayloadData, diff --git a/src/v0/util/facebookUtils/index.test.js b/src/v0/util/facebookUtils/index.test.js index 20c4ee59f2..1a2de4ed12 100644 --- a/src/v0/util/facebookUtils/index.test.js +++ b/src/v0/util/facebookUtils/index.test.js @@ -3,6 +3,7 @@ const { fetchUserData, deduceFbcParam, getContentType, + isHtmlFormat, } = require('./index'); const sha256 = require('sha256'); const { MAPPING_CONFIG, CONFIG_CATEGORIES } = require('../../destinations/facebook_pixel/config'); @@ -639,3 +640,53 @@ describe('getContentType', () => { expect(result).toBe(defaultValue); }); }); + +describe('isHtmlFormat', () => { + it('should return false for Json', () => { + expect(isHtmlFormat('{"a": 1, "b":2}')).toBe(false); + }); + + it('should return false for empty Json', () => { + expect(isHtmlFormat('{}')).toBe(false); + }); + + it('should return false for undefined', () => { + expect(isHtmlFormat(undefined)).toBe(false); + }); + + it('should return false for null', () => { + expect(isHtmlFormat(null)).toBe(false); + }); + + it('should return false for empty array', () => { + expect(isHtmlFormat([])).toBe(false); + }); + + it('should return true for html doctype', () => { + expect( + isHtmlFormat( + '

Sorry, something went wrong.

We\'re working on it and we\'ll get it fixed as soon as we can.

Go Back

', + ), + ).toBe(true); + }); + + it('should return true for html', () => { + expect( + isHtmlFormat( + ' Hello, World!

Hello World!

', + ), + ).toBe(true); + }); + + it('should return true for html', () => { + expect( + isHtmlFormat( + '

Hello World!

', + ), + ).toBe(true); + }); + + it('should return false json type', () => { + expect(isHtmlFormat('{"": 12, "b": "test, "arr": [1,2]}')).toBe(false); + }); +}); diff --git a/src/v0/util/facebookUtils/networkHandler.js b/src/v0/util/facebookUtils/networkHandler.js index d5a731067f..a84128e140 100644 --- a/src/v0/util/facebookUtils/networkHandler.js +++ b/src/v0/util/facebookUtils/networkHandler.js @@ -14,6 +14,7 @@ const { } = require('../../../adapters/utils/networkUtils'); const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); const { ErrorDetailsExtractorBuilder } = require('../../../util/error-extractor'); +const { isHtmlFormat } = require('./index'); /** * Only under below mentioned scenario(s), add the errorCodes, subCodes etc,. to this map @@ -277,6 +278,18 @@ const errorResponseHandler = (destResponse) => { const destResponseHandler = (responseParams) => { const { destinationResponse } = responseParams; + + // check If the response is in html format + if (isHtmlFormat(destinationResponse.response) || isHtmlFormat(destinationResponse)) { + throw new NetworkError( + 'Invalid response format (HTML) during response transformation', + 500, + { + [TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(destinationResponse.status), + }, + destinationResponse, + ); + } errorResponseHandler(destinationResponse); return { destinationResponse: destinationResponse.response, diff --git a/test/integrations/destinations/fb_custom_audience/dataDelivery/other.ts b/test/integrations/destinations/fb_custom_audience/dataDelivery/other.ts index 52138604b0..2ad5964934 100644 --- a/test/integrations/destinations/fb_custom_audience/dataDelivery/other.ts +++ b/test/integrations/destinations/fb_custom_audience/dataDelivery/other.ts @@ -1,6 +1,6 @@ -import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; -import { ProxyV1TestData } from '../../../testTypes'; import { getEndPoint } from '../../../../../src/v0/destinations/fb_custom_audience/config'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; import { statTags, testParams2 as testParams } from './business'; export const otherScenariosV1: ProxyV1TestData[] = [ @@ -50,4 +50,47 @@ export const otherScenariosV1: ProxyV1TestData[] = [ }, }, }, + { + id: 'fbca_v1_other_scenario_2', + name: 'fb_custom_audience', + description: 'got invalid response format (not-json) from facebook', + successCriteria: 'should throw retyable error', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + method: 'DELETE', + endpoint: getEndPoint('aud1'), + headers: { + 'test-dest-response-key': 'htmlResponse', + }, + params: testParams, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + message: 'Invalid response format (HTML) during response transformation', + statTags, + response: [ + { + error: + '"

My First Heading

My first paragraph.

"', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + }, + }, ]; diff --git a/test/integrations/destinations/fb_custom_audience/network.ts b/test/integrations/destinations/fb_custom_audience/network.ts index 9b498bc07e..3331f874a3 100644 --- a/test/integrations/destinations/fb_custom_audience/network.ts +++ b/test/integrations/destinations/fb_custom_audience/network.ts @@ -523,4 +523,64 @@ export const networkCallsData = [ status: 400, }, }, + { + httpReq: { + version: '1', + type: 'REST', + method: 'DELETE', + endpoint: getEndPoint('aud1'), + headers: { + 'test-dest-response-key': 'htmlResponse', + }, + params: { + access_token: 'ABC', + payload: { + is_raw: true, + data_source: { + sub_type: 'ANYTHING', + }, + schema: [ + 'EMAIL', + 'DOBM', + 'DOBD', + 'DOBY', + 'PHONE', + 'GEN', + 'FI', + 'MADID', + 'ZIP', + 'ST', + 'COUNTRY', + ], + data: [ + [ + 'shrouti@abc.com', + '2', + '13', + '2013', + '@09432457768', + 'f', + 'Ms.', + 'ABC', + 'ZIP ', + '123abc ', + 'IN', + ], + ], + }, + }, + userId: '', + body: { + JSON: {}, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + }, + httpRes: { + data: '

My First Heading

My first paragraph.

', + status: 400, + }, + }, ]; From 402aa2c428cb71e66ee71301e99f13b7177b0f32 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 27 May 2024 06:49:22 +0000 Subject: [PATCH 181/240] chore(release): 1.68.0 --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c04835cd4..90a2152b90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,34 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.68.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.67.0...v1.68.0) (2024-05-27) + + +### Features + +* add json-data type support in redis ([#3336](https://github.com/rudderlabs/rudder-transformer/issues/3336)) ([0196f20](https://github.com/rudderlabs/rudder-transformer/commit/0196f20cc79e1f470d96a649dd9404c3c9284329)) +* facebook custom audience app secret support ([#3357](https://github.com/rudderlabs/rudder-transformer/issues/3357)) ([fce4ef9](https://github.com/rudderlabs/rudder-transformer/commit/fce4ef973500411c7ad812e7949bb1b73dabc3ba)) +* filtering unknown events in awin ([#3392](https://github.com/rudderlabs/rudder-transformer/issues/3392)) ([d842da8](https://github.com/rudderlabs/rudder-transformer/commit/d842da87a34cb63023eba288e0c5258e29997dcf)) +* **ga4:** component test refactor ([#3220](https://github.com/rudderlabs/rudder-transformer/issues/3220)) ([3ff9a5e](https://github.com/rudderlabs/rudder-transformer/commit/3ff9a5e8e955b929a1b04a89dcf0ccbc49e18648)) +* **integrations/auth0:** include Auth0 event type in Rudderstack message ([#3370](https://github.com/rudderlabs/rudder-transformer/issues/3370)) ([e9409fd](https://github.com/rudderlabs/rudder-transformer/commit/e9409fde6063d7eaa8558396b85b5fdf99f964e1)) +* onboard koddi destination ([88b2d57](https://github.com/rudderlabs/rudder-transformer/commit/88b2d5709da00445ffae54f5a36de855cb5f8479)) +* onboard koddi destination ([#3359](https://github.com/rudderlabs/rudder-transformer/issues/3359)) ([f74c4a0](https://github.com/rudderlabs/rudder-transformer/commit/f74c4a0bc92ae6ccb0c00ac5b21745e496a015bc)) +* onboarding adjust source ([#3395](https://github.com/rudderlabs/rudder-transformer/issues/3395)) ([668d331](https://github.com/rudderlabs/rudder-transformer/commit/668d3311aadacbb92b1873bf43919db7d341afbb)) + + +### Bug Fixes + +* added componenet test ([189cf93](https://github.com/rudderlabs/rudder-transformer/commit/189cf9367a907dc1848257733e13713245458579)) +* added conversions bidders validation and improved implementation ([ddf8d46](https://github.com/rudderlabs/rudder-transformer/commit/ddf8d46fed980204c561f95daa12fc740302e6e3)) +* config ([847e3e0](https://github.com/rudderlabs/rudder-transformer/commit/847e3e04d3ef67b9a7b5e35127251f3fc34ba3bf)) +* fb custom audience html response ([#3402](https://github.com/rudderlabs/rudder-transformer/issues/3402)) ([d1a2bd6](https://github.com/rudderlabs/rudder-transformer/commit/d1a2bd61468c75f944135cf61cbf2464f08404ed)) +* fixed some issue and added unit test ([bc7970c](https://github.com/rudderlabs/rudder-transformer/commit/bc7970c4f0a70e9fe8ad06ffd92f8f4b2a4ec910)) +* fixed unit test issue ([d4a82e2](https://github.com/rudderlabs/rudder-transformer/commit/d4a82e2d2df7ee86c4b149bca7e0c12be3a6a545)) +* minor mapping issue in conversions ([31e6460](https://github.com/rudderlabs/rudder-transformer/commit/31e6460ccc0c18014ebf67eab23b59abe5d81ef6)) +* resolving comments ([7c0d963](https://github.com/rudderlabs/rudder-transformer/commit/7c0d963d3ee87a3ed5712492300dc50768c529de)) +* standardise hashing for all CAPI integrations ([#3379](https://github.com/rudderlabs/rudder-transformer/issues/3379)) ([c249a69](https://github.com/rudderlabs/rudder-transformer/commit/c249a694d735f6d241a35b6e21f493c54890ac84)) +* tiktok_v2 remove default value for content-type for custom events ([#3383](https://github.com/rudderlabs/rudder-transformer/issues/3383)) ([6e7b5a0](https://github.com/rudderlabs/rudder-transformer/commit/6e7b5a0d8bf2c859dfb15b9cad7ed6070bd0892b)) + ## [1.67.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.66.1...v1.67.0) (2024-05-23) diff --git a/package-lock.json b/package-lock.json index bfeb00963a..b8e2c81a29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.67.0", + "version": "1.68.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.67.0", + "version": "1.68.0", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index 7fa3a7330c..ed15683d4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.67.0", + "version": "1.68.0", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From 7a2ab63674d40870af4d16f0673a2a2594c899e9 Mon Sep 17 00:00:00 2001 From: Abhimanyu Babbar Date: Tue, 28 May 2024 11:02:25 +0530 Subject: [PATCH 182/240] chore: added reconcile fn for openfaas fn pods (#3420) --- src/util/customTransformer-faas.js | 26 ++- src/util/openfaas/faasApi.js | 28 ++- src/util/openfaas/index.js | 205 ++++++++++++++------ test/__tests__/user_transformation.test.js | 215 ++++++++++++++++++--- 4 files changed, 377 insertions(+), 97 deletions(-) diff --git a/src/util/customTransformer-faas.js b/src/util/customTransformer-faas.js index 2c0bbfd8c0..9ac9804097 100644 --- a/src/util/customTransformer-faas.js +++ b/src/util/customTransformer-faas.js @@ -11,9 +11,10 @@ const { } = require('./openfaas'); const { getLibraryCodeV1 } = require('./customTransforrmationsStore-v1'); +const HASH_SECRET = process.env.OPENFAAS_FN_HASH_SECRET || ''; const libVersionIdsCache = new NodeCache(); -function generateFunctionName(userTransformation, libraryVersionIds, testMode) { +function generateFunctionName(userTransformation, libraryVersionIds, testMode, hashSecret = '') { if (userTransformation.versionId === FAAS_AST_VID) return FAAS_AST_FN_NAME; if (testMode) { @@ -21,10 +22,15 @@ function generateFunctionName(userTransformation, libraryVersionIds, testMode) { return funcName.substring(0, 63).toLowerCase(); } - const ids = [userTransformation.workspaceId, userTransformation.versionId].concat( + let ids = [userTransformation.workspaceId, userTransformation.versionId].concat( (libraryVersionIds || []).sort(), ); + if (hashSecret !== '') { + ids = ids.concat([hashSecret]); + } + + // FIXME: Why the id's are sorted ?! const hash = crypto.createHash('md5').update(`${ids}`).digest('hex'); return `fn-${userTransformation.workspaceId}-${hash}`.substring(0, 63).toLowerCase(); } @@ -90,7 +96,13 @@ async function setOpenFaasUserTransform( testMode, }; const functionName = - pregeneratedFnName || generateFunctionName(userTransformation, libraryVersionIds, testMode); + pregeneratedFnName || + generateFunctionName( + userTransformation, + libraryVersionIds, + testMode, + process.env.OPENFAAS_FN_HASH_SECRET, + ); const setupTime = new Date(); await setupFaasFunction( @@ -130,7 +142,13 @@ async function runOpenFaasUserTransform( const trMetadata = events[0].metadata ? getTransformationMetadata(events[0].metadata) : {}; // check and deploy faas function if not exists - const functionName = generateFunctionName(userTransformation, libraryVersionIds, testMode); + const functionName = generateFunctionName( + userTransformation, + libraryVersionIds, + testMode, + process.env.OPENFAAS_FN_HASH_SECRET, + ); + if (testMode) { await setOpenFaasUserTransform( userTransformation, diff --git a/src/util/openfaas/faasApi.js b/src/util/openfaas/faasApi.js index f8f830f6e4..b932b70032 100644 --- a/src/util/openfaas/faasApi.js +++ b/src/util/openfaas/faasApi.js @@ -1,6 +1,8 @@ const axios = require('axios'); const { RespStatusError, RetryRequestError } = require('../utils'); +const logger = require('../../logger'); + const OPENFAAS_GATEWAY_URL = process.env.OPENFAAS_GATEWAY_URL || 'http://localhost:8080'; const OPENFAAS_GATEWAY_USERNAME = process.env.OPENFAAS_GATEWAY_USERNAME || ''; const OPENFAAS_GATEWAY_PASSWORD = process.env.OPENFAAS_GATEWAY_PASSWORD || ''; @@ -12,7 +14,7 @@ const basicAuth = { const parseAxiosError = (error) => { if (error.response) { - const status = error.response.status || 400; + const status = error.response.status || 500; const errorData = error.response?.data; const message = (errorData && (errorData.message || errorData.error || errorData)) || error.message; @@ -61,6 +63,8 @@ const invokeFunction = async (functionName, payload) => }); const checkFunctionHealth = async (functionName) => { + logger.debug(`Checking function health: ${functionName}`); + return new Promise((resolve, reject) => { const url = `${OPENFAAS_GATEWAY_URL}/function/${functionName}`; axios @@ -76,8 +80,10 @@ const checkFunctionHealth = async (functionName) => { }); }; -const deployFunction = async (payload) => - new Promise((resolve, reject) => { +const deployFunction = async (payload) => { + logger.debug(`Deploying function: ${payload?.name}`); + + return new Promise((resolve, reject) => { const url = `${OPENFAAS_GATEWAY_URL}/system/functions`; axios .post(url, payload, { auth: basicAuth }) @@ -86,6 +92,21 @@ const deployFunction = async (payload) => reject(parseAxiosError(err)); }); }); +}; + +const updateFunction = async (fnName, payload) => { + logger.debug(`Updating function: ${fnName}`); + + return new Promise((resolve, reject) => { + const url = `${OPENFAAS_GATEWAY_URL}/system/functions`; + axios + .put(url, payload, { auth: basicAuth }) + .then((resp) => resolve(resp.data)) + .catch((err) => { + reject(parseAxiosError(err)); + }); + }); +}; module.exports = { deleteFunction, @@ -94,4 +115,5 @@ module.exports = { getFunctionList, invokeFunction, checkFunctionHealth, + updateFunction, }; diff --git a/src/util/openfaas/index.js b/src/util/openfaas/index.js index 3cf3525e6f..c0369deb81 100644 --- a/src/util/openfaas/index.js +++ b/src/util/openfaas/index.js @@ -4,6 +4,7 @@ const { deployFunction, invokeFunction, checkFunctionHealth, + updateFunction, } = require('./faasApi'); const logger = require('../../logger'); const { RetryRequestError, RespStatusError } = require('../utils'); @@ -33,6 +34,7 @@ const FAAS_AST_FN_NAME = 'fn-ast'; const CUSTOM_NETWORK_POLICY_WORKSPACE_IDS = process.env.CUSTOM_NETWORK_POLICY_WORKSPACE_IDS || ''; const customNetworkPolicyWorkspaceIds = CUSTOM_NETWORK_POLICY_WORKSPACE_IDS.split(','); const CUSTOMER_TIER = process.env.CUSTOMER_TIER || 'shared'; +const DISABLE_RECONCILE_FN = process.env.DISABLE_RECONCILE_FN == 'true' || false; // Initialise node cache const functionListCache = new NodeCache(); @@ -67,6 +69,8 @@ const awaitFunctionReadiness = async ( maxWaitInMs = 22000, waitBetweenIntervalsInMs = 250, ) => { + logger.debug(`Awaiting function readiness: ${functionName}`); + const executionPromise = new Promise(async (resolve) => { try { await callWithRetry( @@ -121,7 +125,7 @@ const invalidateFnCache = () => { functionListCache.set(FUNC_LIST_KEY, []); }; -const deployFaasFunction = async ( +const updateFaasFunction = async ( functionName, code, versionId, @@ -130,73 +134,50 @@ const deployFaasFunction = async ( trMetadata = {}, ) => { try { - logger.debug(`[Faas] Deploying a faas function: ${functionName}`); - let envProcess = 'python index.py'; - - const lvidsString = libraryVersionIDs.join(','); + logger.debug(`Updating faas fn: ${functionName}`); - if (!testMode) { - envProcess = `${envProcess} --vid ${versionId} --config-backend-url ${CONFIG_BACKEND_URL} --lvids "${lvidsString}"`; - } else { - envProcess = `${envProcess} --code "${code}" --config-backend-url ${CONFIG_BACKEND_URL} --lvids "${lvidsString}"`; - } - - const envVars = {}; - if (FAAS_ENABLE_WATCHDOG_ENV_VARS.trim().toLowerCase() === 'true') { - envVars.max_inflight = FAAS_MAX_INFLIGHT; - envVars.exec_timeout = FAAS_EXEC_TIMEOUT; - } - if (GEOLOCATION_URL) { - envVars.geolocation_url = GEOLOCATION_URL; - } - // labels - const labels = { - 'openfaas-fn': 'true', - 'parent-component': 'openfaas', - 'com.openfaas.scale.max': FAAS_MAX_PODS_IN_TEXT, - 'com.openfaas.scale.min': FAAS_MIN_PODS_IN_TEXT, - 'com.openfaas.scale.zero': FAAS_SCALE_ZERO, - 'com.openfaas.scale.zero-duration': FAAS_SCALE_ZERO_DURATION, - 'com.openfaas.scale.target': FAAS_SCALE_TARGET, - 'com.openfaas.scale.target-proportion': FAAS_SCALE_TARGET_PROPORTION, - 'com.openfaas.scale.type': FAAS_SCALE_TYPE, - transformationId: trMetadata.transformationId, - workspaceId: trMetadata.workspaceId, - team: 'data-management', - service: 'openfaas-fn', - customer: 'shared', - 'customer-tier': CUSTOMER_TIER, - }; - if ( - trMetadata.workspaceId && - customNetworkPolicyWorkspaceIds.includes(trMetadata.workspaceId) - ) { - labels['custom-network-policy'] = 'true'; + const payload = buildOpenfaasFn( + functionName, + code, + versionId, + libraryVersionIDs, + testMode, + trMetadata, + ); + await updateFunction(functionName, payload); + // wait for function to be ready and then set it in cache + await awaitFunctionReadiness(functionName); + setFunctionInCache(functionName); + } catch (error) { + // 404 is statuscode returned from openfaas community edition + // when the function don't exist, so we can safely ignore this error + // and let the function be created in the next step. + if (error.statusCode !== 404) { + throw error; } + } +}; - // TODO: investigate and add more required labels and annotations - const payload = { - service: functionName, - name: functionName, - image: FAAS_BASE_IMG, - envProcess, - envVars, - labels, - annotations: { - 'prometheus.io.scrape': 'true', - }, - limits: { - memory: FAAS_LIMITS_MEMORY, - cpu: FAAS_LIMITS_CPU, - }, - requests: { - memory: FAAS_REQUESTS_MEMORY, - cpu: FAAS_REQUESTS_CPU, - }, - }; +const deployFaasFunction = async ( + functionName, + code, + versionId, + libraryVersionIDs, + testMode, + trMetadata = {}, +) => { + try { + logger.debug(`Deploying faas fn: ${functionName}`); + const payload = buildOpenfaasFn( + functionName, + code, + versionId, + libraryVersionIDs, + testMode, + trMetadata, + ); await deployFunction(payload); - logger.debug('[Faas] Deployed a faas function'); } catch (error) { logger.error(`[Faas] Error while deploying ${functionName}: ${error.message}`); // To handle concurrent create requests, @@ -246,6 +227,95 @@ async function setupFaasFunction( } } +// reconcileFn runs everytime the service boot's up +// trying to update the functions which are not in cache to the +// latest label and envVars +const reconcileFn = async (name, versionId, libraryVersionIDs, trMetadata) => { + if (DISABLE_RECONCILE_FN) { + return; + } + + logger.debug(`Reconciling faas function: ${name}`); + try { + if (isFunctionDeployed(name)) { + return; + } + await updateFaasFunction(name, null, versionId, libraryVersionIDs, false, trMetadata); + } catch (error) { + logger.error( + `unexpected error occurred when reconciling the function ${name}: ${error.message}`, + ); + throw error; + } +}; + +// buildOpenfaasFn is helper function to build openfaas fn CRUD payload +function buildOpenfaasFn(name, code, versionId, libraryVersionIDs, testMode, trMetadata = {}) { + logger.debug(`Building faas fn: ${name}`); + + let envProcess = 'python index.py'; + const lvidsString = libraryVersionIDs.join(','); + + if (!testMode) { + envProcess = `${envProcess} --vid ${versionId} --config-backend-url ${CONFIG_BACKEND_URL} --lvids "${lvidsString}"`; + } else { + envProcess = `${envProcess} --code "${code}" --config-backend-url ${CONFIG_BACKEND_URL} --lvids "${lvidsString}"`; + } + + const envVars = {}; + + if (FAAS_ENABLE_WATCHDOG_ENV_VARS.trim().toLowerCase() === 'true') { + envVars.max_inflight = FAAS_MAX_INFLIGHT; + envVars.exec_timeout = FAAS_EXEC_TIMEOUT; + } + + if (GEOLOCATION_URL) { + envVars.geolocation_url = GEOLOCATION_URL; + } + + const labels = { + 'openfaas-fn': 'true', + 'parent-component': 'openfaas', + 'com.openfaas.scale.max': FAAS_MAX_PODS_IN_TEXT, + 'com.openfaas.scale.min': FAAS_MIN_PODS_IN_TEXT, + 'com.openfaas.scale.zero': FAAS_SCALE_ZERO, + 'com.openfaas.scale.zero-duration': FAAS_SCALE_ZERO_DURATION, + 'com.openfaas.scale.target': FAAS_SCALE_TARGET, + 'com.openfaas.scale.target-proportion': FAAS_SCALE_TARGET_PROPORTION, + 'com.openfaas.scale.type': FAAS_SCALE_TYPE, + transformationId: trMetadata.transformationId, + workspaceId: trMetadata.workspaceId, + team: 'data-management', + service: 'openfaas-fn', + customer: 'shared', + 'customer-tier': CUSTOMER_TIER, + }; + + if (trMetadata.workspaceId && customNetworkPolicyWorkspaceIds.includes(trMetadata.workspaceId)) { + labels['custom-network-policy'] = 'true'; + } + + return { + service: name, + name: name, + image: FAAS_BASE_IMG, + envProcess, + envVars, + labels, + annotations: { + 'prometheus.io.scrape': 'true', + }, + limits: { + memory: FAAS_LIMITS_MEMORY, + cpu: FAAS_LIMITS_CPU, + }, + requests: { + memory: FAAS_REQUESTS_MEMORY, + cpu: FAAS_REQUESTS_CPU, + }, + }; +} + const executeFaasFunction = async ( name, events, @@ -260,7 +330,11 @@ const executeFaasFunction = async ( let errorRaised; try { - if (testMode) await awaitFunctionReadiness(name); + if (testMode) { + await awaitFunctionReadiness(name); + } else { + await reconcileFn(name, versionId, libraryVersionIDs, trMetadata); + } return await invokeFunction(name, events); } catch (error) { logger.error(`Error while invoking ${name}: ${error.message}`); @@ -268,6 +342,7 @@ const executeFaasFunction = async ( if (error.statusCode === 404 && error.message.includes(`error finding function ${name}`)) { removeFunctionFromCache(name); + await setupFaasFunction(name, null, versionId, libraryVersionIDs, testMode, trMetadata); throw new RetryRequestError(`${name} not found`); } @@ -314,6 +389,8 @@ module.exports = { executeFaasFunction, setupFaasFunction, invalidateFnCache, + buildOpenfaasFn, FAAS_AST_VID, FAAS_AST_FN_NAME, + setFunctionInCache, }; diff --git a/test/__tests__/user_transformation.test.js b/test/__tests__/user_transformation.test.js index 924bf4f791..ffb53de16b 100644 --- a/test/__tests__/user_transformation.test.js +++ b/test/__tests__/user_transformation.test.js @@ -7,9 +7,11 @@ jest.mock("axios", () => ({ ...jest.requireActual("axios"), get: jest.fn(), post: jest.fn(), - delete: jest.fn() + delete: jest.fn(), + put: jest.fn() })); +const { generateFunctionName } = require('../../src/util/customTransformer-faas.js'); const { Response, Headers } = jest.requireActual("node-fetch"); const lodashCore = require("lodash/core"); const _ = require("lodash"); @@ -35,6 +37,7 @@ const { } = require("../../src/util/customTransformer"); const { parserForImport } = require("../../src/util/parser"); const { RetryRequestError, RespStatusError } = require("../../src/util/utils"); +const { buildOpenfaasFn, setFunctionInCache, invalidateFnCache } = require("../../src/util/openfaas/index"); const OPENFAAS_GATEWAY_URL = "http://localhost:8080"; const defaultBasicAuth = { @@ -88,8 +91,12 @@ const pyLibCode = (name, versionId) => { } } -const pyfaasFuncName = (workspaceId, versionId, libraryVersionIds=[]) => { - const ids = [workspaceId, versionId].concat(libraryVersionIds.sort()); +const pyfaasFuncName = (workspaceId, versionId, libraryVersionIds=[], hashSecret="") => { + let ids = [workspaceId, versionId].concat(libraryVersionIds.sort()); + if (hashSecret !== "") { + ids = ids.concat([hashSecret]); + } + const hash = crypto.createHash('md5').update(`${ids}`).digest('hex'); return `fn-${workspaceId}-${hash}` @@ -105,6 +112,19 @@ const getfetchResponse = (resp, url) => let importNameLibraryVersionIdsMap; +describe("User transformation utils", () => { + + it("generates the openfaas-fn name correctly", () => { + const fnName = generateFunctionName( + {workspaceId: 'workspaceId', transformationId: 'transformationId'}, + [], + false, + 'hash-secret'); + expect(fnName).toEqual('fn-workspaceid-34a32ade07ebbc7bc5ea795b8200de9f'); + }); + +}); + describe("User transformation", () => { beforeEach(() => { jest.resetAllMocks(); @@ -1386,6 +1406,7 @@ describe("Geolocation function", () => { // Running tests for python transformations with openfaas mocks describe("Python transformations", () => { beforeEach(() => { + invalidateFnCache(); jest.resetAllMocks(); }); afterAll(() => {}); @@ -1421,6 +1442,7 @@ describe("Python transformations", () => { const expectedData = { success: true, publishedVersion: funcName }; + setFunctionInCache(funcName); const output = await setupUserTransformHandler([], trRevCode); expect(output).toEqual(expectedData); expect(axios.post).toHaveBeenCalledTimes(0); @@ -1604,7 +1626,7 @@ describe("Python transformations", () => { expect(axios.delete).toHaveBeenCalledTimes(1); }); - it("Simple transformation run - invokes faas function", async () => { + it("Simple transformation run with function in cache - invokes faas function", async () => { const inputData = require(`./data/${integration}_input.json`); const outputData = require(`./data/${integration}_output.json`); @@ -1612,6 +1634,8 @@ describe("Python transformations", () => { const respBody = pyTrRevCode(versionId); const funcName = pyfaasFuncName(respBody.workspaceId, versionId); + setFunctionInCache(funcName); + const transformerUrl = `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}`; when(fetch) .calledWith(transformerUrl) @@ -1625,7 +1649,7 @@ describe("Python transformations", () => { const output = await userTransformHandler(inputData, versionId, []); expect(output).toEqual(outputData); - expect(axios.post).toHaveBeenCalledTimes(1); + expect(axios.post).toHaveBeenCalledWith( `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, inputData, @@ -1633,12 +1657,16 @@ describe("Python transformations", () => { ); }); - it("Simple transformation run - function not found", async () => { + + it("Simple transformation run with clean cache - reconciles fn with 200OK and then invokes faas function", async () => { + const inputData = require(`./data/${integration}_input.json`); + const outputData = require(`./data/${integration}_output.json`); const versionId = randomID(); const respBody = pyTrRevCode(versionId); - const funcName = pyfaasFuncName(respBody.workspaceId, respBody.versionId); + const funcName = pyfaasFuncName(respBody.workspaceId, versionId); + const transformerUrl = `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}`; when(fetch) @@ -1648,28 +1676,13 @@ describe("Python transformations", () => { json: jest.fn().mockResolvedValue(respBody) }); - axios.post - .mockRejectedValueOnce({ - response: { status: 404, data: `error finding function ${funcName}` } // invoke function not found - }) - .mockResolvedValueOnce({}); // create function + axios.put.mockResolvedValue({}); axios.get.mockResolvedValue({}); // awaitFunctionReadiness() + axios.post.mockResolvedValue({ data: { transformedEvents: outputData } }); - await expect(async () => { - await userTransformHandler(inputData, versionId, []); - }).rejects.toThrow(RetryRequestError); + const output = await userTransformHandler(inputData, versionId, []); + expect(output).toEqual(outputData); - expect(axios.post).toHaveBeenCalledTimes(2); - expect(axios.post).toHaveBeenCalledWith( - `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, - inputData, - { auth: defaultBasicAuth }, - ); - expect(axios.post).toHaveBeenCalledWith( - `${OPENFAAS_GATEWAY_URL}/system/functions`, - expect.objectContaining({ name: funcName, service: funcName }), - { auth: defaultBasicAuth }, - ); expect(axios.get).toHaveBeenCalledTimes(1); expect(axios.get).toHaveBeenCalledWith( @@ -1677,6 +1690,154 @@ describe("Python transformations", () => { {"headers": {"X-REQUEST-TYPE": "HEALTH-CHECK"}}, { auth: defaultBasicAuth }, ); + expect(axios.put).toHaveBeenCalledTimes(1); + expect(axios.put).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/system/functions`, + buildOpenfaasFn(funcName, null, versionId, [], false, {}), + { auth: defaultBasicAuth }); + expect(axios.post).toHaveBeenCalledTimes(1); + expect(axios.post).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, + inputData, + { auth: defaultBasicAuth }, + ); + }); + + describe("Simple transformation run with clean cache - function not found", () => { + + it('eventually sets up the function on 404 from update and then invokes it', async () => { + const inputData = require(`./data/${integration}_input.json`); + + const versionId = randomID(); + const respBody = pyTrRevCode(versionId); + const funcName = pyfaasFuncName(respBody.workspaceId, respBody.versionId); + + const transformerUrl = `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}`; + when(fetch) + .calledWith(transformerUrl) + .mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + + axios.put.mockRejectedValueOnce({ + response: { status: 404, data: `deployment not found`} + }); + + axios.post + .mockRejectedValueOnce({ + response: { status: 404, data: `error finding function ${funcName}` } // invoke function not found + }) + .mockResolvedValueOnce({}); // create function + axios.get.mockResolvedValue({}); // awaitFunctionReadiness() + + await expect(async () => { + await userTransformHandler(inputData, versionId, []); + }).rejects.toThrow(RetryRequestError); + + expect(axios.put).toHaveBeenCalledTimes(1); + expect(axios.put).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/system/functions`, + buildOpenfaasFn(funcName, null, versionId, [], false, {}), + { auth: defaultBasicAuth }, + ); + expect(axios.post).toHaveBeenCalledTimes(2); + expect(axios.post).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, + inputData, + { auth: defaultBasicAuth }, + ); + expect(axios.post).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/system/functions`, + expect.objectContaining({ name: funcName, service: funcName }), + { auth: defaultBasicAuth }, + ); + + expect(axios.get).toHaveBeenCalledTimes(1); + expect(axios.get).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, + {"headers": {"X-REQUEST-TYPE": "HEALTH-CHECK"}}, + { auth: defaultBasicAuth }, + ); + }); + + it('sets up the function on 202 from update and then invokes it', async() => { + const inputData = require(`./data/${integration}_input.json`); + const outputData = require(`./data/${integration}_output.json`); + + const versionId = randomID(); + const respBody = pyTrRevCode(versionId); + const funcName = pyfaasFuncName(respBody.workspaceId, respBody.versionId); + + const transformerUrl = `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}`; + when(fetch) + .calledWith(transformerUrl) + .mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + + axios.put.mockResolvedValueOnce({ + response: { status: 202, data: `deployment created`} + }); + axios.get.mockResolvedValue({}); // awaitFunctionReadiness() + axios.post.mockResolvedValue({ data: { transformedEvents: outputData } }); + + const output = await userTransformHandler(inputData, versionId, []); + expect(output).toEqual(outputData); + + expect(axios.put).toHaveBeenCalledTimes(1); + expect(axios.put).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/system/functions`, + buildOpenfaasFn(funcName, null, versionId, [], false, {}), + { auth: defaultBasicAuth }, + ); + expect(axios.post).toHaveBeenCalledTimes(1); + expect(axios.post).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, + inputData, + { auth: defaultBasicAuth }, + ); + expect(axios.get).toHaveBeenCalledTimes(1); + expect(axios.get).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, + {"headers": {"X-REQUEST-TYPE": "HEALTH-CHECK"}}, + { auth: defaultBasicAuth }, + ); + }); + + it('throws from the userTransform handler when reconciles errors with anything other than 404', async() => { + const inputData = require(`./data/${integration}_input.json`); + const outputData = require(`./data/${integration}_output.json`); + + const versionId = randomID(); + const respBody = pyTrRevCode(versionId); + const funcName = pyfaasFuncName(respBody.workspaceId, respBody.versionId); + + const transformerUrl = `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}`; + when(fetch) + .calledWith(transformerUrl) + .mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + + axios.put.mockRejectedValueOnce({response: {status: 400, data: 'bad request'}}); + await expect(async () => { + await userTransformHandler(inputData, versionId, []); + }).rejects.toThrow(RespStatusError); + + expect(axios.put).toHaveBeenCalledTimes(1); + expect(axios.put).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/system/functions`, + buildOpenfaasFn(funcName, null, versionId, [], false, {}), + { auth: defaultBasicAuth }, + ); + }); + }); it("Simple transformation run - error requests", async () => { @@ -1694,6 +1855,8 @@ describe("Python transformations", () => { json: jest.fn().mockResolvedValue(respBody) }); + setFunctionInCache(funcName); + axios.post .mockRejectedValueOnce({ response: { status: 429, data: `Rate limit exceeded` } // invoke function with rate limit From 7719741265f2f7e69be14272962d938063f197f5 Mon Sep 17 00:00:00 2001 From: Abhimanyu Babbar Date: Tue, 28 May 2024 11:25:18 +0530 Subject: [PATCH 183/240] chore: updating changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90a2152b90..5ae244b760 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ All notable changes to this project will be documented in this file. See [standa * resolving comments ([7c0d963](https://github.com/rudderlabs/rudder-transformer/commit/7c0d963d3ee87a3ed5712492300dc50768c529de)) * standardise hashing for all CAPI integrations ([#3379](https://github.com/rudderlabs/rudder-transformer/issues/3379)) ([c249a69](https://github.com/rudderlabs/rudder-transformer/commit/c249a694d735f6d241a35b6e21f493c54890ac84)) * tiktok_v2 remove default value for content-type for custom events ([#3383](https://github.com/rudderlabs/rudder-transformer/issues/3383)) ([6e7b5a0](https://github.com/rudderlabs/rudder-transformer/commit/6e7b5a0d8bf2c859dfb15b9cad7ed6070bd0892b)) +* added step for reconciling openfaas functions for python transformations ([#3420](https://github.com/rudderlabs/rudder-transformer/issues/3420)) ([7a2ab63](https://github.com/rudderlabs/rudder-transformer/commit/7a2ab63674d40870af4d16f0673a2a2594c899e9)) ## [1.67.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.66.1...v1.67.0) (2024-05-23) From 11ac86ba55f59d43039b9f33c535bf9a5122ee43 Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Tue, 28 May 2024 13:37:52 +0530 Subject: [PATCH 184/240] fix: remove individual commits from CHANGELOG.md --- CHANGELOG.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ae244b760..3ba14c6e92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,21 +12,13 @@ All notable changes to this project will be documented in this file. See [standa * filtering unknown events in awin ([#3392](https://github.com/rudderlabs/rudder-transformer/issues/3392)) ([d842da8](https://github.com/rudderlabs/rudder-transformer/commit/d842da87a34cb63023eba288e0c5258e29997dcf)) * **ga4:** component test refactor ([#3220](https://github.com/rudderlabs/rudder-transformer/issues/3220)) ([3ff9a5e](https://github.com/rudderlabs/rudder-transformer/commit/3ff9a5e8e955b929a1b04a89dcf0ccbc49e18648)) * **integrations/auth0:** include Auth0 event type in Rudderstack message ([#3370](https://github.com/rudderlabs/rudder-transformer/issues/3370)) ([e9409fd](https://github.com/rudderlabs/rudder-transformer/commit/e9409fde6063d7eaa8558396b85b5fdf99f964e1)) -* onboard koddi destination ([88b2d57](https://github.com/rudderlabs/rudder-transformer/commit/88b2d5709da00445ffae54f5a36de855cb5f8479)) * onboard koddi destination ([#3359](https://github.com/rudderlabs/rudder-transformer/issues/3359)) ([f74c4a0](https://github.com/rudderlabs/rudder-transformer/commit/f74c4a0bc92ae6ccb0c00ac5b21745e496a015bc)) * onboarding adjust source ([#3395](https://github.com/rudderlabs/rudder-transformer/issues/3395)) ([668d331](https://github.com/rudderlabs/rudder-transformer/commit/668d3311aadacbb92b1873bf43919db7d341afbb)) ### Bug Fixes -* added componenet test ([189cf93](https://github.com/rudderlabs/rudder-transformer/commit/189cf9367a907dc1848257733e13713245458579)) -* added conversions bidders validation and improved implementation ([ddf8d46](https://github.com/rudderlabs/rudder-transformer/commit/ddf8d46fed980204c561f95daa12fc740302e6e3)) -* config ([847e3e0](https://github.com/rudderlabs/rudder-transformer/commit/847e3e04d3ef67b9a7b5e35127251f3fc34ba3bf)) -* fb custom audience html response ([#3402](https://github.com/rudderlabs/rudder-transformer/issues/3402)) ([d1a2bd6](https://github.com/rudderlabs/rudder-transformer/commit/d1a2bd61468c75f944135cf61cbf2464f08404ed)) -* fixed some issue and added unit test ([bc7970c](https://github.com/rudderlabs/rudder-transformer/commit/bc7970c4f0a70e9fe8ad06ffd92f8f4b2a4ec910)) -* fixed unit test issue ([d4a82e2](https://github.com/rudderlabs/rudder-transformer/commit/d4a82e2d2df7ee86c4b149bca7e0c12be3a6a545)) -* minor mapping issue in conversions ([31e6460](https://github.com/rudderlabs/rudder-transformer/commit/31e6460ccc0c18014ebf67eab23b59abe5d81ef6)) -* resolving comments ([7c0d963](https://github.com/rudderlabs/rudder-transformer/commit/7c0d963d3ee87a3ed5712492300dc50768c529de)) +* fb custom audience html response ([#3402](https://github.com/rudderlabs/rudder-transformer/issues/3402)) ([d1a2bd6](https://github.com/rudder * standardise hashing for all CAPI integrations ([#3379](https://github.com/rudderlabs/rudder-transformer/issues/3379)) ([c249a69](https://github.com/rudderlabs/rudder-transformer/commit/c249a694d735f6d241a35b6e21f493c54890ac84)) * tiktok_v2 remove default value for content-type for custom events ([#3383](https://github.com/rudderlabs/rudder-transformer/issues/3383)) ([6e7b5a0](https://github.com/rudderlabs/rudder-transformer/commit/6e7b5a0d8bf2c859dfb15b9cad7ed6070bd0892b)) * added step for reconciling openfaas functions for python transformations ([#3420](https://github.com/rudderlabs/rudder-transformer/issues/3420)) ([7a2ab63](https://github.com/rudderlabs/rudder-transformer/commit/7a2ab63674d40870af4d16f0673a2a2594c899e9)) From f9f3151391142dd21d1018c9df445673be08db6a Mon Sep 17 00:00:00 2001 From: devops-github-rudderstack <88187154+devops-github-rudderstack@users.noreply.github.com> Date: Thu, 30 May 2024 12:22:44 +0530 Subject: [PATCH 185/240] chore(release): pull hotfix-release/v1.68.1 into main (#3427) * fix: tiktok_v2 assigning value to undefined properties (#3426) * fix: tiktok_v2 assigning value to undefined properties * chore: address comments * chore(release): 1.68.1 * fix: bugsnag error for emarsys track call (#3428) --------- Co-authored-by: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Co-authored-by: GitHub Actions Co-authored-by: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> --- CHANGELOG.md | 7 ++ package-lock.json | 4 +- package.json | 2 +- src/cdk/v2/destinations/emarsys/utils.js | 2 +- src/v0/destinations/tiktok_ads/transformV2.js | 4 + .../destinations/emarsys/processor/data.ts | 117 ++++++++++++++++++ .../destinations/tiktok_ads/processor/data.ts | 103 +++++++++++++++ 7 files changed, 235 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ba14c6e92..d1f97b8b5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [1.68.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.68.0...v1.68.1) (2024-05-29) + + +### Bug Fixes + +* tiktok_v2 assigning value to undefined properties ([#3426](https://github.com/rudderlabs/rudder-transformer/issues/3426)) ([323396b](https://github.com/rudderlabs/rudder-transformer/commit/323396b09fd6b7fda3cce53cc4f1cc443d7a78c1)) + ## [1.68.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.67.0...v1.68.0) (2024-05-27) diff --git a/package-lock.json b/package-lock.json index b8e2c81a29..ded836ef59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.68.0", + "version": "1.68.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.68.0", + "version": "1.68.1", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index ed15683d4f..73cac39767 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.68.0", + "version": "1.68.1", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { diff --git a/src/cdk/v2/destinations/emarsys/utils.js b/src/cdk/v2/destinations/emarsys/utils.js index 2fe686718d..e093064fc5 100644 --- a/src/cdk/v2/destinations/emarsys/utils.js +++ b/src/cdk/v2/destinations/emarsys/utils.js @@ -162,7 +162,7 @@ const deduceEventId = (message, destConfig) => { const { eventsMapping } = destConfig; const { event } = message; validateEventName(event); - if (eventsMapping.length > 0) { + if (Array.isArray(eventsMapping) && eventsMapping.length > 0) { const keyMap = getHashFromArray(eventsMapping, 'from', 'to', false); eventId = keyMap[event]; } diff --git a/src/v0/destinations/tiktok_ads/transformV2.js b/src/v0/destinations/tiktok_ads/transformV2.js index 4624ec9181..8760dee52c 100644 --- a/src/v0/destinations/tiktok_ads/transformV2.js +++ b/src/v0/destinations/tiktok_ads/transformV2.js @@ -42,6 +42,8 @@ const getTrackResponsePayload = (message, destConfig, event, setDefaultForConten // if contents is not present but we have properties.products present which has fields with superset of contents fields if (!payload.properties?.contents && message.properties?.products) { // retreiving data from products only when contents is not present + // properties object may be empty due which next line may give some error + payload.properties = payload.properties || {}; payload.properties.contents = getContents(message, false); } @@ -55,6 +57,8 @@ const getTrackResponsePayload = (message, destConfig, event, setDefaultForConten } // setting content-type default value in case of all standard event except `page-view` if (!payload.properties?.content_type && setDefaultForContentType) { + // properties object may be empty due which next line may give some error + payload.properties = payload.properties || {}; payload.properties.content_type = 'product'; } payload.event = event; diff --git a/test/integrations/destinations/emarsys/processor/data.ts b/test/integrations/destinations/emarsys/processor/data.ts index fbeca6f4d8..cfa53fd4f6 100644 --- a/test/integrations/destinations/emarsys/processor/data.ts +++ b/test/integrations/destinations/emarsys/processor/data.ts @@ -1377,4 +1377,121 @@ export const data = [ }, }, }, + { + name: 'emarsys', + description: 'Test 11 : Track call with no event mapping field should fail', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + event: 'Order Completed', + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + }, + }, + integrations: { + All: true, + EMARSYS: { + trigger_id: 'EVENT_TRIGGER_ID', + }, + }, + properties: { + company: 'testComp', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + }, + messageId: '2536eda4-d638-4c93-8014-8ffe3f083214', + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'track', + userId: 'userId06', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '3', + defaultContactList: 'dummy', + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Order Completed is not mapped to any Emersys external event. Aborting: Workflow: procWorkflow, Step: preparePayloadForTrack, ChildStep: undefined, OriginalError: Order Completed is not mapped to any Emersys external event. Aborting', + metadata: {}, + statTags: { + destType: 'EMARSYS', + errorCategory: 'dataValidation', + errorType: 'configuration', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, ].map((d) => ({ ...d, mockFns })); diff --git a/test/integrations/destinations/tiktok_ads/processor/data.ts b/test/integrations/destinations/tiktok_ads/processor/data.ts index af58b66302..4dfd32d671 100644 --- a/test/integrations/destinations/tiktok_ads/processor/data.ts +++ b/test/integrations/destinations/tiktok_ads/processor/data.ts @@ -6971,6 +6971,109 @@ export const data = [ }, }, }, + { + name: 'tiktok_ads', + description: 'Test 46 -> V2 -> Standard Event with no properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + traits: { + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [ + { + type: 'tiktokExternalId', + id: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + ], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'Search', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + version: 'v2', + accessToken: 'dummyAccessToken', + pixelCode: '{{PIXEL-CODE}}', + hashUserProperties: false, + sendCustomEvents: true, + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source: 'web', + event_source_id: '{{PIXEL-CODE}}', + partner_name: 'RudderStack', + data: [ + { + event: 'Search', + event_id: '84e26acc-56a5-4835-8233-591137fca468', + event_time: 1600372167, + properties: { content_type: 'product' }, + user: { + locale: 'en-US', + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + external_id: + 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, { name: 'tiktok_ads', description: 'Testing if the event name provided as a string or not', From f8a275cc77a44969eb63a92ec4fe0c1eb40f3a41 Mon Sep 17 00:00:00 2001 From: devops-github-rudderstack <88187154+devops-github-rudderstack@users.noreply.github.com> Date: Thu, 30 May 2024 12:52:27 +0530 Subject: [PATCH 186/240] chore(release): pull main into develop post release v1.68.1 (#3430) chore(release): pull hotfix-release/v1.68.1 into main (#3427) * fix: tiktok_v2 assigning value to undefined properties (#3426) * fix: tiktok_v2 assigning value to undefined properties * chore: address comments * chore(release): 1.68.1 * fix: bugsnag error for emarsys track call (#3428) --------- Co-authored-by: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Co-authored-by: GitHub Actions Co-authored-by: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> --- CHANGELOG.md | 7 ++ package-lock.json | 4 +- package.json | 2 +- src/cdk/v2/destinations/emarsys/utils.js | 2 +- src/v0/destinations/tiktok_ads/transformV2.js | 4 + .../destinations/emarsys/processor/data.ts | 117 ++++++++++++++++++ .../destinations/tiktok_ads/processor/data.ts | 103 +++++++++++++++ 7 files changed, 235 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ba14c6e92..d1f97b8b5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [1.68.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.68.0...v1.68.1) (2024-05-29) + + +### Bug Fixes + +* tiktok_v2 assigning value to undefined properties ([#3426](https://github.com/rudderlabs/rudder-transformer/issues/3426)) ([323396b](https://github.com/rudderlabs/rudder-transformer/commit/323396b09fd6b7fda3cce53cc4f1cc443d7a78c1)) + ## [1.68.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.67.0...v1.68.0) (2024-05-27) diff --git a/package-lock.json b/package-lock.json index b8e2c81a29..ded836ef59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.68.0", + "version": "1.68.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.68.0", + "version": "1.68.1", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index ed15683d4f..73cac39767 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.68.0", + "version": "1.68.1", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { diff --git a/src/cdk/v2/destinations/emarsys/utils.js b/src/cdk/v2/destinations/emarsys/utils.js index 2fe686718d..e093064fc5 100644 --- a/src/cdk/v2/destinations/emarsys/utils.js +++ b/src/cdk/v2/destinations/emarsys/utils.js @@ -162,7 +162,7 @@ const deduceEventId = (message, destConfig) => { const { eventsMapping } = destConfig; const { event } = message; validateEventName(event); - if (eventsMapping.length > 0) { + if (Array.isArray(eventsMapping) && eventsMapping.length > 0) { const keyMap = getHashFromArray(eventsMapping, 'from', 'to', false); eventId = keyMap[event]; } diff --git a/src/v0/destinations/tiktok_ads/transformV2.js b/src/v0/destinations/tiktok_ads/transformV2.js index 4624ec9181..8760dee52c 100644 --- a/src/v0/destinations/tiktok_ads/transformV2.js +++ b/src/v0/destinations/tiktok_ads/transformV2.js @@ -42,6 +42,8 @@ const getTrackResponsePayload = (message, destConfig, event, setDefaultForConten // if contents is not present but we have properties.products present which has fields with superset of contents fields if (!payload.properties?.contents && message.properties?.products) { // retreiving data from products only when contents is not present + // properties object may be empty due which next line may give some error + payload.properties = payload.properties || {}; payload.properties.contents = getContents(message, false); } @@ -55,6 +57,8 @@ const getTrackResponsePayload = (message, destConfig, event, setDefaultForConten } // setting content-type default value in case of all standard event except `page-view` if (!payload.properties?.content_type && setDefaultForContentType) { + // properties object may be empty due which next line may give some error + payload.properties = payload.properties || {}; payload.properties.content_type = 'product'; } payload.event = event; diff --git a/test/integrations/destinations/emarsys/processor/data.ts b/test/integrations/destinations/emarsys/processor/data.ts index fbeca6f4d8..cfa53fd4f6 100644 --- a/test/integrations/destinations/emarsys/processor/data.ts +++ b/test/integrations/destinations/emarsys/processor/data.ts @@ -1377,4 +1377,121 @@ export const data = [ }, }, }, + { + name: 'emarsys', + description: 'Test 11 : Track call with no event mapping field should fail', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + event: 'Order Completed', + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + }, + }, + integrations: { + All: true, + EMARSYS: { + trigger_id: 'EVENT_TRIGGER_ID', + }, + }, + properties: { + company: 'testComp', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + }, + messageId: '2536eda4-d638-4c93-8014-8ffe3f083214', + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'track', + userId: 'userId06', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '3', + defaultContactList: 'dummy', + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Order Completed is not mapped to any Emersys external event. Aborting: Workflow: procWorkflow, Step: preparePayloadForTrack, ChildStep: undefined, OriginalError: Order Completed is not mapped to any Emersys external event. Aborting', + metadata: {}, + statTags: { + destType: 'EMARSYS', + errorCategory: 'dataValidation', + errorType: 'configuration', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, ].map((d) => ({ ...d, mockFns })); diff --git a/test/integrations/destinations/tiktok_ads/processor/data.ts b/test/integrations/destinations/tiktok_ads/processor/data.ts index af58b66302..4dfd32d671 100644 --- a/test/integrations/destinations/tiktok_ads/processor/data.ts +++ b/test/integrations/destinations/tiktok_ads/processor/data.ts @@ -6971,6 +6971,109 @@ export const data = [ }, }, }, + { + name: 'tiktok_ads', + description: 'Test 46 -> V2 -> Standard Event with no properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + traits: { + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [ + { + type: 'tiktokExternalId', + id: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + ], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'Search', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + version: 'v2', + accessToken: 'dummyAccessToken', + pixelCode: '{{PIXEL-CODE}}', + hashUserProperties: false, + sendCustomEvents: true, + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source: 'web', + event_source_id: '{{PIXEL-CODE}}', + partner_name: 'RudderStack', + data: [ + { + event: 'Search', + event_id: '84e26acc-56a5-4835-8233-591137fca468', + event_time: 1600372167, + properties: { content_type: 'product' }, + user: { + locale: 'en-US', + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + external_id: + 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, { name: 'tiktok_ads', description: 'Testing if the event name provided as a string or not', From 5111ce9dee2ca4362ea398d4d9805bab7070c8f7 Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Fri, 31 May 2024 01:07:57 +0530 Subject: [PATCH 187/240] chore: changes in test transform API --- src/controllers/userTransform.ts | 3 ++- src/services/userTransform.ts | 3 ++- src/util/customTransformer.js | 17 +++++++++---- test/__tests__/user_transformation.test.js | 5 ++++ .../user_transformation_errors.test.js | 8 +++++++ .../user_transformation_fetch.test.js | 24 +++++++++---------- 6 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/controllers/userTransform.ts b/src/controllers/userTransform.ts index 0e288c6f04..33e1507832 100644 --- a/src/controllers/userTransform.ts +++ b/src/controllers/userTransform.ts @@ -33,11 +33,12 @@ export class UserTransformController { '(User transform - router:/transformation/test ):: Request to transformer', ctx.request.body, ); - const { events, trRevCode, libraryVersionIDs = [] } = ctx.request.body as any; + const { events, trRevCode, libraryVersionIDs = [], credentials } = ctx.request.body as any; const response = await UserTransformService.testTransformRoutine( events, trRevCode, libraryVersionIDs, + credentials, ); ctx.body = response.body; ControllerUtility.postProcess(ctx, response.status); diff --git a/src/services/userTransform.ts b/src/services/userTransform.ts index 18c47ddc83..a587973deb 100644 --- a/src/services/userTransform.ts +++ b/src/services/userTransform.ts @@ -192,7 +192,7 @@ export class UserTransformService { } as UserTransformationServiceResponse; } - public static async testTransformRoutine(events, trRevCode, libraryVersionIDs) { + public static async testTransformRoutine(events, trRevCode, libraryVersionIDs, credentials) { const response: FixMe = {}; try { if (!trRevCode || !trRevCode.code || !trRevCode.codeVersion) { @@ -210,6 +210,7 @@ export class UserTransformService { trRevCode.versionId, libraryVersionIDs, trRevCode, + credentials, true, ); logger.debug(`[CT] Test Output Events: ${JSON.stringify(response.body.transformedEvents)}`); diff --git a/src/util/customTransformer.js b/src/util/customTransformer.js index 2573b82e6a..c0082e2c63 100644 --- a/src/util/customTransformer.js +++ b/src/util/customTransformer.js @@ -273,6 +273,7 @@ async function userTransformHandler( versionId, libraryVersionIDs, trRevCode = {}, + credentials = {}, testMode = false, ) { if (versionId) { @@ -285,16 +286,22 @@ async function userTransformHandler( events.forEach((ev) => { eventsMetadata[ev.message.messageId] = ev.metadata; }); - const credentials = {}; - events[0]?.credentials?.forEach((cred) => { - credentials[cred.key] = cred.value; - }); + const credentialsMap = {}; + if (testMode === false) { + events[0]?.credentials?.forEach((cred) => { + credentialsMap[cred.key] = cred.value; + }); + } else { + credentials?.forEach((cred) => { + credentialsMap[cred.key] = cred.value; + }); + } let userTransformedEvents = []; let result; if (res.codeVersion && res.codeVersion === '1') { result = await UserTransformHandlerFactory(res).runUserTransfrom( events, - credentials, + credentialsMap, testMode, libraryVersionIDs, ); diff --git a/test/__tests__/user_transformation.test.js b/test/__tests__/user_transformation.test.js index de6a587d1e..5706f926d2 100644 --- a/test/__tests__/user_transformation.test.js +++ b/test/__tests__/user_transformation.test.js @@ -1081,6 +1081,7 @@ describe("User transformation", () => { trRevCode.versionId, [libraryVersionId], trRevCode, + null, true ); @@ -1117,6 +1118,7 @@ describe("User transformation", () => { trRevCode.versionId, [], trRevCode, + null, true ); expect(output).toEqual(expectedData); @@ -1536,6 +1538,7 @@ describe("Python transformations", () => { trRevCode.versionId, [], trRevCode, + null, true ); expect(output).toEqual(outputData); @@ -1581,6 +1584,7 @@ describe("Python transformations", () => { trRevCode.versionId, Object.values(importNameLibraryVersionIdsMap), trRevCode, + null, true ); expect(output).toEqual(outputData); @@ -1622,6 +1626,7 @@ describe("Python transformations", () => { trRevCode.versionId, Object.values(importNameLibraryVersionIdsMap), trRevCode, + null, true ); expect(output).toEqual(outputData); diff --git a/test/__tests__/user_transformation_errors.test.js b/test/__tests__/user_transformation_errors.test.js index c2a99ce09d..eb171a1e4d 100644 --- a/test/__tests__/user_transformation_errors.test.js +++ b/test/__tests__/user_transformation_errors.test.js @@ -47,6 +47,7 @@ describe("JS Transformation Error Tests", () => { versionId, [], trRevCode, + null, true, ); @@ -65,6 +66,7 @@ describe("JS Transformation Error Tests", () => { versionId, [], trRevCode, + null, true, ); @@ -102,6 +104,7 @@ describe("JS Transformation Error Tests", () => { versionId, [libVid], trRevCode, + null, true, ); @@ -140,6 +143,7 @@ describe("JS Transformation Error Tests", () => { versionId, [libVid], trRevCode, + null, true, ); @@ -160,6 +164,7 @@ describe("JS Transformation Error Tests", () => { versionId, [], trRevCode, + null, true, ); @@ -181,6 +186,7 @@ describe("JS Transformation Error Tests", () => { versionId, [], trRevCode, + null, true, ); @@ -218,6 +224,7 @@ describe("JS Transformation Error Tests", () => { versionId, [libVid], trRevCode, + null, true, ); @@ -256,6 +263,7 @@ describe("JS Transformation Error Tests", () => { versionId, [libVid], trRevCode, + null, true, ); diff --git a/test/__tests__/user_transformation_fetch.test.js b/test/__tests__/user_transformation_fetch.test.js index e37b42ea1e..edbac04cf3 100644 --- a/test/__tests__/user_transformation_fetch.test.js +++ b/test/__tests__/user_transformation_fetch.test.js @@ -43,7 +43,7 @@ describe("User transformation fetch tests", () => { ` }; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); expect(output).toEqual({ logs: [], transformedEvents: expectedData.map(ev => (ev.transformedEvent)) @@ -67,7 +67,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "ERROR"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); expect(mockResolver).toHaveBeenCalledTimes(0); output.transformedEvents.forEach(ev => { @@ -92,7 +92,7 @@ describe("User transformation fetch tests", () => { const errMsg = "ERROR"; mockResolver.mockResolvedValue([ '127.0.0.1' ]); - const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); expect(mockResolver).toHaveBeenCalledTimes(inputData.length); expect(mockResolver).toHaveBeenCalledWith('abc.xyz.com'); @@ -122,7 +122,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "invalid url, localhost requests are not allowed"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); expect(mockResolver).toHaveBeenCalledTimes(0); output.transformedEvents.forEach(ev => { @@ -152,7 +152,7 @@ describe("User transformation fetch tests", () => { const errMsg = "request to https://abc.xyz.com/dummyUrl failed, reason: Invalid IP address: unable to resolve IP address for abc.xyz.com"; mockResolver.mockRejectedValue('invalid host'); - const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); expect(mockResolver).toHaveBeenCalledTimes(inputData.length); expect(mockResolver).toHaveBeenCalledWith('abc.xyz.com'); @@ -182,7 +182,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "invalid protocol, only http and https are supported"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); output.transformedEvents.forEach(ev => { expect(ev.errMsg).toEqual(errMsg); @@ -204,7 +204,7 @@ describe("User transformation fetch tests", () => { versionId, }; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); expect(output).toEqual({ logs: [], transformedEvents: expectedData.map(ev => (ev.transformedEvent)) @@ -226,7 +226,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "ERROR"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); expect(mockResolver).toHaveBeenCalledTimes(0); output.transformedEvents.forEach(ev => { @@ -250,7 +250,7 @@ describe("User transformation fetch tests", () => { const errMsg = "ERROR"; mockResolver.mockRejectedValue('invalid host'); - const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); expect(mockResolver).toHaveBeenCalledTimes(inputData.length); expect(mockResolver).toHaveBeenCalledWith('abc.xyz.com'); @@ -279,7 +279,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "invalid url, localhost requests are not allowed"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); expect(mockResolver).toHaveBeenCalledTimes(0); output.transformedEvents.forEach(ev => { @@ -308,7 +308,7 @@ describe("User transformation fetch tests", () => { const errMsg = "request to https://abc.xyz.com/dummyUrl failed, reason: Invalid IP address: cannot use 127.0.0.1 as IP address"; mockResolver.mockResolvedValue(['3.122.122.122', '127.0.0.1']); - const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); expect(mockResolver).toHaveBeenCalledTimes(inputData.length); expect(mockResolver).toHaveBeenCalledWith('abc.xyz.com'); @@ -337,7 +337,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "fetch url is required"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); output.transformedEvents.forEach(ev => { expect(ev.errMsg).toEqual(errMsg); From c280d81307a3c5da02f728961d7eeeabb79a7e39 Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Fri, 31 May 2024 01:53:25 +0530 Subject: [PATCH 188/240] fix: credential param --- src/util/customTransformer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/customTransformer.js b/src/util/customTransformer.js index c0082e2c63..9b60cc163f 100644 --- a/src/util/customTransformer.js +++ b/src/util/customTransformer.js @@ -273,7 +273,7 @@ async function userTransformHandler( versionId, libraryVersionIDs, trRevCode = {}, - credentials = {}, + credentials = [], testMode = false, ) { if (versionId) { From 31379645c33b9dc75cd839849952297fa233f2cb Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Fri, 31 May 2024 16:07:19 +0530 Subject: [PATCH 189/240] fix: test --- src/legacy/router.js | 1 + test/__tests__/user_transformation.integration.test.js | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/legacy/router.js b/src/legacy/router.js index 9dd83b5988..90b6b05693 100644 --- a/src/legacy/router.js +++ b/src/legacy/router.js @@ -675,6 +675,7 @@ if (transformerTestModeEnabled) { trRevCode.versionId, libraryVersionIDs, trRevCode, + null, true, ); logger.debug(`[CT] Test Output Events: ${JSON.stringify(res.transformedEvents)}`); diff --git a/test/__tests__/user_transformation.integration.test.js b/test/__tests__/user_transformation.integration.test.js index b0b8fc9cae..1e2fafeb73 100644 --- a/test/__tests__/user_transformation.integration.test.js +++ b/test/__tests__/user_transformation.integration.test.js @@ -159,13 +159,14 @@ describe("Function invocation & creation tests", () => { versionId, [], trRevCode, + null, true ); expect(response).toEqual(outputEvents); // Test with language python; should return same output trRevCode = contructTrRevCode(versionId, 'python'); - response = await userTransformHandler(inputEvents, versionId, [], trRevCode, true); + response = await userTransformHandler(inputEvents, versionId, [], trRevCode, null, true); expect(response).toEqual(outputEvents); }); @@ -184,7 +185,7 @@ describe("Function invocation & creation tests", () => { }); await expect(async () => { - await userTransformHandler(inputEvents, respBody.versionId, []); + await userTransformHandler(inputEvents, respBody.versionId, null, []); }).rejects.toThrow(RetryRequestError); // If function is not found, it will be created From 4b8f9a309f2d182298b4119e8598d7194468f181 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Date: Sun, 2 Jun 2024 13:18:18 +0530 Subject: [PATCH 190/240] chore: pr template update (#3423) * chore: update PR template for adding test cases in new format --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index cfcb1fc0d8..c52962467d 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -46,7 +46,7 @@ N/A - [ ] Is the PR limited to one linear task? -- [ ] Are relevant unit and component test-cases added? +- [ ] Are relevant unit and component test-cases added in **new readability format**? ### Reviewer checklist From 7eb537c91f40ee4802f14a088a249ebed903e7b5 Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Mon, 3 Jun 2024 14:21:02 +0530 Subject: [PATCH 191/240] fix: credential error handling --- src/types/index.ts | 3 ++- src/util/ivmFactory.js | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/types/index.ts b/src/types/index.ts index 25b9beae01..93eb9bb6ba 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -138,9 +138,10 @@ type UserTransformationLibrary = { }; type Credential = { + ID: string; Key: string; Value: string; - isPublic: boolean; + IsSecret: boolean; }; type ProcessorTransformationRequest = { diff --git a/src/util/ivmFactory.js b/src/util/ivmFactory.js index 62da76295d..e2692697a7 100644 --- a/src/util/ivmFactory.js +++ b/src/util/ivmFactory.js @@ -244,7 +244,12 @@ async function createIvm(code, libraryVersionIds, versionId, credentials, secret ); await jail.set('_credentials', function (...args) { - if (args.length == 0 || !credentials || !credentials[args[0]]) return 'ERROR'; + if (args[0].length === 0) { + throw new Error('Invalid credentials key'); + } + if (!credentials) { + throw new Error('Credentials not available'); + } return credentials[args[0]]; }); From a57da897c0b1206415954134cf0f855c7ec6c27f Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Mon, 3 Jun 2024 14:39:23 +0530 Subject: [PATCH 192/240] chore: added tests --- test/__tests__/user_transformation.test.js | 58 ++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/test/__tests__/user_transformation.test.js b/test/__tests__/user_transformation.test.js index 5706f926d2..9c9fc55f35 100644 --- a/test/__tests__/user_transformation.test.js +++ b/test/__tests__/user_transformation.test.js @@ -271,6 +271,64 @@ describe("User transformation", () => { expect(output[0].transformedEvent.credentialValue).toEqual("value1"); }); + it(`Simple ${name} Test with credentials without arguements for codeVersion 1`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformEvent(event, metadata) { + event.credentialValue = credentials(); + return event; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].error).toMatch(/Invalid credentials key/); + }); + + it(`Simple ${name} Test with credentials without value for codeVersion 1`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformEvent(event, metadata) { + event.credentialValue = credentials('key3'); + return event; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].transformedEvent.credentialValue).toEqual(undefined); + }); + it(`Simple async ${name} FetchV2 Test for V0 transformation`, async () => { const versionId = randomID(); const inputData = require(`./data/${integration}_input.json`); From 1fd068b8e6e4ccda40c3e6ee65add5a5785367d2 Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Mon, 3 Jun 2024 20:53:11 +0530 Subject: [PATCH 193/240] chore: added errors in credentials --- src/util/ivmFactory.js | 20 +- test/__tests__/user_transformation.test.js | 232 +++++++++++++-------- 2 files changed, 156 insertions(+), 96 deletions(-) diff --git a/src/util/ivmFactory.js b/src/util/ivmFactory.js index e2692697a7..9e1207f844 100644 --- a/src/util/ivmFactory.js +++ b/src/util/ivmFactory.js @@ -243,14 +243,14 @@ async function createIvm(code, libraryVersionIds, versionId, credentials, secret }), ); - await jail.set('_credentials', function (...args) { - if (args[0].length === 0) { - throw new Error('Invalid credentials key'); + await jail.set('_credential', function (key) { + if (_.isNil(credentials) || !_.isObject(credentials)) { + throw new Error('Credentials in incorrect format'); } - if (!credentials) { - throw new Error('Credentials not available'); + if (_.isNil(key[0]) || !_.isString(key[0])) { + throw new Error('Key should be a string'); } - return credentials[args[0]]; + return credentials[key]; }); await jail.set('_rsSecrets', function (...args) { @@ -331,10 +331,10 @@ async function createIvm(code, libraryVersionIds, versionId, credentials, secret ]); }; - let credentials = _credentials; - delete _credentials; - global.credentials = function(...args) { - return credentials([ + let credential = _credential; + delete _credential; + global.credential = function(...args) { + return credential([ ...args.map(arg => new ivm.ExternalCopy(arg).copyInto()) ]); }; diff --git a/test/__tests__/user_transformation.test.js b/test/__tests__/user_transformation.test.js index 9c9fc55f35..893b60e799 100644 --- a/test/__tests__/user_transformation.test.js +++ b/test/__tests__/user_transformation.test.js @@ -243,92 +243,6 @@ describe("User transformation", () => { expect(output[0].transformedEvent.dummy_key).toEqual(secrets.dummy_key); }); - it(`Simple ${name} Test with credentials for codeVersion 1`, async () => { - const versionId = randomID(); - - const inputData = require(`./data/${integration}_input_credentials.json`); - - const respBody = { - versionId: versionId, - codeVersion: "1", - name, - code: ` - export function transformEvent(event, metadata) { - event.credentialValue = credentials("key1"); - return event; - } - ` - }; - fetch.mockResolvedValue({ - status: 200, - json: jest.fn().mockResolvedValue(respBody) - }); - - const output = await userTransformHandler(inputData, versionId, []); - expect(fetch).toHaveBeenCalledWith( - `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` - ); - expect(output[0].transformedEvent.credentialValue).toEqual("value1"); - }); - - it(`Simple ${name} Test with credentials without arguements for codeVersion 1`, async () => { - const versionId = randomID(); - - const inputData = require(`./data/${integration}_input_credentials.json`); - - const respBody = { - versionId: versionId, - codeVersion: "1", - name, - code: ` - export function transformEvent(event, metadata) { - event.credentialValue = credentials(); - return event; - } - ` - }; - fetch.mockResolvedValue({ - status: 200, - json: jest.fn().mockResolvedValue(respBody) - }); - - const output = await userTransformHandler(inputData, versionId, []); - - expect(fetch).toHaveBeenCalledWith( - `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` - ); - expect(output[0].error).toMatch(/Invalid credentials key/); - }); - - it(`Simple ${name} Test with credentials without value for codeVersion 1`, async () => { - const versionId = randomID(); - - const inputData = require(`./data/${integration}_input_credentials.json`); - - const respBody = { - versionId: versionId, - codeVersion: "1", - name, - code: ` - export function transformEvent(event, metadata) { - event.credentialValue = credentials('key3'); - return event; - } - ` - }; - fetch.mockResolvedValue({ - status: 200, - json: jest.fn().mockResolvedValue(respBody) - }); - - const output = await userTransformHandler(inputData, versionId, []); - - expect(fetch).toHaveBeenCalledWith( - `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` - ); - expect(output[0].transformedEvent.credentialValue).toEqual(undefined); - }); - it(`Simple async ${name} FetchV2 Test for V0 transformation`, async () => { const versionId = randomID(); const inputData = require(`./data/${integration}_input.json`); @@ -1181,6 +1095,152 @@ describe("User transformation", () => { ); expect(output).toEqual(expectedData); }); + + describe("Credentials", () => { + it(`Simple ${name} Test with credentials for codeVersion 1`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformEvent(event, metadata) { + event.credentialValue = credential("key1"); + return event; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].transformedEvent.credentialValue).toEqual("value1"); + }); + + it(`Simple ${name} Test with credentials without key for codeVersion 1`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformEvent(event, metadata) { + event.credentialValue = credential(); + return event; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].error).toMatch(/Key should be a string/); + }); + + it(`Simple ${name} Test with credentials with non string key for codeVersion 1`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformEvent(event, metadata) { + event.credentialValue = credential(1); + return event; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].error).toMatch(/Key should be a string/); + }); + + it(`Simple ${name} Test with credentials without value for codeVersion 1`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformEvent(event, metadata) { + event.credentialValue = credential('key3'); + return event; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].transformedEvent.credentialValue).toEqual(undefined); + }); + + it(`Simple ${name} Test without credentials for codeVersion 1`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformEvent(event, metadata) { + event.credentialValue = credential('key1'); + return event; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].transformedEvent.credentialValue).toEqual(undefined); + }); + }); }); // Running timeout tests From 8808508d74ce6f5762bd95792f37b9c9eb068b55 Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Tue, 4 Jun 2024 19:08:38 +0530 Subject: [PATCH 194/240] chore: added credentials at event level for python transformations --- src/util/customTransformer.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/util/customTransformer.js b/src/util/customTransformer.js index 9b60cc163f..b3d7214a51 100644 --- a/src/util/customTransformer.js +++ b/src/util/customTransformer.js @@ -8,6 +8,7 @@ const { parserForImport } = require('./parser'); const stats = require('./stats'); const { fetchWithDnsWrapper } = require('./utils'); const { getMetadata, getTransformationMetadata } = require('../v0/util'); +const _ = require('lodash'); const ISOLATE_VM_MEMORY = parseInt(process.env.ISOLATE_VM_MEMORY || '128', 10); const GEOLOCATION_TIMEOUT_IN_MS = parseInt(process.env.GEOLOCATION_TIMEOUT_IN_MS || '1000', 10); @@ -295,6 +296,11 @@ async function userTransformHandler( credentials?.forEach((cred) => { credentialsMap[cred.key] = cred.value; }); + events.forEach((ev) => { + if (_.isNil(ev?.credentials)) { + ev.credentials = credentials; + } + }); } let userTransformedEvents = []; let result; From ac4a32ab5e0c7e02a149e81d455666ed24fa01a3 Mon Sep 17 00:00:00 2001 From: Vikas26021999 <79831638+Vikas26021999@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:08:01 +0530 Subject: [PATCH 195/240] feat: changes for supporting record event in FB audience (#3351) --- src/types/index.ts | 2 + .../fb_custom_audience/recordTransform.js | 277 + .../fb_custom_audience/transform.js | 84 +- .../destinations/fb_custom_audience/util.js | 43 +- .../fb_custom_audience/util.test.js | 122 + .../fb_custom_audience/router/audienceList.ts | 123 + .../router/batchingRecord.ts | 130 + .../fb_custom_audience/router/data.ts | 53195 +--------------- .../fb_custom_audience/router/eventStream.ts | 143 + .../fb_custom_audience/router/record.ts | 250 + 10 files changed, 1789 insertions(+), 52580 deletions(-) create mode 100644 src/v0/destinations/fb_custom_audience/recordTransform.js create mode 100644 src/v0/destinations/fb_custom_audience/util.test.js create mode 100644 test/integrations/destinations/fb_custom_audience/router/audienceList.ts create mode 100644 test/integrations/destinations/fb_custom_audience/router/batchingRecord.ts create mode 100644 test/integrations/destinations/fb_custom_audience/router/eventStream.ts create mode 100644 test/integrations/destinations/fb_custom_audience/router/record.ts diff --git a/src/types/index.ts b/src/types/index.ts index 68dfe3870d..b3d45c062e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -131,6 +131,8 @@ type Destination = { WorkspaceID: string; Transformations: UserTransformationInput[]; RevisionID?: string; + IsProcessorEnabled?: boolean; + IsConnectionEnabled?: boolean; }; type UserTransformationLibrary = { diff --git a/src/v0/destinations/fb_custom_audience/recordTransform.js b/src/v0/destinations/fb_custom_audience/recordTransform.js new file mode 100644 index 0000000000..0f7b65c0bf --- /dev/null +++ b/src/v0/destinations/fb_custom_audience/recordTransform.js @@ -0,0 +1,277 @@ +/* eslint-disable no-const-assign */ +const lodash = require('lodash'); +const get = require('get-value'); +const { + InstrumentationError, + ConfigurationError, + getErrorRespEvents, +} = require('@rudderstack/integrations-lib'); +const { schemaFields } = require('./config'); +const { MappedToDestinationKey } = require('../../../constants'); +const stats = require('../../../util/stats'); +const { + getDestinationExternalIDInfoForRetl, + isDefinedAndNotNullAndNotEmpty, + checkSubsetOfArray, + returnArrayOfSubarrays, + getSuccessRespEvents, + generateErrorObject, +} = require('../../util'); +const { + ensureApplicableFormat, + getUpdatedDataElement, + getSchemaForEventMappedToDest, + batchingWithPayloadSize, + responseBuilderSimple, + getDataSource, +} = require('./util'); + +function getErrorMetaData(inputs, acceptedOperations) { + const metadata = []; + // eslint-disable-next-line no-restricted-syntax + for (const key in inputs) { + if (!acceptedOperations.includes(key)) { + inputs[key].forEach((input) => { + metadata.push(input.metadata); + }); + } + } + return metadata; +} + +const processRecordEventArray = ( + recordChunksArray, + userSchema, + isHashRequired, + disableFormat, + paramsPayload, + prepareParams, + destination, + operation, + operationAudienceId, +) => { + const toSendEvents = []; + const metadata = []; + recordChunksArray.forEach((recordArray) => { + const data = []; + recordArray.forEach((input) => { + const { fields } = input.message; + let dataElement = []; + let nullUserData = true; + + userSchema.forEach((eachProperty) => { + const userProperty = fields[eachProperty]; + let updatedProperty = userProperty; + + if (isHashRequired && !disableFormat) { + updatedProperty = ensureApplicableFormat(eachProperty, userProperty); + } + + dataElement = getUpdatedDataElement( + dataElement, + isHashRequired, + eachProperty, + updatedProperty, + ); + + if (dataElement[dataElement.length - 1]) { + nullUserData = false; + } + }); + + if (nullUserData) { + stats.increment('fb_custom_audience_event_having_all_null_field_values_for_a_user', { + destinationId: destination.ID, + nullFields: userSchema, + }); + } + data.push(dataElement); + metadata.push(input.metadata); + }); + + const prepareFinalPayload = lodash.cloneDeep(paramsPayload); + prepareFinalPayload.schema = userSchema; + prepareFinalPayload.data = data; + const payloadBatches = batchingWithPayloadSize(prepareFinalPayload); + + payloadBatches.forEach((payloadBatch) => { + const response = { + ...prepareParams, + payload: payloadBatch, + }; + + const wrappedResponse = { + responseField: response, + operationCategory: operation, + }; + + const builtResponse = responseBuilderSimple(wrappedResponse, operationAudienceId); + + toSendEvents.push(builtResponse); + }); + }); + + const response = getSuccessRespEvents(toSendEvents, metadata, destination, true); + + return response; +}; + +async function processRecordInputs(groupedRecordInputs) { + const { destination } = groupedRecordInputs[0]; + const { message } = groupedRecordInputs[0]; + const { + isHashRequired, + accessToken, + disableFormat, + type, + subType, + isRaw, + maxUserCount, + audienceId, + } = destination.Config; + const prepareParams = { + access_token: accessToken, + }; + + // maxUserCount validation + const maxUserCountNumber = parseInt(maxUserCount, 10); + if (Number.isNaN(maxUserCountNumber)) { + throw new ConfigurationError('Batch size must be an Integer.'); + } + + // audience id validation + let operationAudienceId = audienceId; + const mappedToDestination = get(message, MappedToDestinationKey); + if (mappedToDestination) { + const { objectType } = getDestinationExternalIDInfoForRetl(message, 'FB_CUSTOM_AUDIENCE'); + operationAudienceId = objectType; + } + if (!isDefinedAndNotNullAndNotEmpty(operationAudienceId)) { + throw new ConfigurationError('Audience ID is a mandatory field'); + } + + // user schema validation + let { userSchema } = destination.Config; + if (mappedToDestination) { + userSchema = getSchemaForEventMappedToDest(message); + } + if (!Array.isArray(userSchema)) { + userSchema = [userSchema]; + } + if (!checkSubsetOfArray(schemaFields, userSchema)) { + throw new ConfigurationError('One or more of the schema fields are not supported'); + } + + const paramsPayload = {}; + + if (isRaw) { + paramsPayload.is_raw = isRaw; + } + + const dataSource = getDataSource(type, subType); + if (Object.keys(dataSource).length > 0) { + paramsPayload.data_source = dataSource; + } + + const groupedRecordsByAction = lodash.groupBy(groupedRecordInputs, (record) => + record.message.action?.toLowerCase(), + ); + + const finalResponse = []; + + let insertResponse; + let deleteResponse; + let updateResponse; + + if (groupedRecordsByAction.delete) { + const deleteRecordChunksArray = returnArrayOfSubarrays( + groupedRecordsByAction.delete, + maxUserCountNumber, + ); + deleteResponse = processRecordEventArray( + deleteRecordChunksArray, + userSchema, + isHashRequired, + disableFormat, + paramsPayload, + prepareParams, + destination, + 'remove', + operationAudienceId, + ); + } + + if (groupedRecordsByAction.insert) { + const insertRecordChunksArray = returnArrayOfSubarrays( + groupedRecordsByAction.insert, + maxUserCountNumber, + ); + + insertResponse = processRecordEventArray( + insertRecordChunksArray, + userSchema, + isHashRequired, + disableFormat, + paramsPayload, + prepareParams, + destination, + 'add', + operationAudienceId, + ); + } + + if (groupedRecordsByAction.update) { + const updateRecordChunksArray = returnArrayOfSubarrays( + groupedRecordsByAction.update, + maxUserCountNumber, + ); + updateResponse = processRecordEventArray( + updateRecordChunksArray, + userSchema, + isHashRequired, + disableFormat, + paramsPayload, + prepareParams, + destination, + 'add', + operationAudienceId, + ); + } + + const eventTypes = ['update', 'insert', 'delete']; + const errorMetaData = []; + const errorMetaDataObject = getErrorMetaData(groupedRecordsByAction, eventTypes); + if (errorMetaDataObject.length > 0) { + errorMetaData.push(errorMetaDataObject); + } + + const error = new InstrumentationError('Invalid action type in record event'); + const errorObj = generateErrorObject(error); + const errorResponseList = errorMetaData.map((metadata) => + getErrorRespEvents(metadata, errorObj.status, errorObj.message, errorObj.statTags), + ); + + if (deleteResponse && deleteResponse.batchedRequest.length > 0) { + finalResponse.push(deleteResponse); + } + if (insertResponse && insertResponse.batchedRequest.length > 0) { + finalResponse.push(insertResponse); + } + if (updateResponse && updateResponse.batchedRequest.length > 0) { + finalResponse.push(updateResponse); + } + if (errorResponseList.length > 0) { + finalResponse.push(...errorResponseList); + } + + if (finalResponse.length === 0) { + throw new InstrumentationError( + 'Missing valid parameters, unable to generate transformed payload', + ); + } + return finalResponse; +} + +module.exports = { + processRecordInputs, +}; diff --git a/src/v0/destinations/fb_custom_audience/transform.js b/src/v0/destinations/fb_custom_audience/transform.js index dfe9a04618..c5c340c043 100644 --- a/src/v0/destinations/fb_custom_audience/transform.js +++ b/src/v0/destinations/fb_custom_audience/transform.js @@ -1,14 +1,7 @@ const lodash = require('lodash'); const get = require('get-value'); +const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); const { - InstrumentationError, - TransformationError, - ConfigurationError, -} = require('@rudderstack/integrations-lib'); -const { - defaultRequestConfig, - defaultPostRequestConfig, - defaultDeleteRequestConfig, checkSubsetOfArray, isDefinedAndNotNullAndNotEmpty, returnArrayOfSubarrays, @@ -21,40 +14,27 @@ const { getSchemaForEventMappedToDest, batchingWithPayloadSize, generateAppSecretProof, + responseBuilderSimple, + getDataSource, } = require('./util'); -const { - getEndPoint, - schemaFields, - USER_ADD, - USER_DELETE, - typeFields, - subTypeFields, -} = require('./config'); +const { schemaFields, USER_ADD, USER_DELETE } = require('./config'); const { MappedToDestinationKey } = require('../../../constants'); +const { processRecordInputs } = require('./recordTransform'); +const logger = require('../../../logger'); -const responseBuilderSimple = (payload, audienceId) => { - if (payload) { - const responseParams = payload.responseField; - const response = defaultRequestConfig(); - response.endpoint = getEndPoint(audienceId); - - if (payload.operationCategory === 'add') { - response.method = defaultPostRequestConfig.requestMethod; +function checkForUnsupportedEventTypes(dictionary, keyList) { + const unsupportedEventTypes = []; + // eslint-disable-next-line no-restricted-syntax + for (const key in dictionary) { + if (!keyList.includes(key)) { + unsupportedEventTypes.push(key); } - if (payload.operationCategory === 'remove') { - response.method = defaultDeleteRequestConfig.requestMethod; - } - - response.params = responseParams; - return response; } - // fail-safety for developer error - throw new TransformationError(`Payload could not be constructed`); -}; + return unsupportedEventTypes; +} // Function responsible prepare the payload field of every event parameter - const preparePayload = ( userUpdateList, userSchema, @@ -102,7 +82,6 @@ const prepareResponse = ( const prepareParams = {}; // creating the parameters field const paramsPayload = {}; - const dataSource = {}; prepareParams.access_token = accessToken; @@ -118,13 +97,7 @@ const prepareResponse = ( } // creating the data_source block - if (type && type !== 'NA' && typeFields.includes(type)) { - dataSource.type = type; - } - - if (subType && subType !== 'NA' && subTypeFields.includes(subType)) { - dataSource.sub_type = subType; - } + const dataSource = getDataSource(type, subType); if (Object.keys(dataSource).length > 0) { paramsPayload.data_source = dataSource; } @@ -250,6 +223,7 @@ const processEvent = (message, destination) => { ), ); } + toSendEvents.forEach((sendEvent) => { respList.push(responseBuilderSimple(sendEvent, operationAudienceId)); }); @@ -265,7 +239,31 @@ const processEvent = (message, destination) => { const process = (event) => processEvent(event.message, event.destination); const processRouterDest = async (inputs, reqMetadata) => { - const respList = await simpleProcessRouterDest(inputs, process, reqMetadata); + const respList = []; + const groupedInputs = lodash.groupBy(inputs, (input) => input.message.type?.toLowerCase()); + let transformedRecordEvent = []; + let transformedAudienceEvent = []; + + const eventTypes = ['record', 'audiencelist']; + const unsupportedEventList = checkForUnsupportedEventTypes(groupedInputs, eventTypes); + if (unsupportedEventList.length > 0) { + logger.info(`unsupported events found ${unsupportedEventList}`); + throw new ConfigurationError('unsupported events present in the event'); + } + + if (groupedInputs.record) { + transformedRecordEvent = await processRecordInputs(groupedInputs.record, reqMetadata); + } + + if (groupedInputs.audiencelist) { + transformedAudienceEvent = await simpleProcessRouterDest( + groupedInputs.audiencelist, + process, + reqMetadata, + ); + } + + respList.push(...transformedRecordEvent, ...transformedAudienceEvent); return flattenMap(respList); }; diff --git a/src/v0/destinations/fb_custom_audience/util.js b/src/v0/destinations/fb_custom_audience/util.js index 9385dfbd36..401b601869 100644 --- a/src/v0/destinations/fb_custom_audience/util.js +++ b/src/v0/destinations/fb_custom_audience/util.js @@ -4,6 +4,13 @@ const crypto = require('crypto'); const get = require('get-value'); const jsonSize = require('json-size'); const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); +const { TransformationError } = require('@rudderstack/integrations-lib'); +const { typeFields, subTypeFields, getEndPoint } = require('./config'); +const { + defaultRequestConfig, + defaultPostRequestConfig, + defaultDeleteRequestConfig, +} = require('../../util'); const stats = require('../../../util/stats'); const { isDefinedAndNotNull } = require('../../util'); @@ -208,7 +215,6 @@ const prepareDataField = ( }; // ref: https://developers.facebook.com/docs/facebook-login/security/#generate-the-proof - const generateAppSecretProof = (accessToken, appSecret, dateNow) => { const currentTime = Math.floor(dateNow / 1000); // Get current Unix time in seconds const data = `${accessToken}|${currentTime}`; @@ -221,9 +227,44 @@ const generateAppSecretProof = (accessToken, appSecret, dateNow) => { return appsecretProof; }; +const getDataSource = (type, subType) => { + const dataSource = {}; + if (type && type !== 'NA' && typeFields.includes(type)) { + dataSource.type = type; + } + if (subType && subType !== 'NA' && subTypeFields.includes(subType)) { + dataSource.sub_type = subType; + } + return dataSource; +}; + +const responseBuilderSimple = (payload, audienceId) => { + if (payload) { + const responseParams = payload.responseField; + const response = defaultRequestConfig(); + response.endpoint = getEndPoint(audienceId); + + if (payload.operationCategory === 'add') { + response.method = defaultPostRequestConfig.requestMethod; + } + if (payload.operationCategory === 'remove') { + response.method = defaultDeleteRequestConfig.requestMethod; + } + + response.params = responseParams; + return response; + } + // fail-safety for developer error + throw new TransformationError(`Payload could not be constructed`); +}; + module.exports = { prepareDataField, getSchemaForEventMappedToDest, batchingWithPayloadSize, + ensureApplicableFormat, + getUpdatedDataElement, generateAppSecretProof, + responseBuilderSimple, + getDataSource, }; diff --git a/src/v0/destinations/fb_custom_audience/util.test.js b/src/v0/destinations/fb_custom_audience/util.test.js new file mode 100644 index 0000000000..60e0aff464 --- /dev/null +++ b/src/v0/destinations/fb_custom_audience/util.test.js @@ -0,0 +1,122 @@ +const { getDataSource, responseBuilderSimple, getUpdatedDataElement } = require('./util'); + +const basePayload = { + responseField: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FI'], + data: [ + [ + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', + ], + ], + }, + }, +}; + +const baseResponse = { + version: '1', + type: 'REST', + endpoint: 'https://graph.facebook.com/v18.0/23848494844100489/users', + headers: {}, + params: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FI'], + data: [ + [ + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', + ], + ], + }, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, +}; + +describe('FB_custom_audience utils test', () => { + describe('getDataSource function tests', () => { + it('Should return empty datasource if type and subType are both NA', () => { + const expectedDataSource = {}; + const dataSource = getDataSource('NA', 'NA'); + expect(dataSource).toEqual(expectedDataSource); + }); + it('Should set subType and type if value present in destination config macthes with preset list', () => { + const expectedDataSource = { + type: 'EVENT_BASED', + }; + const dataSource = getDataSource('EVENT_BASED', 'something'); + expect(dataSource).toEqual(expectedDataSource); + }); + }); + + describe('responseBuilderSimple function tests', () => { + it('Should return correct response for add payload', () => { + const payload = basePayload; + payload.operationCategory = 'add'; + const expectedResponse = baseResponse; + expectedResponse.method = 'POST'; + const response = responseBuilderSimple(payload, '23848494844100489'); + expect(response).toEqual(expectedResponse); + }); + + it('Should return correct response for delete payload', () => { + const payload = basePayload; + payload.operationCategory = 'remove'; + const expectedResponse = baseResponse; + expectedResponse.method = 'DELETE'; + const response = responseBuilderSimple(payload, '23848494844100489'); + expect(response).toEqual(expectedResponse); + }); + + it('Should throw error if payload is empty', () => { + try { + const response = responseBuilderSimple(payload, ''); + expect(response).toEqual(); + } catch (error) { + expect(error.message).toEqual(`payload is not defined`); + } + }); + }); + + describe('getUpdatedDataElement function tests', () => { + it('Should hash field if isHashRequired is set to true', () => { + const expectedDataElement = [ + '59107c750fd5ee2758d1988f2bf12d9f110439221ebdb7997e70d6a2c1c5afda', + ]; + let dataElement = []; + dataElement = getUpdatedDataElement(dataElement, true, 'FN', 'some-name'); + expect(dataElement).toEqual(expectedDataElement); + }); + + it('Should not hash field if isHashRequired is set to false', () => { + const expectedDataElement = ['some-name']; + let dataElement = []; + dataElement = getUpdatedDataElement(dataElement, false, 'FN', 'some-name'); + expect(dataElement).toEqual(expectedDataElement); + }); + + it('Should not hash MADID or EXTERN_ID and just pass value', () => { + const expectedDataElement = ['some-id', 'some-ext-id']; + let dataElement = []; + dataElement = getUpdatedDataElement(dataElement, true, 'MADID', 'some-id'); + dataElement = getUpdatedDataElement(dataElement, true, 'EXTERN_ID', 'some-ext-id'); + expect(dataElement).toEqual(expectedDataElement); + }); + + it('Should not hash MADID or EXTERN_ID and just pass empty value if value does not exist', () => { + const expectedDataElement = ['', '']; + let dataElement = []; + dataElement = getUpdatedDataElement(dataElement, true, 'MADID', ''); + dataElement = getUpdatedDataElement(dataElement, true, 'EXTERN_ID', ''); + expect(dataElement).toEqual(expectedDataElement); + }); + }); +}); diff --git a/test/integrations/destinations/fb_custom_audience/router/audienceList.ts b/test/integrations/destinations/fb_custom_audience/router/audienceList.ts new file mode 100644 index 0000000000..c386fbf782 --- /dev/null +++ b/test/integrations/destinations/fb_custom_audience/router/audienceList.ts @@ -0,0 +1,123 @@ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + Config: { + accessToken: 'ABC', + disableFormat: false, + isHashRequired: true, + isRaw: false, + maxUserCount: '50', + oneTrustCookieCategories: [], + skipVerify: false, + subType: 'NA', + type: 'NA', + userSchema: ['EMAIL'], + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, +}; + +export const rETLAudienceRouterRequest: RouterTransformationRequest = { + input: [ + { + message: { + sentAt: '2023-03-30 06:42:55.991938402 +0000 UTC', + userId: '2MUWghI7u85n91dd1qzGyswpZan-2MUWqbQqvctyfMGqU9QCNadpKNy', + channel: 'sources', + messageId: '4d906837-031d-4d34-b97a-62fdf51b4d3a', + event: 'Add_Audience', + context: { + destinationFields: 'EMAIL, FN', + externalId: [{ type: 'FB_CUSTOM_AUDIENCE-23848494844100489', identifierType: 'EMAIL' }], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '725ad989-6750-4839-b46b-0ddb3b8e5aa2/1/10', + rudderId: '85c49666-c628-4835-937b-8f1d9ee7a724', + properties: { + listData: { + add: [ + { EMAIL: 'dede@gmail.com', FN: 'vishwa' }, + { EMAIL: 'fchsjjn@gmail.com', FN: 'hskks' }, + { EMAIL: 'fghjnbjk@gmail.com', FN: 'ghfry' }, + { EMAIL: 'gvhjkk@gmail.com', FN: 'hbcwqe' }, + { EMAIL: 'qsdwert@egf.com', FN: 'dsfds' }, + { EMAIL: 'ascscxsaca@com', FN: 'scadscdvcda' }, + { EMAIL: 'abc@gmail.com', FN: 'subscribed' }, + { EMAIL: 'ddwnkl@gmail.com', FN: 'subscribed' }, + { EMAIL: 'subscribed@eewrfrd.com', FN: 'pending' }, + { EMAIL: 'acsdvdf@ddfvf.com', FN: 'pending' }, + ], + }, + }, + type: 'audienceList', + anonymousId: '63228b51-394e-4ca2-97a0-427f6187480b', + }, + destination: destination, + metadata: generateMetadata(3), + }, + { + message: { + sentAt: '2023-03-30 06:42:55.991938402 +0000 UTC', + userId: '2MUWghI7u85n91dd1qzGyswpZan-2MUWqbQqvctyfMGqU9QCNadpKNy', + channel: 'sources', + messageId: '4d906837-031d-4d34-b97a-62fdf51b4d3a', + event: 'Add_Audience', + context: { + externalId: [{ type: 'FB_CUSTOM_AUDIENCE-23848494844100489', identifierType: 'EMAIL' }], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '725ad989-6750-4839-b46b-0ddb3b8e5aa2/1/10', + rudderId: '85c49666-c628-4835-937b-8f1d9ee7a724', + properties: { + listData: { + add: [ + { EMAIL: 'dede@gmail.com', FN: 'vishwa' }, + { EMAIL: 'fchsjjn@gmail.com', FN: 'hskks' }, + { EMAIL: 'fghjnbjk@gmail.com', FN: 'ghfry' }, + { EMAIL: 'gvhjkk@gmail.com', FN: 'hbcwqe' }, + { EMAIL: 'qsdwert@egf.com', FN: 'dsfds' }, + { EMAIL: 'ascscxsaca@com', FN: 'scadscdvcda' }, + { EMAIL: 'abc@gmail.com', FN: 'subscribed' }, + { EMAIL: 'ddwnkl@gmail.com', FN: 'subscribed' }, + { EMAIL: 'subscribed@eewrfrd.com', FN: 'pending' }, + { EMAIL: 'acsdvdf@ddfvf.com', FN: 'pending' }, + ], + }, + }, + type: 'audienceList', + anonymousId: '63228b51-394e-4ca2-97a0-427f6187480b', + }, + destination: destination, + metadata: generateMetadata(4), + }, + ], + destType: 'fb_custom_audience', +}; + +module.exports = { + rETLAudienceRouterRequest, +}; diff --git a/test/integrations/destinations/fb_custom_audience/router/batchingRecord.ts b/test/integrations/destinations/fb_custom_audience/router/batchingRecord.ts new file mode 100644 index 0000000000..0ceff5260e --- /dev/null +++ b/test/integrations/destinations/fb_custom_audience/router/batchingRecord.ts @@ -0,0 +1,130 @@ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + Config: { + accessToken: 'ABC', + disableFormat: false, + isHashRequired: true, + isRaw: false, + maxUserCount: '2', + oneTrustCookieCategories: [], + skipVerify: false, + subType: 'NA', + type: 'NA', + userSchema: ['EMAIL'], + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, +}; + +export const rETLBatchingRouterRequest: RouterTransformationRequest = { + input: [ + { + destination: destination, + message: { + action: 'insert', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(1), + }, + { + destination: destination, + message: { + action: 'insert', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(2), + }, + { + destination: destination, + message: { + action: 'insert', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(3), + }, + ], + destType: 'fb_custom_audience', +}; + +module.exports = { + rETLBatchingRouterRequest, +}; diff --git a/test/integrations/destinations/fb_custom_audience/router/data.ts b/test/integrations/destinations/fb_custom_audience/router/data.ts index 1240939dbd..72438e74b0 100644 --- a/test/integrations/destinations/fb_custom_audience/router/data.ts +++ b/test/integrations/destinations/fb_custom_audience/router/data.ts @@ -1,167 +1,20 @@ -import { getEndPoint } from '../../../../../src/v0/destinations/fb_custom_audience/config'; +import { eventStreamRouterRequest } from './eventStream'; +import { rETLAudienceRouterRequest } from './audienceList'; +import { rETLBatchingRouterRequest } from './batchingRecord'; +import { rETLRecordRouterRequest } from './record'; export const data = [ { name: 'fb_custom_audience', description: 'eventStream tests', + scenario: 'business', + successCriteria: 'event stream events should be batched correctly', feature: 'router', module: 'destination', version: 'v0', input: { request: { - body: { - input: [ - { - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - add: [ - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'f', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - ], - remove: [ - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'f', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - ], - }, - }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', - }, - metadata: { jobId: 1, userId: 'u1' }, - destination: { - Config: { - accessToken: 'ABC', - userSchema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], - isHashRequired: false, - disableFormat: false, - audienceId: 'aud1', - isRaw: true, - type: 'UNKNOWN', - subType: 'ANYTHING', - maxUserCount: '50', - }, - Enabled: true, - Transformations: [], - IsProcessorEnabled: true, - }, - }, - { - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - add: [ - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'f', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - ], - remove: [ - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'f', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - ], - }, - }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', - }, - metadata: { jobId: 2, userId: 'u1' }, - destination: { - Config: { - accessToken: 'ABC', - userSchema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], - isHashRequired: false, - disableFormat: false, - audienceId: 'aud1', - isRaw: true, - type: 'NA', - subType: 'ANYTHING', - maxUserCount: '50', - }, - Enabled: true, - Transformations: [], - IsProcessorEnabled: true, - }, - libraries: [], - request: { query: {} }, - }, - ], - destType: 'fb_custom_audience', - }, + body: eventStreamRouterRequest, }, }, output: { @@ -175,13 +28,15 @@ export const data = [ version: '1', type: 'REST', method: 'DELETE', - endpoint: getEndPoint('aud1'), + endpoint: 'https://graph.facebook.com/v18.0/aud1/users', headers: {}, params: { access_token: 'ABC', payload: { is_raw: true, - data_source: { type: 'UNKNOWN', sub_type: 'ANYTHING' }, + data_source: { + sub_type: 'ANYTHING', + }, schema: [ 'EMAIL', 'DOBM', @@ -212,20 +67,27 @@ export const data = [ ], }, }, - body: { JSON: {}, JSON_ARRAY: {}, XML: {}, FORM: {} }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, files: {}, }, { version: '1', type: 'REST', method: 'POST', - endpoint: getEndPoint('aud1'), + endpoint: 'https://graph.facebook.com/v18.0/aud1/users', headers: {}, params: { access_token: 'ABC', payload: { is_raw: true, - data_source: { type: 'UNKNOWN', sub_type: 'ANYTHING' }, + data_source: { + sub_type: 'ANYTHING', + }, schema: [ 'EMAIL', 'DOBM', @@ -256,11 +118,29 @@ export const data = [ ], }, }, - body: { JSON: {}, JSON_ARRAY: {}, XML: {}, FORM: {} }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, files: {}, }, ], - metadata: [{ jobId: 1, userId: 'u1' }], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 1, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], batched: false, statusCode: 200, destination: { @@ -283,13 +163,22 @@ export const data = [ disableFormat: false, audienceId: 'aud1', isRaw: true, - type: 'UNKNOWN', + type: 'NA', subType: 'ANYTHING', maxUserCount: '50', }, Enabled: true, Transformations: [], IsProcessorEnabled: true, + ID: '123', + Name: 'fb_custom_audience', + DestinationDefinition: { + ID: '123', + Name: 'fb_custom_audience', + DisplayName: 'fb_custom_audience', + Config: {}, + }, + WorkspaceID: '123', }, }, { @@ -298,13 +187,15 @@ export const data = [ version: '1', type: 'REST', method: 'DELETE', - endpoint: getEndPoint('aud1'), + endpoint: 'https://graph.facebook.com/v18.0/aud1/users', headers: {}, params: { access_token: 'ABC', payload: { is_raw: true, - data_source: { sub_type: 'ANYTHING' }, + data_source: { + sub_type: 'ANYTHING', + }, schema: [ 'EMAIL', 'DOBM', @@ -335,20 +226,27 @@ export const data = [ ], }, }, - body: { JSON: {}, JSON_ARRAY: {}, XML: {}, FORM: {} }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, files: {}, }, { version: '1', type: 'REST', method: 'POST', - endpoint: getEndPoint('aud1'), + endpoint: 'https://graph.facebook.com/v18.0/aud1/users', headers: {}, params: { access_token: 'ABC', payload: { is_raw: true, - data_source: { sub_type: 'ANYTHING' }, + data_source: { + sub_type: 'ANYTHING', + }, schema: [ 'EMAIL', 'DOBM', @@ -379,11 +277,29 @@ export const data = [ ], }, }, - body: { JSON: {}, JSON_ARRAY: {}, XML: {}, FORM: {} }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, files: {}, }, ], - metadata: [{ jobId: 2, userId: 'u1' }], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 2, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], batched: false, statusCode: 200, destination: { @@ -413,6 +329,15 @@ export const data = [ Enabled: true, Transformations: [], IsProcessorEnabled: true, + ID: '123', + Name: 'fb_custom_audience', + DestinationDefinition: { + ID: '123', + Name: 'fb_custom_audience', + DisplayName: 'fb_custom_audience', + Config: {}, + }, + WorkspaceID: '123', }, }, ], @@ -423,54 +348,102 @@ export const data = [ { name: 'fb_custom_audience', description: 'rETL tests', + scenario: 'business', + successCriteria: 'it should transform audience event correctly', feature: 'router', module: 'destination', version: 'v0', input: { request: { + body: rETLAudienceRouterRequest, + }, + }, + output: { + response: { + status: 200, body: { - input: [ + output: [ { - message: { - sentAt: '2023-03-30 06:42:55.991938402 +0000 UTC', - userId: '2MUWghI7u85n91dd1qzGyswpZan-2MUWqbQqvctyfMGqU9QCNadpKNy', - channel: 'sources', - messageId: '4d906837-031d-4d34-b97a-62fdf51b4d3a', - event: 'Add_Audience', - context: { - destinationFields: 'EMAIL, FN', - externalId: [ - { type: 'FB_CUSTOM_AUDIENCE-23848494844100489', identifierType: 'EMAIL' }, - ], - mappedToDestination: 'true', - sources: { - job_run_id: 'cgiiurt8um7k7n5dq480', - task_run_id: 'cgiiurt8um7k7n5dq48g', - job_id: '2MUWghI7u85n91dd1qzGyswpZan', - version: '895/merge', + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/23848494844100489/users', + headers: {}, + params: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FN'], + data: [ + [ + '7625cab24612c37df6d2f724721bb38a25095d0295e29b807238ee188b8aca43', + 'e328a0d90d4b5132b2655cf7079b160040d2c1a83d70d4cad9cf1f69310635b3', + ], + [ + 'b2b4abadd72190af54305c0d3abf1977fec4935016bb13ff28040d5712318dfd', + 'f8147eb72c9bb356c362fdb0796b54971ebc983cb60b3cc3ff29582ce2052bad', + ], + [ + 'c4b007d1c3c9a5d31bd4082237a913e8e0db1767225c2a5ef33be2716df005fa', + 'd8bb13b95eaed7f9b6a8af276aa6122e8015e0c466c1a84e49ff7c69ad6ac911', + ], + [ + '94639be1bd9f17c05820164e9d71ef78558f117a9e8bfab43cf8015e08aa0b27', + 'b1661f97721dede0f876dcbf603289ee339f641b9c310deba53c76940f472698', + ], + [ + '39b456cfb4bb07f9e6bb18698aa173171ca49c731fccc4790e9ecea808d24ae6', + '6c882abd6d0aff713cdd6a4a31ee28c9140612fb2627a611f6f9f539bac44f81', + ], + [ + '769f73387add781a481ca08300008a08fb2f1816aaed196137efc2e05976d711', + '2222cb73346f7a01a1d4d3db28b58fd41045782bb66152b92aade379192544c5', + ], + [ + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + 'abc12f8d666517c35280bf220f5390b1f0ef4bdbbc794ac59c95bba0381bf91b', + ], + [ + 'da2d431121cd10578fd81f8f80344b06db59ea2d05a7b5d27536c8789ddae8f0', + 'abc12f8d666517c35280bf220f5390b1f0ef4bdbbc794ac59c95bba0381bf91b', + ], + [ + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '62a2fed3d6e08c44835fce71f02210b1ddabfb066e39edf1e6c261988f824dd3', + ], + [ + '0c1d1b0ba547a742013366d6fbc8f71dd77f566d94e41ed9f828a74b96928161', + '62a2fed3d6e08c44835fce71f02210b1ddabfb066e39edf1e6c261988f824dd3', + ], + ], + }, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, + files: {}, }, - recordId: '725ad989-6750-4839-b46b-0ddb3b8e5aa2/1/10', - rudderId: '85c49666-c628-4835-937b-8f1d9ee7a724', - properties: { - listData: { - add: [ - { EMAIL: 'dede@gmail.com', FN: 'vishwa' }, - { EMAIL: 'fchsjjn@gmail.com', FN: 'hskks' }, - { EMAIL: 'fghjnbjk@gmail.com', FN: 'ghfry' }, - { EMAIL: 'gvhjkk@gmail.com', FN: 'hbcwqe' }, - { EMAIL: 'qsdwert@egf.com', FN: 'dsfds' }, - { EMAIL: 'ascscxsaca@com', FN: 'scadscdvcda' }, - { EMAIL: 'abc@gmail.com', FN: 'subscribed' }, - { EMAIL: 'ddwnkl@gmail.com', FN: 'subscribed' }, - { EMAIL: 'subscribed@eewrfrd.com', FN: 'pending' }, - { EMAIL: 'acsdvdf@ddfvf.com', FN: 'pending' }, - ], + ], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 3, + secret: { + accessToken: 'default-accessToken', }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', }, - type: 'audienceList', - anonymousId: '63228b51-394e-4ca2-97a0-427f6187480b', - }, + ], + batched: false, + statusCode: 200, destination: { Config: { accessToken: 'ABC', @@ -484,66 +457,49 @@ export const data = [ type: 'NA', userSchema: ['EMAIL'], }, - secretConfig: {}, ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', - name: 'FB_CUSTOM_AUDIENCE', - enabled: true, - workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', - deleted: false, - createdAt: '2020-12-30T08:39:32.005Z', - updatedAt: '2021-02-03T16:22:31.374Z', - destinationDefinition: { - id: '1aIXqM806xAVm92nx07YwKbRrO9', - name: 'FB_CUSTOM_AUDIENCE', - displayName: 'FB_CUSTOM_AUDIENCE', - createdAt: '2020-04-09T09:24:31.794Z', - updatedAt: '2021-01-11T11:03:28.103Z', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, }, - transformations: [], - isConnectionEnabled: true, - isProcessorEnabled: true, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, }, - metadata: { jobId: 2, userId: 'u1' }, }, { - message: { - sentAt: '2023-03-30 06:42:55.991938402 +0000 UTC', - userId: '2MUWghI7u85n91dd1qzGyswpZan-2MUWqbQqvctyfMGqU9QCNadpKNy', - channel: 'sources', - messageId: '4d906837-031d-4d34-b97a-62fdf51b4d3a', - event: 'Add_Audience', - context: { - externalId: [ - { type: 'FB_CUSTOM_AUDIENCE-23848494844100489', identifierType: 'EMAIL' }, - ], - mappedToDestination: 'true', - sources: { - job_run_id: 'cgiiurt8um7k7n5dq480', - task_run_id: 'cgiiurt8um7k7n5dq48g', - job_id: '2MUWghI7u85n91dd1qzGyswpZan', - version: '895/merge', - }, - }, - recordId: '725ad989-6750-4839-b46b-0ddb3b8e5aa2/1/10', - rudderId: '85c49666-c628-4835-937b-8f1d9ee7a724', - properties: { - listData: { - add: [ - { EMAIL: 'dede@gmail.com', FN: 'vishwa' }, - { EMAIL: 'fchsjjn@gmail.com', FN: 'hskks' }, - { EMAIL: 'fghjnbjk@gmail.com', FN: 'ghfry' }, - { EMAIL: 'gvhjkk@gmail.com', FN: 'hbcwqe' }, - { EMAIL: 'qsdwert@egf.com', FN: 'dsfds' }, - { EMAIL: 'ascscxsaca@com', FN: 'scadscdvcda' }, - { EMAIL: 'abc@gmail.com', FN: 'subscribed' }, - { EMAIL: 'ddwnkl@gmail.com', FN: 'subscribed' }, - { EMAIL: 'subscribed@eewrfrd.com', FN: 'pending' }, - { EMAIL: 'acsdvdf@ddfvf.com', FN: 'pending' }, - ], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 4, + secret: { + accessToken: 'default-accessToken', }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', }, - type: 'audienceList', - anonymousId: '63228b51-394e-4ca2-97a0-427f6187480b', + ], + batched: false, + statusCode: 400, + error: + 'context.destinationFields is required property for events mapped to destination ', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'FB_CUSTOM_AUDIENCE', + destinationId: 'default-destinationId', + module: 'destination', + implementation: 'native', + feature: 'router', + workspaceId: 'default-workspaceId', }, destination: { Config: { @@ -558,52321 +514,488 @@ export const data = [ type: 'NA', userSchema: ['EMAIL'], }, - secretConfig: {}, ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', - name: 'FB_CUSTOM_AUDIENCE', - enabled: true, - workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', - deleted: false, - createdAt: '2020-12-30T08:39:32.005Z', - updatedAt: '2021-02-03T16:22:31.374Z', - destinationDefinition: { - id: '1aIXqM806xAVm92nx07YwKbRrO9', - name: 'FB_CUSTOM_AUDIENCE', - displayName: 'FB_CUSTOM_AUDIENCE', - createdAt: '2020-04-09T09:24:31.794Z', - updatedAt: '2021-01-11T11:03:28.103Z', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, }, - transformations: [], - isConnectionEnabled: true, - isProcessorEnabled: true, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, }, - metadata: { jobId: 3, userId: 'u1' }, }, + ], + }, + }, + }, + }, + { + name: 'fb_custom_audience', + description: 'rETL record tests', + scenario: 'business', + successCriteria: 'all record events should be transformed correctly based on their operation', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: rETLRecordRouterRequest, + }, + }, + output: { + response: { + status: 200, + body: { + output: [ { - message: { - sentAt: '2023-03-30 06:42:55.991938402 +0000 UTC', - userId: '2MUWghI7u85n91dd1qzGyswpZan-2MUWqbQqvctyfMGqU9QCNadpKNy', - channel: 'sources', - messageId: '4d906837-031d-4d34-b97a-62fdf51b4d3a', - event: 'Add_Audience', - context: { - destinationFields: - 'EMAIL, DOBM, DOBD, DOBY, PHONE, GEN, FI, MADID, ZIP, ST, COUNTRY', - externalId: [{ type: 'FB_CUSTOM_AUDIENCE-aud1', identifierType: 'EMAIL' }], - mappedToDestination: 'true', - sources: { - job_run_id: 'cgiiurt8um7k7n5dq480', - task_run_id: 'cgiiurt8um7k7n5dq48g', - job_id: '2MUWghI7u85n91dd1qzGyswpZan', - version: '895/merge', + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'DELETE', + endpoint: 'https://graph.facebook.com/v18.0/23848494844100489/users', + headers: {}, + params: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FI'], + data: [ + [ + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', + ], + [ + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', + ], + ], + }, }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, }, - recordId: '725ad989-6750-4839-b46b-0ddb3b8e5aa2/1/10', - rudderId: '85c49666-c628-4835-937b-8f1d9ee7a724', - properties: { - listData: { - add: [ - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - ], - }, - }, - type: 'audienceList', - anonymousId: '63228b51-394e-4ca2-97a0-427f6187480b', - }, - destination: { - Config: { - accessToken: 'ABC', - disableFormat: false, - isHashRequired: false, - isRaw: true, - maxUserCount: '1000', - oneTrustCookieCategories: [], - skipVerify: false, - subType: 'NA', - type: 'NA', - userSchema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], - }, - secretConfig: {}, - ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', - name: 'FB_CUSTOM_AUDIENCE', - enabled: true, - workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', - deleted: false, - createdAt: '2020-12-30T08:39:32.005Z', - updatedAt: '2021-02-03T16:22:31.374Z', - destinationDefinition: { - id: '1aIXqM806xAVm92nx07YwKbRrO9', - name: 'FB_CUSTOM_AUDIENCE', - displayName: 'FB_CUSTOM_AUDIENCE', - createdAt: '2020-04-09T09:24:31.794Z', - updatedAt: '2021-01-11T11:03:28.103Z', - }, - transformations: [], - isConnectionEnabled: true, - isProcessorEnabled: true, - }, - metadata: { jobId: 4, userId: 'u1' }, - }, - ], - destType: 'fb_custom_audience', - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: [ - { - batchedRequest: [ - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: getEndPoint('23848494844100489'), - headers: {}, - params: { - access_token: 'ABC', - payload: { - schema: ['EMAIL', 'FN'], - data: [ - [ - '7625cab24612c37df6d2f724721bb38a25095d0295e29b807238ee188b8aca43', - 'e328a0d90d4b5132b2655cf7079b160040d2c1a83d70d4cad9cf1f69310635b3', - ], - [ - 'b2b4abadd72190af54305c0d3abf1977fec4935016bb13ff28040d5712318dfd', - 'f8147eb72c9bb356c362fdb0796b54971ebc983cb60b3cc3ff29582ce2052bad', - ], - [ - 'c4b007d1c3c9a5d31bd4082237a913e8e0db1767225c2a5ef33be2716df005fa', - 'd8bb13b95eaed7f9b6a8af276aa6122e8015e0c466c1a84e49ff7c69ad6ac911', - ], - [ - '94639be1bd9f17c05820164e9d71ef78558f117a9e8bfab43cf8015e08aa0b27', - 'b1661f97721dede0f876dcbf603289ee339f641b9c310deba53c76940f472698', - ], - [ - '39b456cfb4bb07f9e6bb18698aa173171ca49c731fccc4790e9ecea808d24ae6', - '6c882abd6d0aff713cdd6a4a31ee28c9140612fb2627a611f6f9f539bac44f81', - ], - [ - '769f73387add781a481ca08300008a08fb2f1816aaed196137efc2e05976d711', - '2222cb73346f7a01a1d4d3db28b58fd41045782bb66152b92aade379192544c5', - ], - [ - '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', - 'abc12f8d666517c35280bf220f5390b1f0ef4bdbbc794ac59c95bba0381bf91b', - ], - [ - 'da2d431121cd10578fd81f8f80344b06db59ea2d05a7b5d27536c8789ddae8f0', - 'abc12f8d666517c35280bf220f5390b1f0ef4bdbbc794ac59c95bba0381bf91b', - ], - [ - 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', - '62a2fed3d6e08c44835fce71f02210b1ddabfb066e39edf1e6c261988f824dd3', - ], - [ - '0c1d1b0ba547a742013366d6fbc8f71dd77f566d94e41ed9f828a74b96928161', - '62a2fed3d6e08c44835fce71f02210b1ddabfb066e39edf1e6c261988f824dd3', - ], - ], - }, - }, - body: { JSON: {}, JSON_ARRAY: {}, XML: {}, FORM: {} }, - files: {}, - }, - ], - metadata: [{ jobId: 2, userId: 'u1' }], - batched: false, - statusCode: 200, - destination: { - Config: { - accessToken: 'ABC', - disableFormat: false, - isHashRequired: true, - isRaw: false, - maxUserCount: '50', - oneTrustCookieCategories: [], - skipVerify: false, - subType: 'NA', - type: 'NA', - userSchema: ['EMAIL'], - }, - secretConfig: {}, - ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', - name: 'FB_CUSTOM_AUDIENCE', - enabled: true, - workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', - deleted: false, - createdAt: '2020-12-30T08:39:32.005Z', - updatedAt: '2021-02-03T16:22:31.374Z', - destinationDefinition: { - id: '1aIXqM806xAVm92nx07YwKbRrO9', - name: 'FB_CUSTOM_AUDIENCE', - displayName: 'FB_CUSTOM_AUDIENCE', - createdAt: '2020-04-09T09:24:31.794Z', - updatedAt: '2021-01-11T11:03:28.103Z', - }, - transformations: [], - isConnectionEnabled: true, - isProcessorEnabled: true, - }, - }, - { - destination: { - Config: { - accessToken: 'ABC', - disableFormat: false, - isHashRequired: true, - isRaw: false, - maxUserCount: '50', - oneTrustCookieCategories: [], - skipVerify: false, - subType: 'NA', - type: 'NA', - userSchema: ['EMAIL'], - }, - secretConfig: {}, - ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', - name: 'FB_CUSTOM_AUDIENCE', - enabled: true, - workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', - deleted: false, - createdAt: '2020-12-30T08:39:32.005Z', - updatedAt: '2021-02-03T16:22:31.374Z', - destinationDefinition: { - id: '1aIXqM806xAVm92nx07YwKbRrO9', - name: 'FB_CUSTOM_AUDIENCE', - displayName: 'FB_CUSTOM_AUDIENCE', - createdAt: '2020-04-09T09:24:31.794Z', - updatedAt: '2021-01-11T11:03:28.103Z', - }, - transformations: [], - isConnectionEnabled: true, - isProcessorEnabled: true, - }, - error: - 'context.destinationFields is required property for events mapped to destination ', - metadata: [{ jobId: 3, userId: 'u1' }], - batched: false, - statusCode: 400, - statTags: { - destType: 'FB_CUSTOM_AUDIENCE', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'router', - implementation: 'native', - module: 'destination', - }, - }, - { - batchedRequest: [ - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: getEndPoint('aud1'), - headers: {}, - params: { - access_token: 'ABC', - payload: { - is_raw: true, - schema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], - data: [ - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - ], - }, - }, - body: { JSON: {}, XML: {}, JSON_ARRAY: {}, FORM: {} }, - files: {}, - }, - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: getEndPoint('aud1'), - headers: {}, - params: { - access_token: 'ABC', - payload: { - is_raw: true, - schema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], - data: [ - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - ], - }, - }, - body: { JSON: {}, XML: {}, JSON_ARRAY: {}, FORM: {} }, - files: {}, - }, - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: getEndPoint('aud1'), - headers: {}, - params: { - access_token: 'ABC', - payload: { - is_raw: true, - schema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], - data: [ - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - ], - }, - }, - body: { JSON: {}, XML: {}, JSON_ARRAY: {}, FORM: {} }, - files: {}, - }, - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: getEndPoint('aud1'), - headers: {}, - params: { - access_token: 'ABC', - payload: { - is_raw: true, - schema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], - data: [ - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], + ], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 1, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 2, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], + batched: true, + statusCode: 200, + destination: { + Config: { + accessToken: 'ABC', + disableFormat: false, + isHashRequired: true, + isRaw: false, + maxUserCount: '3', + oneTrustCookieCategories: [], + skipVerify: false, + subType: 'NA', + type: 'NA', + userSchema: ['EMAIL'], + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, + }, + }, + { + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/23848494844100489/users', + headers: {}, + params: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FI'], + data: [ [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', ], + ], + }, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + ], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 3, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], + batched: true, + statusCode: 200, + destination: { + Config: { + accessToken: 'ABC', + disableFormat: false, + isHashRequired: true, + isRaw: false, + maxUserCount: '3', + oneTrustCookieCategories: [], + skipVerify: false, + subType: 'NA', + type: 'NA', + userSchema: ['EMAIL'], + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, + }, + }, + { + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/23848494844100489/users', + headers: {}, + params: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FI'], + data: [ [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', ], [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', ], [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', ], + ], + }, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + ], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 4, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 5, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 6, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], + batched: true, + statusCode: 200, + destination: { + Config: { + accessToken: 'ABC', + disableFormat: false, + isHashRequired: true, + isRaw: false, + maxUserCount: '3', + oneTrustCookieCategories: [], + skipVerify: false, + subType: 'NA', + type: 'NA', + userSchema: ['EMAIL'], + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, + }, + }, + { + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 7, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], + batched: false, + statusCode: 400, + error: 'Invalid action type in record event', + statTags: { + errorCategory: 'dataValidation', + destinationId: 'default-destinationId', + errorType: 'instrumentation', + destType: 'FB_CUSTOM_AUDIENCE', + workspaceId: 'default-workspaceId', + module: 'destination', + implementation: 'native', + feature: 'router', + }, + }, + ], + }, + }, + }, + }, + { + name: 'fb_custom_audience', + description: 'rETL record batching tests', + scenario: 'Framework', + successCriteria: 'All the record events should be batched', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: rETLBatchingRouterRequest, + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/23848494844100489/users', + headers: {}, + params: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FI'], + data: [ [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', ], [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', ], + ], + }, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/23848494844100489/users', + headers: {}, + params: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FI'], + data: [ [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', ], ], }, }, - body: { JSON: {}, XML: {}, JSON_ARRAY: {}, FORM: {} }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, files: {}, }, ], - metadata: [{ jobId: 4, userId: 'u1' }], - batched: false, + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 1, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 2, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 3, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], + batched: true, statusCode: 200, destination: { Config: { accessToken: 'ABC', disableFormat: false, - isHashRequired: false, - isRaw: true, - maxUserCount: '1000', + isHashRequired: true, + isRaw: false, + maxUserCount: '2', oneTrustCookieCategories: [], skipVerify: false, subType: 'NA', type: 'NA', - userSchema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], + userSchema: ['EMAIL'], }, - secretConfig: {}, - ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', - name: 'FB_CUSTOM_AUDIENCE', - enabled: true, - workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', - deleted: false, - createdAt: '2020-12-30T08:39:32.005Z', - updatedAt: '2021-02-03T16:22:31.374Z', - destinationDefinition: { - id: '1aIXqM806xAVm92nx07YwKbRrO9', - name: 'FB_CUSTOM_AUDIENCE', - displayName: 'FB_CUSTOM_AUDIENCE', - createdAt: '2020-04-09T09:24:31.794Z', - updatedAt: '2021-01-11T11:03:28.103Z', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + Config: {}, + DisplayName: 'FB_CUSTOM_AUDIENCE', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', }, - transformations: [], - isConnectionEnabled: true, - isProcessorEnabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, }, }, ], diff --git a/test/integrations/destinations/fb_custom_audience/router/eventStream.ts b/test/integrations/destinations/fb_custom_audience/router/eventStream.ts new file mode 100644 index 0000000000..b4dcebf48b --- /dev/null +++ b/test/integrations/destinations/fb_custom_audience/router/eventStream.ts @@ -0,0 +1,143 @@ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + Config: { + accessToken: 'ABC', + userSchema: [ + 'EMAIL', + 'DOBM', + 'DOBD', + 'DOBY', + 'PHONE', + 'GEN', + 'FI', + 'MADID', + 'ZIP', + 'ST', + 'COUNTRY', + ], + isHashRequired: false, + disableFormat: false, + audienceId: 'aud1', + isRaw: true, + type: 'NA', + subType: 'ANYTHING', + maxUserCount: '50', + }, + Enabled: true, + Transformations: [], + IsProcessorEnabled: true, + ID: '123', + Name: 'fb_custom_audience', + DestinationDefinition: { + ID: '123', + Name: 'fb_custom_audience', + DisplayName: 'fb_custom_audience', + Config: {}, + }, + WorkspaceID: '123', +}; + +export const eventStreamRouterRequest: RouterTransformationRequest = { + input: [ + { + message: { + userId: 'user 1', + anonymousId: 'anon-id-new', + event: 'event1', + type: 'audiencelist', + properties: { + listData: { + add: [ + { + EMAIL: 'shrouti@abc.com', + DOBM: '2', + DOBD: '13', + DOBY: '2013', + PHONE: '@09432457768', + GEN: 'f', + FI: 'Ms.', + MADID: 'ABC', + ZIP: 'ZIP ', + ST: '123abc ', + COUNTRY: 'IN', + }, + ], + remove: [ + { + EMAIL: 'shrouti@abc.com', + DOBM: '2', + DOBD: '13', + DOBY: '2013', + PHONE: '@09432457768', + GEN: 'f', + FI: 'Ms.', + MADID: 'ABC', + ZIP: 'ZIP ', + ST: '123abc ', + COUNTRY: 'IN', + }, + ], + }, + }, + context: { ip: '14.5.67.21', library: { name: 'http' } }, + timestamp: '2020-02-02T00:23:09.544Z', + }, + metadata: generateMetadata(1), + destination: destination, + }, + { + message: { + userId: 'user 1', + anonymousId: 'anon-id-new', + event: 'event1', + type: 'audiencelist', + properties: { + listData: { + add: [ + { + EMAIL: 'shrouti@abc.com', + DOBM: '2', + DOBD: '13', + DOBY: '2013', + PHONE: '@09432457768', + GEN: 'f', + FI: 'Ms.', + MADID: 'ABC', + ZIP: 'ZIP ', + ST: '123abc ', + COUNTRY: 'IN', + }, + ], + remove: [ + { + EMAIL: 'shrouti@abc.com', + DOBM: '2', + DOBD: '13', + DOBY: '2013', + PHONE: '@09432457768', + GEN: 'f', + FI: 'Ms.', + MADID: 'ABC', + ZIP: 'ZIP ', + ST: '123abc ', + COUNTRY: 'IN', + }, + ], + }, + }, + context: { ip: '14.5.67.21', library: { name: 'http' } }, + timestamp: '2020-02-02T00:23:09.544Z', + }, + metadata: generateMetadata(2), + destination: destination, + request: { query: {} }, + }, + ], + destType: 'fb_custom_audience', +}; + +module.exports = { + eventStreamRouterRequest, +}; diff --git a/test/integrations/destinations/fb_custom_audience/router/record.ts b/test/integrations/destinations/fb_custom_audience/router/record.ts new file mode 100644 index 0000000000..534c1c40c2 --- /dev/null +++ b/test/integrations/destinations/fb_custom_audience/router/record.ts @@ -0,0 +1,250 @@ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + Config: { + accessToken: 'ABC', + disableFormat: false, + isHashRequired: true, + isRaw: false, + maxUserCount: '3', + oneTrustCookieCategories: [], + skipVerify: false, + subType: 'NA', + type: 'NA', + userSchema: ['EMAIL'], + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, +}; + +export const rETLRecordRouterRequest: RouterTransformationRequest = { + input: [ + { + destination: destination, + message: { + action: 'insert', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(3), + }, + { + destination: destination, + message: { + action: 'update', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(4), + }, + { + destination: destination, + message: { + action: 'delete', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(1), + }, + { + destination: destination, + message: { + action: 'delete', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(2), + }, + { + destination: destination, + message: { + action: 'update', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(5), + }, + { + destination: destination, + message: { + action: 'update', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(6), + }, + { + destination: destination, + message: { + action: 'lol', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(7), + }, + ], + destType: 'fb_custom_audience', +}; + +module.exports = { + rETLRecordRouterRequest, +}; From d2b0779d3a0145c0088903edfe328c1c7554cbd2 Mon Sep 17 00:00:00 2001 From: Akash Gupta Date: Wed, 5 Jun 2024 19:10:33 +0530 Subject: [PATCH 196/240] fix(user-transformation): pass tf id in common metadata --- src/services/userTransform.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/userTransform.ts b/src/services/userTransform.ts index 62980a935a..db9facc152 100644 --- a/src/services/userTransform.ts +++ b/src/services/userTransform.ts @@ -67,6 +67,7 @@ export class UserTransformService { destinationId: eventsToProcess[0]?.metadata.destinationId, destinationType: eventsToProcess[0]?.metadata.destinationType, workspaceId: eventsToProcess[0]?.metadata.workspaceId, + transformationId: eventsToProcess[0]?.metadata.transformationId, messageIds, }; From 96f00bb9eac0ac297ab8f4f49320d656e90700cd Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 6 Jun 2024 06:17:00 +0000 Subject: [PATCH 197/240] chore(release): 1.68.2 --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1f97b8b5f..8b2fff8bce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [1.68.2](https://github.com/rudderlabs/rudder-transformer/compare/v1.68.1...v1.68.2) (2024-06-06) + + +### Bug Fixes + +* **user-transformation:** pass tf id in common metadata ([d2b0779](https://github.com/rudderlabs/rudder-transformer/commit/d2b0779d3a0145c0088903edfe328c1c7554cbd2)) + ### [1.68.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.68.0...v1.68.1) (2024-05-29) diff --git a/package-lock.json b/package-lock.json index ded836ef59..5449e97f36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.68.1", + "version": "1.68.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.68.1", + "version": "1.68.2", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index 73cac39767..106065b72a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.68.1", + "version": "1.68.2", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From a73ab75032d753b35cb0e18234dcd7289dd1e644 Mon Sep 17 00:00:00 2001 From: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Date: Thu, 6 Jun 2024 12:57:07 +0530 Subject: [PATCH 198/240] feat: add request_ip as fallback for mixpanel group call (#3421) feat: add request_ip as fallback for mixpanel --- src/v0/destinations/mp/transform.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v0/destinations/mp/transform.js b/src/v0/destinations/mp/transform.js index 2065764b98..02eca8ed22 100644 --- a/src/v0/destinations/mp/transform.js +++ b/src/v0/destinations/mp/transform.js @@ -386,7 +386,7 @@ const processGroupEvents = (message, type, destination) => { $set: { [groupKey]: groupKeyVal, }, - $ip: get(message, 'context.ip'), + $ip: get(message, 'context.ip') || message.request_ip, }; if (destination?.Config.identityMergeApi === 'simplified') { payload.$distinct_id = message.userId || `$device:${message.anonymousId}`; From b1a0f24eb544a1035eb8bf4ccc43260cffe33932 Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Fri, 7 Jun 2024 14:45:27 +0530 Subject: [PATCH 199/240] chore: upgrade packages --- package-lock.json | 45 ++++++++++++++++++++++++--------------------- package.json | 2 +- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5449e97f36..5effe0c249 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.8", - "@rudderstack/workflow-engine": "^0.7.5", + "@rudderstack/workflow-engine": "^0.7.9", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", @@ -4458,21 +4458,21 @@ } }, "node_modules/@rudderstack/json-template-engine": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.8.5.tgz", - "integrity": "sha512-+iH40g+ZA2ANgwjOITdEdZJLZV+ljR28Akn/dRoDia591tMu7PptyvDaAvl+m1DijWXddpLQ8SX9xaEcIdmqlw==" + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.10.5.tgz", + "integrity": "sha512-PasCK5RDwiRHsFhAb3w0n+8JPRYcZTffe2l+M/wtzvqU+12NPj3YTEIaMWkhogY6AmPYswAaMX/kr+4j7dKiUA==" }, "node_modules/@rudderstack/workflow-engine": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.7.5.tgz", - "integrity": "sha512-HmhxiF/gZorrEEmVvQYopIN6xicQ7kr0mHtw2fPqXmHIFLr9MnEyefo4+MPw/Re9iNFbXNQC9uKkYd7lLHbAyw==", + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.7.9.tgz", + "integrity": "sha512-uMELZk7UXs40bgQkIk7fIVrfHo/5ld+5I5kYgZt5rcT65H9aNpWjnNRnsKH9dgu+oxiBFAMassZq5ko4hpEdIQ==", "dependencies": { - "@aws-crypto/sha256-js": "^5.0.0", - "@rudderstack/json-template-engine": "^0.8.4", - "jsonata": "^2.0.4", + "@aws-crypto/sha256-js": "^5.2.0", + "@rudderstack/json-template-engine": "^0.10.5", + "jsonata": "^2.0.5", "lodash": "^4.17.21", - "object-sizeof": "^2.6.3", - "yaml": "^2.3.2" + "object-sizeof": "^2.6.4", + "yaml": "^2.4.3" } }, "node_modules/@rudderstack/workflow-engine/node_modules/@aws-crypto/sha256-js": { @@ -14480,9 +14480,9 @@ } }, "node_modules/jsonata": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/jsonata/-/jsonata-2.0.4.tgz", - "integrity": "sha512-vfavX4/G/yrYxE+UrmT/oUJ3ph7KqUrb0R7b0LVRcntQwxw+Z5kA1pNUIQzX5hF04Oe1eKxyoIPsmXtc2LgJTQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/jsonata/-/jsonata-2.0.5.tgz", + "integrity": "sha512-wEse9+QLIIU5IaCgtJCPsFi/H4F3qcikWzF4bAELZiRz08ohfx3Q6CjDRf4ZPF5P/92RI3KIHtb7u3jqPaHXdQ==", "engines": { "node": ">= 8" } @@ -16793,9 +16793,9 @@ } }, "node_modules/object-sizeof": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/object-sizeof/-/object-sizeof-2.6.3.tgz", - "integrity": "sha512-GNkVRrLh11Qr5BGr73dwwPE200/78QG2rbx30cnXPnMvt7UuttH4Dup5t+LtcQhARkg8Hbr0c8Kiz52+CFxYmw==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/object-sizeof/-/object-sizeof-2.6.4.tgz", + "integrity": "sha512-YuJAf7Bi61KROcYmXm8RCeBrBw8UOaJDzTm1gp0eU7RjYi1xEte3/Nmg/VyPaHcJZ3sNojs1Y0xvSrgwkLmcFw==", "dependencies": { "buffer": "^6.0.3" } @@ -20964,9 +20964,12 @@ "dev": true }, "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.3.tgz", + "integrity": "sha512-sntgmxj8o7DE7g/Qi60cqpLBA3HG3STcDA0kO+WfB05jEKhZMbY7umNm2rBpQvsmZ16/lPXCJGW2672dgOUkrg==", + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } diff --git a/package.json b/package.json index 106065b72a..3aa3c017ff 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.8", - "@rudderstack/workflow-engine": "^0.7.5", + "@rudderstack/workflow-engine": "^0.7.9", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", From e124470e82b6aa9934094146d4050af02bb62fff Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Fri, 7 Jun 2024 19:25:09 +0530 Subject: [PATCH 200/240] feat: add v3 api support to appsflyer (#3412) * feat: initial commit * feat: onboarding adjust source (#3395) * fix: onboard adjust source * feat: onboard adjust source * feat: small edit * feat: small edit * fix: fb custom audience html response (#3402) * fix: adding test cases for old and new config * fix: review comments addressed --------- Co-authored-by: AASHISH MALIK --- src/v0/destinations/af/config.js | 2 + src/v0/destinations/af/transform.js | 44 +- .../destinations/af/processor/data.ts | 338 +-- .../destinations/af/processor/validation.ts | 2048 +++++++++++++++++ test/integrations/testUtils.ts | 2 + 5 files changed, 2197 insertions(+), 237 deletions(-) create mode 100644 test/integrations/destinations/af/processor/validation.ts diff --git a/src/v0/destinations/af/config.js b/src/v0/destinations/af/config.js index bc49706959..eb3b191950 100644 --- a/src/v0/destinations/af/config.js +++ b/src/v0/destinations/af/config.js @@ -57,6 +57,7 @@ const Event = { }; const ENDPOINT = 'https://api2.appsflyer.com/inappevent/'; +const ENDPOINT_V2 = 'https://api3.appsflyer.com/inappevent/'; const mappingConfig = getMappingConfig(ConfigCategory, __dirname); @@ -69,6 +70,7 @@ events.forEach((event) => { module.exports = { ConfigCategory, ENDPOINT, + ENDPOINT_V2, Event, mappingConfig, nameToEventMap, diff --git a/src/v0/destinations/af/transform.js b/src/v0/destinations/af/transform.js index 72ba47a227..a611dcc249 100644 --- a/src/v0/destinations/af/transform.js +++ b/src/v0/destinations/af/transform.js @@ -17,20 +17,32 @@ const { simpleProcessRouterDest, } = require('../../util'); -const { Event, ENDPOINT, ConfigCategory, mappingConfig, nameToEventMap } = require('./config'); +const { + Event, + ENDPOINT, + ENDPOINT_V2, + ConfigCategory, + mappingConfig, + nameToEventMap, +} = require('./config'); const { JSON_MIME_TYPE } = require('../../util/constant'); function responseBuilderSimple(payload, message, destination) { - const { androidAppId, appleAppId } = destination.Config; + const { androidAppId, appleAppId, sharingFilter, devKey, s2sKey, authVersion } = + destination.Config; let endpoint; const os = get(message, 'context.os.name'); // if ((os && os.toLowerCase() === "android") || (os && isAppleFamily(os))){ // if() // } + + const finalEndPoint = + isDefinedAndNotNull(authVersion) && authVersion === 'v2' ? ENDPOINT_V2 : ENDPOINT; + if (os && os.toLowerCase() === 'android' && androidAppId) { - endpoint = `${ENDPOINT}${androidAppId}`; + endpoint = `${finalEndPoint}${androidAppId}`; } else if (os && isAppleFamily(os) && appleAppId) { - endpoint = `${ENDPOINT}id${appleAppId}`; + endpoint = `${finalEndPoint}id${appleAppId}`; } else { throw new ConfigurationError( 'os name is required along with the respective appId eg. (os->android & Android App Id is required) or (os->ios & Apple App Id is required)', @@ -87,16 +99,19 @@ function responseBuilderSimple(payload, message, destination) { updatedPayload.bundleIdentifier = bundleIdentifier; } - const { sharingFilter, devKey } = destination.Config; + // const { sharingFilter, devKey } = destination.Config; if (isDefinedAndNotNullAndNotEmpty(sharingFilter)) { updatedPayload.sharing_filter = sharingFilter; } + const finalAuthentication = + isDefinedAndNotNull(authVersion) && authVersion === 'v2' ? s2sKey : devKey; + const response = defaultRequestConfig(); response.endpoint = endpoint; response.headers = { 'Content-Type': JSON_MIME_TYPE, - authentication: devKey, + authentication: finalAuthentication, }; response.method = defaultPostRequestConfig.requestMethod; response.body.JSON = removeUndefinedAndNullValues(updatedPayload); @@ -203,6 +218,19 @@ function processEventTypeTrack(message, config) { } function processSingleMessage(message, destination) { + const { devKey, s2sKey, authVersion, useRichEventName } = destination.Config; + + if (!isDefinedAndNotNull(authVersion) && !isDefinedAndNotNull(devKey)) { + throw new ConfigurationError('No authentication key is present. Aborting.'); + } + + if (isDefinedAndNotNull(authVersion) && authVersion === 'v2' && !isDefinedAndNotNull(s2sKey)) { + throw new ConfigurationError('s2s key is mandatory for v2 authorization. Aborting.'); + } + + if (isDefinedAndNotNull(authVersion) && authVersion === 'v1' && !isDefinedAndNotNull(devKey)) { + throw new ConfigurationError('dev key is mandatory for v1 authorization. Aborting.'); + } const messageType = message.type.toLowerCase(); let payload; switch (messageType) { @@ -212,7 +240,7 @@ function processSingleMessage(message, destination) { } case EventType.SCREEN: { let eventName; - if (destination.Config.useRichEventName === true) { + if (useRichEventName === true) { eventName = `Viewed ${ message.name || message.event || get(message, 'properties.name') || '' } Screen`; @@ -224,7 +252,7 @@ function processSingleMessage(message, destination) { } case EventType.PAGE: { let eventName; - if (destination.Config.useRichEventName === true) { + if (useRichEventName === true) { eventName = `Viewed ${message.name || get(message, 'properties.name') || ''} Page`; } else { eventName = EventType.PAGE; diff --git a/test/integrations/destinations/af/processor/data.ts b/test/integrations/destinations/af/processor/data.ts index 210f04331d..9812f7f902 100644 --- a/test/integrations/destinations/af/processor/data.ts +++ b/test/integrations/destinations/af/processor/data.ts @@ -1,4 +1,56 @@ -export const data = [ +import { newConfigValidationTests } from './validation'; + +const commonPropertiesWithProduct = { + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, + currency: 'ZAR', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + products: [ + { + sku: '45790-32', + url: 'https://www.example.com/product/path', + name: 'Monopoly: 3rd Edition', + price: 19, + category: 'Games', + quantity: 1, + image_url: 'https:///www.example.com/product/path.jpg', + product_id: '507f1f77bcf86cd799439011', + }, + { + sku: '46493-32', + name: 'Uno Card Game', + price: 3, + category: 'Games', + quantity: 2, + product_id: '505bd76785ebb509fc183733', + }, + ], + shipping: 3, + subtotal: 22.5, + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', +}; + +const commonContextWithExternalId = { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + source: 'test', + app: { namespace: 'com.rudderlabs.javascript' }, + os: { name: 'android' }, + traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, + library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, +}; + +const commonHeader = { 'Content-Type': 'application/json', authentication: 'abcde' }; + +const commonV2EndPoint = 'https://api3.appsflyer.com/inappevent/com.rudderlabs.javascript'; +const commonV1EndPoint = 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript'; + +export const existingTestCases = [ { name: 'af', description: 'Test 0', @@ -43,7 +95,7 @@ export const data = [ integrations: { AF: { af_uid: 'afUid' } }, }, destination: { - Config: { devKey: 'ef1d42390426e3f7c90ac78272e74344', androidAppId: 'appId' }, + Config: { devKey: 'abcde', androidAppId: 'appId' }, Enabled: true, addPropertiesAtRoot: false, }, @@ -117,7 +169,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', addPropertiesAtRoot: false, }, @@ -136,11 +188,8 @@ export const data = [ output: { version: '1', type: 'REST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, method: 'POST', params: {}, body: { @@ -208,7 +257,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', }, Enabled: true, @@ -227,11 +276,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -305,7 +351,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', addPropertiesAtRoot: false, }, @@ -397,7 +443,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', }, Enabled: true, @@ -474,7 +520,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', }, Enabled: true, @@ -493,11 +539,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -568,7 +611,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', }, Enabled: true, @@ -586,11 +629,8 @@ export const data = [ output: { version: '1', type: 'REST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, method: 'POST', params: {}, body: { @@ -661,7 +701,7 @@ export const data = [ integrations: { AF: { af_uid: 'afUid' } }, }, destination: { - Config: { devKey: 'ef1d42390426e3f7c90ac78272e74344', appleAppId: '123456789' }, + Config: { devKey: 'abcde', appleAppId: '123456789' }, Enabled: true, }, }, @@ -678,10 +718,7 @@ export const data = [ version: '1', type: 'REST', endpoint: 'https://api2.appsflyer.com/inappevent/id123456789', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + headers: commonHeader, method: 'POST', params: {}, body: { @@ -722,51 +759,10 @@ export const data = [ type: 'track', event: 'Order Completed', sentAt: '2020-08-14T05:30:30.118Z', - context: { - externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], - source: 'test', - app: { namespace: 'com.rudderlabs.javascript' }, - os: { name: 'android' }, - traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, - library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, - }, + context: commonContextWithExternalId, messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', timestamp: '2020-08-14T05:30:30.118Z', - properties: { - tax: 2, - total: 27.5, - coupon: 'hasbros', - revenue: 48, - price: 25, - quantity: 2, - currency: 'ZAR', - discount: 2.5, - order_id: '50314b8e9bcf000000000000', - products: [ - { - sku: '45790-32', - url: 'https://www.example.com/product/path', - name: 'Monopoly: 3rd Edition', - price: 19, - category: 'Games', - quantity: 1, - image_url: 'https:///www.example.com/product/path.jpg', - product_id: '507f1f77bcf86cd799439011', - }, - { - sku: '46493-32', - name: 'Uno Card Game', - price: 3, - category: 'Games', - quantity: 2, - product_id: '505bd76785ebb509fc183733', - }, - ], - shipping: 3, - subtotal: 22.5, - affiliation: 'Google Store', - checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', - }, + properties: commonPropertiesWithProduct, anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', integrations: { AF: { af_uid: 'afUid' } }, }, @@ -794,8 +790,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { 'Content-Type': 'application/json', authentication: 'abcde' }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -834,14 +830,7 @@ export const data = [ type: 'track', event: 'Order Completed', sentAt: '2020-08-14T05:30:30.118Z', - context: { - externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], - source: 'test', - app: { namespace: 'com.rudderlabs.javascript' }, - os: { name: 'android' }, - traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, - library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, - }, + context: commonContextWithExternalId, messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', timestamp: '2020-08-14T05:30:30.118Z', properties: { @@ -886,8 +875,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { 'Content-Type': 'application/json', authentication: 'abcde' }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -926,14 +915,7 @@ export const data = [ type: 'track', event: 'Order Completed', sentAt: '2020-08-14T05:30:30.118Z', - context: { - externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], - source: 'test', - app: { namespace: 'com.rudderlabs.javascript' }, - os: { name: 'android' }, - traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, - library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, - }, + context: commonContextWithExternalId, messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', timestamp: '2020-08-14T05:30:30.118Z', anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', @@ -963,8 +945,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { 'Content-Type': 'application/json', authentication: 'abcde' }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -1001,51 +983,10 @@ export const data = [ type: 'track', event: 'normal track event', sentAt: '2020-08-14T05:30:30.118Z', - context: { - externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], - source: 'test', - app: { namespace: 'com.rudderlabs.javascript' }, - os: { name: 'android' }, - traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, - library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, - }, + context: commonContextWithExternalId, messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', timestamp: '2020-08-14T05:30:30.118Z', - properties: { - tax: 2, - total: 27.5, - coupon: 'hasbros', - revenue: 48, - price: 25, - quantity: 2, - currency: 'ZAR', - discount: 2.5, - order_id: '50314b8e9bcf000000000000', - products: [ - { - sku: '45790-32', - url: 'https://www.example.com/product/path', - name: 'Monopoly: 3rd Edition', - price: 19, - category: 'Games', - quantity: 1, - image_url: 'https:///www.example.com/product/path.jpg', - product_id: '507f1f77bcf86cd799439011', - }, - { - sku: '46493-32', - name: 'Uno Card Game', - price: 3, - category: 'Games', - quantity: 2, - product_id: '505bd76785ebb509fc183733', - }, - ], - shipping: 3, - subtotal: 22.5, - affiliation: 'Google Store', - checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', - }, + properties: commonPropertiesWithProduct, anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', integrations: { AF: { af_uid: 'afUid' } }, }, @@ -1074,8 +1015,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { 'Content-Type': 'application/json', authentication: 'abcde' }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -1114,14 +1055,7 @@ export const data = [ type: 'track', event: 'normal track event', sentAt: '2020-08-14T05:30:30.118Z', - context: { - externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], - source: 'test', - app: { namespace: 'com.rudderlabs.javascript' }, - os: { name: 'android' }, - traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, - library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, - }, + context: commonContextWithExternalId, messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', timestamp: '2020-08-14T05:30:30.118Z', anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', @@ -1151,8 +1085,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { 'Content-Type': 'application/json', authentication: 'abcde' }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -1219,7 +1153,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', }, Enabled: true, @@ -1238,11 +1172,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -1313,7 +1244,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', sharingFilter: ['hello'], }, @@ -1332,11 +1263,8 @@ export const data = [ output: { version: '1', type: 'REST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, method: 'POST', params: {}, body: { @@ -1409,7 +1337,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', sharingFilter: 'all', }, @@ -1428,11 +1356,8 @@ export const data = [ output: { version: '1', type: 'REST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, method: 'POST', params: {}, body: { @@ -1505,7 +1430,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', }, Enabled: true, @@ -1581,7 +1506,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', sharingFilter: 'all', addPropertiesAtRoot: true, @@ -1601,11 +1526,8 @@ export const data = [ output: { version: '1', type: 'REST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, method: 'POST', params: {}, body: { @@ -1674,7 +1596,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', addPropertiesAtRoot: true, }, @@ -1694,11 +1616,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -1739,51 +1658,10 @@ export const data = [ type: 'track', event: 'Order Completed', sentAt: '2020-08-14T05:30:30.118Z', - context: { - externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], - source: 'test', - app: { namespace: 'com.rudderlabs.javascript' }, - os: { name: 'android' }, - traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, - library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, - }, + context: commonContextWithExternalId, messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', timestamp: '2020-08-14T05:30:30.118Z', - properties: { - tax: 2, - total: 27.5, - coupon: 'hasbros', - revenue: 48, - price: 25, - quantity: 2, - currency: 'ZAR', - discount: 2.5, - order_id: '50314b8e9bcf000000000000', - products: [ - { - sku: '45790-32', - url: 'https://www.example.com/product/path', - name: 'Monopoly: 3rd Edition', - price: 19, - category: 'Games', - quantity: 1, - image_url: 'https:///www.example.com/product/path.jpg', - product_id: '507f1f77bcf86cd799439011', - }, - { - sku: '46493-32', - name: 'Uno Card Game', - price: 3, - category: 'Games', - quantity: 2, - product_id: '505bd76785ebb509fc183733', - }, - ], - shipping: 3, - subtotal: 22.5, - affiliation: 'Google Store', - checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', - }, + properties: commonPropertiesWithProduct, anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', integrations: { AF: { af_uid: 'afUid' } }, }, @@ -1812,8 +1690,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { 'Content-Type': 'application/json', authentication: 'abcde' }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -1839,3 +1717,5 @@ export const data = [ }, }, ]; + +export const data = [...existingTestCases, ...newConfigValidationTests]; diff --git a/test/integrations/destinations/af/processor/validation.ts b/test/integrations/destinations/af/processor/validation.ts new file mode 100644 index 0000000000..8042570c7d --- /dev/null +++ b/test/integrations/destinations/af/processor/validation.ts @@ -0,0 +1,2048 @@ +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + generateSimplifiedTrackPayload, + overrideDestination, + transformResultBuilder, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'AF', + DestinationDefinition: { + ID: '123', + Name: 'AF', + DisplayName: 'Appsflyer', + Config: {}, + }, + Config: { + androidAppId: 'com.rudderlabs.javascript', + groupTypeTrait: 'email', + groupValueTrait: 'age', + trackProductsOnce: false, + trackRevenuePerProduct: false, + addPropertiesAtRoot: true, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const commonProperties = { + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, +}; + +const commonPropertiesWithProduct = { + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, + currency: 'ZAR', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + products: [ + { + sku: '45790-32', + url: 'https://www.example.com/product/path', + name: 'Monopoly: 3rd Edition', + price: 19, + category: 'Games', + quantity: 1, + image_url: 'https:///www.example.com/product/path.jpg', + product_id: '507f1f77bcf86cd799439011', + }, + { + sku: '46493-32', + name: 'Uno Card Game', + price: 3, + category: 'Games', + quantity: 2, + product_id: '505bd76785ebb509fc183733', + }, + ], + shipping: 3, + subtotal: 22.5, + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', +}; + +const commonContextWithExternalId = { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + source: 'test', + app: { namespace: 'com.rudderlabs.javascript' }, + os: { name: 'android' }, + traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, + library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, +}; + +const commonHeader = { 'Content-Type': 'application/json', authentication: 'abcde' }; + +const commonV2EndPoint = 'https://api3.appsflyer.com/inappevent/com.rudderlabs.javascript'; +const commonV1EndPoint = 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript'; + +export const existingTestCases = [ + { + name: 'af', + description: 'Test 0', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + page: { path: '', referrer: '', search: '', title: '', url: '' }, + screen: { density: 2 }, + }, + type: 'identify', + messageId: '50360b9c-ea8d-409c-b672-c9230f91cce5', + originalTimestamp: '2019-10-15T09:35:31.288Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + userProperties: { test_key: 'test value' }, + sentAt: '2019-10-14T09:03:22.563Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { devKey: 'abcde', androidAppId: 'appId' }, + Enabled: true, + addPropertiesAtRoot: false, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'message type not supported', + statTags: { + destType: 'AF', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 1', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + addPropertiesAtRoot: false, + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + endpoint: commonV1EndPoint, + headers: commonHeader, + method: 'POST', + params: {}, + body: { + JSON: { + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '{"path":"","referrer":"","search":"","title":"","url":""}', + eventName: 'page', + appsflyer_id: 'afUid', + os: '', + ip: '0.0.0.0', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 2', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { email: 'testhubspot2@email.com', name: 'Test Hubspot' }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'track', + messageId: '08829772-d991-427c-b976-b4c4f4430b4e', + originalTimestamp: '2019-10-15T09:35:31.291Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + event: 'test track event HS', + properties: { user_actual_role: 'system_admin, system_user', user_actual_id: 12345 }, + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + eventValue: + '{"properties":{"user_actual_role":"system_admin, system_user","user_actual_id":12345}}', + eventName: 'test track event HS', + customer_user_id: '12345', + ip: '0.0.0.0', + os: '', + appsflyer_id: 'afUid', + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 3', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '5094f5704b9cf2b3', + channel: 'mobile', + context: { + app: { + build: '1', + name: 'LeanPlumIntegrationAndroid', + namespace: 'com.android.SampleLeanPlum', + version: '1.0', + }, + device: { + id: '5094f5704b9cf2b3', + manufacturer: 'Google', + model: 'Android SDK built for x86', + name: 'generic_x86', + type: 'android', + }, + library: { name: 'com.rudderstack.android.sdk.core', version: '1.0.1-beta.1' }, + locale: 'en-US', + network: { carrier: 'Android', bluetooth: false, cellular: true, wifi: true }, + os: { name: 'Android', version: '8.1.0' }, + screen: { density: 420, height: 1794, width: 1080 }, + timezone: 'Asia/Kolkata', + traits: { anonymousId: '5094f5704b9cf2b3' }, + userAgent: + 'Dalvik/2.1.0 (Linux; U; Android 8.1.0; Android SDK built for x86 Build/OSM1.180201.007)', + }, + event: 'MainActivity', + integrations: { All: true }, + messageId: 'id1', + properties: { name: 'MainActivity', automatic: true }, + originalTimestamp: '2020-03-12T09:05:03.421Z', + type: 'screen', + sentAt: '2020-03-12T09:05:13.042Z', + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + addPropertiesAtRoot: false, + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Appsflyer id is not set. Rejecting the event', + statTags: { + destType: 'AF', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 4', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { email: 'test@rudderstack.com' }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'track', + messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', + originalTimestamp: '2019-10-14T11:15:18.300Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + event: 'checkout started', + properties: { + currency: 'CAD', + products: [ + { + product_id: 'pr1', + quantity: 1, + price: 24.75, + name: 'my product', + sku: 'p-298', + }, + { + product_id: 'pr2', + quantity: 1, + price: 24.75, + name: 'my product 2', + sku: 'p-299', + }, + ], + step: 1, + paymentMethod: 'Visa', + testDimension: true, + testMetric: true, + }, + integrations: { All: true }, + sentAt: '2019-10-14T11:15:53.296Z', + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Appsflyer id is not set. Rejecting the event', + statTags: { + destType: 'AF', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 5', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [ + { id: 'some_other2345_sample_external_id', type: 'appsflyerExternalId' }, + ], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '{"path":"","referrer":"","search":"","title":"","url":""}', + eventName: 'page', + appsflyer_id: 'some_other2345_sample_external_id', + os: '', + ip: '0.0.0.0', + app_version_name: '1.0.0', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 6', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + endpoint: commonV1EndPoint, + headers: commonHeader, + method: 'POST', + params: {}, + body: { + JSON: { + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '{"path":"","referrer":"","search":"","title":"","url":""}', + eventName: 'page', + appsflyer_id: 'afUid', + os: '', + ip: '0.0.0.0', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 7', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'ios', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { devKey: 'abcde', appleAppId: '123456789' }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + endpoint: 'https://api2.appsflyer.com/inappevent/id123456789', + headers: commonHeader, + method: 'POST', + params: {}, + body: { + JSON: { + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '{"path":"","referrer":"","search":"","title":"","url":""}', + eventName: 'page', + appsflyer_id: 'afUid', + os: '', + ip: '0.0.0.0', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 8', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonPropertiesWithProduct, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + groupTypeTrait: 'email', + groupValueTrait: 'age', + trackProductsOnce: false, + trackRevenuePerProduct: false, + }, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + bundleIdentifier: 'com.rudderlabs.javascript', + eventValue: + '{"properties":{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"sku":"45790-32","url":"https://www.example.com/product/path","name":"Monopoly: 3rd Edition","price":19,"category":"Games","quantity":1,"image_url":"https:///www.example.com/product/path.jpg","product_id":"507f1f77bcf86cd799439011"},{"sku":"46493-32","name":"Uno Card Game","price":3,"category":"Games","quantity":2,"product_id":"505bd76785ebb509fc183733"}],"shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f"},"af_revenue":48,"af_price":[19,3],"af_quantity":[1,2],"af_order_id":"50314b8e9bcf000000000000","af_content_id":["507f1f77bcf86cd799439011","505bd76785ebb509fc183733"]}', + eventName: 'Order Completed', + eventCurrency: 'ZAR', + eventTime: '2020-08-14T05:30:30.118Z', + appsflyer_id: 'afUid', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 9', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: { + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, + currency: 'ZAR', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + shipping: 3, + subtotal: 22.5, + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', + }, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + groupTypeTrait: 'email', + groupValueTrait: 'age', + trackProductsOnce: false, + trackRevenuePerProduct: false, + }, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + bundleIdentifier: 'com.rudderlabs.javascript', + eventValue: + '{"properties":{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f"},"af_revenue":48,"af_price":25,"af_quantity":2,"af_order_id":"50314b8e9bcf000000000000"}', + eventName: 'Order Completed', + eventCurrency: 'ZAR', + eventTime: '2020-08-14T05:30:30.118Z', + appsflyer_id: 'afUid', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 10', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + groupTypeTrait: 'email', + groupValueTrait: 'age', + trackProductsOnce: false, + trackRevenuePerProduct: false, + }, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + bundleIdentifier: 'com.rudderlabs.javascript', + eventValue: '', + eventName: 'Order Completed', + eventTime: '2020-08-14T05:30:30.118Z', + appsflyer_id: 'afUid', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 11', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'normal track event', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonPropertiesWithProduct, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + groupTypeTrait: 'email', + groupValueTrait: 'age', + trackProductsOnce: false, + trackRevenuePerProduct: false, + afCurrencyAtRoot: true, + }, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + eventValue: + '{"properties":{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"sku":"45790-32","url":"https://www.example.com/product/path","name":"Monopoly: 3rd Edition","price":19,"category":"Games","quantity":1,"image_url":"https:///www.example.com/product/path.jpg","product_id":"507f1f77bcf86cd799439011"},{"sku":"46493-32","name":"Uno Card Game","price":3,"category":"Games","quantity":2,"product_id":"505bd76785ebb509fc183733"}],"shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f"},"af_revenue":48,"af_quantity":2,"af_price":25,"af_currency":"ZAR"}', + eventName: 'normal track event', + eventTime: '2020-08-14T05:30:30.118Z', + eventCurrency: 'ZAR', + appsflyer_id: 'afUid', + bundleIdentifier: 'com.rudderlabs.javascript', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 12', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'normal track event', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + groupTypeTrait: 'email', + groupValueTrait: 'age', + trackProductsOnce: false, + trackRevenuePerProduct: false, + }, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + bundleIdentifier: 'com.rudderlabs.javascript', + eventValue: '', + eventName: 'normal track event', + eventTime: '2020-08-14T05:30:30.118Z', + appsflyer_id: 'afUid', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 13', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '', + eventName: 'page', + appsflyer_id: 'afUid', + os: '', + ip: '0.0.0.0', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 14', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + sharingFilter: ['hello'], + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + endpoint: commonV1EndPoint, + headers: commonHeader, + method: 'POST', + params: {}, + body: { + JSON: { + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '{"path":"","referrer":"","search":"","title":"","url":""}', + eventName: 'page', + appsflyer_id: 'afUid', + os: '', + ip: '0.0.0.0', + sharing_filter: ['hello'], + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 15', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + sharingFilter: 'all', + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + endpoint: commonV1EndPoint, + headers: commonHeader, + method: 'POST', + params: {}, + body: { + JSON: { + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '{"path":"","referrer":"","search":"","title":"","url":""}', + eventName: 'page', + appsflyer_id: 'afUid', + os: '', + ip: '0.0.0.0', + sharing_filter: 'all', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 16', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: '', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'os name is required along with the respective appId eg. (os->android & Android App Id is required) or (os->ios & Apple App Id is required)', + statTags: { + destType: 'AF', + errorCategory: 'dataValidation', + errorType: 'configuration', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Place Properties at root level Page Call', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + sharingFilter: 'all', + addPropertiesAtRoot: true, + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + endpoint: commonV1EndPoint, + headers: commonHeader, + method: 'POST', + params: {}, + body: { + JSON: { + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '{"path":"","referrer":"","search":"","title":"","url":""}', + eventName: 'page', + appsflyer_id: 'afUid', + os: '', + ip: '0.0.0.0', + sharing_filter: 'all', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Place properties at root level track call', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { email: 'testhubspot2@email.com', name: 'Test Hubspot' }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'track', + messageId: '08829772-d991-427c-b976-b4c4f4430b4e', + originalTimestamp: '2019-10-15T09:35:31.291Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + event: 'test track event HS', + properties: { user_actual_role: 'system_admin, system_user', user_actual_id: 12345 }, + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + addPropertiesAtRoot: true, + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + eventValue: + '{"user_actual_role":"system_admin, system_user","user_actual_id":12345}', + eventName: 'test track event HS', + customer_user_id: '12345', + ip: '0.0.0.0', + os: '', + appsflyer_id: 'afUid', + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Place properties at root track call with af data', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonPropertiesWithProduct, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + groupTypeTrait: 'email', + groupValueTrait: 'age', + trackProductsOnce: false, + trackRevenuePerProduct: false, + addPropertiesAtRoot: true, + }, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + bundleIdentifier: 'com.rudderlabs.javascript', + eventValue: + '{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"sku":"45790-32","url":"https://www.example.com/product/path","name":"Monopoly: 3rd Edition","price":19,"category":"Games","quantity":1,"image_url":"https:///www.example.com/product/path.jpg","product_id":"507f1f77bcf86cd799439011"},{"sku":"46493-32","name":"Uno Card Game","price":3,"category":"Games","quantity":2,"product_id":"505bd76785ebb509fc183733"}],"shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","af_revenue":48,"af_price":[19,3],"af_quantity":[1,2],"af_order_id":"50314b8e9bcf000000000000","af_content_id":["507f1f77bcf86cd799439011","505bd76785ebb509fc183733"]}', + eventName: 'Order Completed', + eventCurrency: 'ZAR', + eventTime: '2020-08-14T05:30:30.118Z', + appsflyer_id: 'afUid', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, +]; + +export const newConfigValidationTests: ProcessorTestData[] = [ + { + id: 'af-config-validation-test-1', + name: 'af', + description: 'No authentication Key is present', + scenario: 'Framework', + successCriteria: + 'The request should fail as atleast one authentication key is must to propagate request', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: {}, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonProperties, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'No authentication key is present. Aborting.', + statTags: { + destinationId: 'default-destinationId', + destType: 'AF', + errorCategory: 'dataValidation', + errorType: 'configuration', + feature: 'processor', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'af-config-validation-test-2', + name: 'af', + description: 'No dev key present even if v1 authorization is chosen', + scenario: 'Framework', + successCriteria: + 'The request should fail when authorization is of v1 type and there is no dev key', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(destination, { + authVersion: 'v1', + s2sKey: 'dummy', + }), + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: {}, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonProperties, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'dev key is mandatory for v1 authorization. Aborting.', + statTags: { + destType: 'AF', + destinationId: 'default-destinationId', + errorCategory: 'dataValidation', + errorType: 'configuration', + feature: 'processor', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'af-config-validation-test-3', + name: 'af', + description: 'No dev key present even if v1 authorization is chosen', + scenario: 'Framework', + successCriteria: + 'The request should fail when authorization is of v1 type and there is no dev key', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(destination, { + authVersion: 'v2', + devKey: 'abcde', + }), + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: {}, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonProperties, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 's2s key is mandatory for v2 authorization. Aborting.', + statTags: { + destType: 'AF', + errorCategory: 'dataValidation', + errorType: 'configuration', + feature: 'processor', + implementation: 'native', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'af-config-validation-test-4', + name: 'af', + description: 'New Config and old way of authentication', + scenario: 'Business', + successCriteria: 'Response should contain only event payload and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(destination, { + authVersion: 'v1', + devKey: 'abcde', + }), + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonPropertiesWithProduct, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + JSON: { + customer_user_id: 'default-user-id', + bundleIdentifier: 'com.rudderlabs.javascript', + eventValue: + '{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"sku":"45790-32","url":"https://www.example.com/product/path","name":"Monopoly: 3rd Edition","price":19,"category":"Games","quantity":1,"image_url":"https:///www.example.com/product/path.jpg","product_id":"507f1f77bcf86cd799439011"},{"sku":"46493-32","name":"Uno Card Game","price":3,"category":"Games","quantity":2,"product_id":"505bd76785ebb509fc183733"}],"shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","af_revenue":48,"af_price":[19,3],"af_quantity":[1,2],"af_order_id":"50314b8e9bcf000000000000","af_content_id":["507f1f77bcf86cd799439011","505bd76785ebb509fc183733"]}', + eventName: 'Order Completed', + eventCurrency: 'ZAR', + appsflyer_id: 'afUid', + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'af-config-validation-test-5', + name: 'af', + description: 'New Config and new way of authentication', + scenario: 'Business', + successCriteria: 'Response should contain only event payload and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(destination, { + authVersion: 'v2', + s2sKey: 'dummy', + }), + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonPropertiesWithProduct, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: commonV2EndPoint, + headers: { ...commonHeader, authentication: 'dummy' }, + JSON: { + bundleIdentifier: 'com.rudderlabs.javascript', + eventValue: + '{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"sku":"45790-32","url":"https://www.example.com/product/path","name":"Monopoly: 3rd Edition","price":19,"category":"Games","quantity":1,"image_url":"https:///www.example.com/product/path.jpg","product_id":"507f1f77bcf86cd799439011"},{"sku":"46493-32","name":"Uno Card Game","price":3,"category":"Games","quantity":2,"product_id":"505bd76785ebb509fc183733"}],"shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","af_revenue":48,"af_price":[19,3],"af_quantity":[1,2],"af_order_id":"50314b8e9bcf000000000000","af_content_id":["507f1f77bcf86cd799439011","505bd76785ebb509fc183733"]}', + eventName: 'Order Completed', + eventCurrency: 'ZAR', + customer_user_id: 'default-user-id', + appsflyer_id: 'afUid', + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/testUtils.ts b/test/integrations/testUtils.ts index 694f224859..a6f0720e37 100644 --- a/test/integrations/testUtils.ts +++ b/test/integrations/testUtils.ts @@ -244,6 +244,8 @@ export const generateSimplifiedTrackPayload: any = (parametersOverride: any) => externalId: parametersOverride.context.externalId, traits: parametersOverride.context.traits, device: parametersOverride.context.device, + os: parametersOverride.context.os, + app: parametersOverride.context.app, }), anonymousId: parametersOverride.anonymousId || 'default-anonymousId', originalTimestamp: parametersOverride.originalTimestamp || '2021-01-03T17:02:53.193Z', From 3bea186e725ec473ad757760355d6cc9670c4f8c Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:12:04 +0530 Subject: [PATCH 201/240] fix: allowing traffic type dynamically for split.io (#3425) --- src/v0/destinations/splitio/data/EventConfig.json | 12 ++++++++++++ src/v0/destinations/splitio/transform.js | 8 +++++++- .../destinations/splitio/processor/data.ts | 9 ++++++--- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/v0/destinations/splitio/data/EventConfig.json b/src/v0/destinations/splitio/data/EventConfig.json index d41398a5e2..6435458368 100644 --- a/src/v0/destinations/splitio/data/EventConfig.json +++ b/src/v0/destinations/splitio/data/EventConfig.json @@ -29,5 +29,17 @@ "type": "toFloat" }, "required": false + }, + { + "destKey": "trafficTypeName", + "sourceKeys": [ + "traits.trafficTypeName", + "context.traits.trafficTypeName", + "properties.trafficTypeName" + ], + "metadata": { + "type": "toString" + }, + "required": false } ] diff --git a/src/v0/destinations/splitio/transform.js b/src/v0/destinations/splitio/transform.js index 6641a62fe8..3dd5bea4f2 100644 --- a/src/v0/destinations/splitio/transform.js +++ b/src/v0/destinations/splitio/transform.js @@ -56,6 +56,7 @@ function prepareResponse(message, destination, category) { let outputPayload = {}; + // ref: https://docs.split.io/reference/events-overview outputPayload = constructPayload(message, MAPPING_CONFIG[category.name]); outputPayload.eventTypeId = outputPayload.eventTypeId.replace(/ /g, '_'); if (EVENT_TYPE_ID_REGEX.test(outputPayload.eventTypeId)) { @@ -93,7 +94,12 @@ function prepareResponse(message, destination, category) { if (isDefinedAndNotNullAndNotEmpty(environment)) { outputPayload.environmentName = environment; } - outputPayload.trafficTypeName = trafficType; + + // in case traffic type could not be mapped from the input payloads, falls back to the UI configured default traffic type. + if (!isDefinedAndNotNullAndNotEmpty(outputPayload.trafficTypeName)) { + outputPayload.trafficTypeName = trafficType; + } + outputPayload.properties = removeUndefinedNullValuesAndEmptyObjectArray( flattenJson(bufferProperty), ); diff --git a/test/integrations/destinations/splitio/processor/data.ts b/test/integrations/destinations/splitio/processor/data.ts index 3fda93cc30..d4c3e39794 100644 --- a/test/integrations/destinations/splitio/processor/data.ts +++ b/test/integrations/destinations/splitio/processor/data.ts @@ -43,7 +43,7 @@ export const data = [ Config: { apiKey: 'abcde', environment: 'staging', - trafficType: 'user', + trafficType: 'anonymous', }, }, }, @@ -246,7 +246,7 @@ export const data = [ Config: { apiKey: 'abcde', environment: 'production', - trafficType: 'user', + trafficType: 'anonymous', }, }, }, @@ -847,6 +847,9 @@ export const data = [ library: { name: 'http', }, + traits: { + trafficTypeName: 'user', + }, }, type: 'identify', timestamp: '2020-01-21T00:21:34.208Z', @@ -858,7 +861,7 @@ export const data = [ Config: { apiKey: 'abcde', environment: 'staging', - trafficType: 'user', + trafficType: 'anonymous', }, }, }, From 775e8ee55a62ecddb58ff505302e4aabb8bffe24 Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:15:12 +0530 Subject: [PATCH 202/240] fix: bugsnag issue fix for zendesk (#3439) --- src/v0/destinations/zendesk/transform.js | 4 + .../destinations/zendesk/processor/data.ts | 85 +++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/src/v0/destinations/zendesk/transform.js b/src/v0/destinations/zendesk/transform.js index 5862014784..cadb1d3964 100644 --- a/src/v0/destinations/zendesk/transform.js +++ b/src/v0/destinations/zendesk/transform.js @@ -4,6 +4,7 @@ const { NetworkInstrumentationError, InstrumentationError, NetworkError, + isDefinedAndNotNull, } = require('@rudderstack/integrations-lib'); const myAxios = require('../../../util/myAxios'); @@ -405,6 +406,9 @@ async function getUserMembershipPayload(message, headers, orgId, destinationConf } async function createOrganization(message, category, headers, destinationConfig, baseEndpoint) { + if (!isDefinedAndNotNull(message.traits)) { + throw new InstrumentationError('Organisation Traits are missing. Aborting.'); + } await checkAndCreateUserFields( message.traits, category.organizationFieldsEndpoint, diff --git a/test/integrations/destinations/zendesk/processor/data.ts b/test/integrations/destinations/zendesk/processor/data.ts index 4dab6c5d5d..d84cdba15a 100644 --- a/test/integrations/destinations/zendesk/processor/data.ts +++ b/test/integrations/destinations/zendesk/processor/data.ts @@ -2370,4 +2370,89 @@ export const data = [ }, }, }, + { + name: 'zendesk', + description: 'Group Call : If message.traits are not present, aborting the call.', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + apiToken: 'myDummyApiToken4', + createUsersAsVerified: true, + domain: 'rudderlabshelp', + email: 'myDummyUserName1', + password: 'myDummyPwd1', + removeUsersFromOrganization: true, + sendGroupCallsWithoutUserId: true, + }, + DestinationDefinition: { + DisplayName: 'Zendesk', + ID: '1YknZ1ENqB8UurJQJE2VrEA61tr', + Name: 'ZENDESK', + }, + Enabled: true, + ID: 'xxxxxxxxxxxxxxxxxxxxxxxO51P', + Name: 'zendesk', + Transformations: [], + }, + message: { + anonymousId: '297b0750-934b-4411-b66c-9b418cdbc0c9', + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.1.0-beta.2', + }, + ip: '0.0.0.0', + library: { name: 'RudderLabs JavaScript SDK', version: '1.1.0-beta.2' }, + locale: 'en-GB', + os: { name: '', version: '' }, + screen: { density: 2 }, + traits: { email: 'example124@email.com', name: 'abcd124' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36', + }, + groupId: 'group-124', + integrations: { All: true }, + messageId: '2d54ba80-ce5f-4bcb-b1d7-7587e7a865fc', + originalTimestamp: '2020-03-23T18:27:28.983Z', + receivedAt: '2020-03-23T23:57:29.022+05:30', + request_ip: '[::1]:51574', + sentAt: '2020-03-23T18:27:28.983Z', + timestamp: '2020-03-23T23:57:29.022+05:30', + type: 'group', + userId: 'abcd-124', + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Organisation Traits are missing. Aborting.', + statTags: { + destType: 'ZENDESK', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, ]; From a39df2b4139e9a85ada78665393d0bf62f9272df Mon Sep 17 00:00:00 2001 From: Utsab Chowdhury Date: Mon, 10 Jun 2024 15:58:12 +0530 Subject: [PATCH 203/240] chore: add metrics for braze alias failures (#3452) --- src/util/prometheus.js | 6 ++++ src/v0/destinations/braze/transform.js | 4 +++ src/v0/destinations/braze/util.js | 43 ++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 78d32c9cb9..bc4c6f2eb9 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -601,6 +601,12 @@ class Prometheus { type: 'gauge', labelNames: ['destination_id'], }, + { + name: 'braze_alias_failure_count', + help: 'braze_alias_failure_count', + type: 'counter', + labelNames: ['destination_id'], + }, { name: 'mixpanel_batch_engage_pack_size', help: 'mixpanel_batch_engage_pack_size', diff --git a/src/v0/destinations/braze/transform.js b/src/v0/destinations/braze/transform.js index d45640272e..11b2bb0636 100644 --- a/src/v0/destinations/braze/transform.js +++ b/src/v0/destinations/braze/transform.js @@ -13,6 +13,7 @@ const { getPurchaseObjs, setExternalId, setAliasObjectWithAnonId, + collectStatsForAliasFailure, } = require('./util'); const tags = require('../../util/tags'); const { EventType, MappedToDestinationKey } = require('../../../constants'); @@ -228,6 +229,7 @@ async function processIdentify(message, destination) { endpointPath: '/users/identify', }, ); + if (!isHttpStatusSuccess(brazeIdentifyResp.status)) { throw new NetworkError( `Braze identify failed - ${JSON.stringify(brazeIdentifyResp.response)}`, @@ -238,6 +240,8 @@ async function processIdentify(message, destination) { brazeIdentifyResp.response, ); } + + collectStatsForAliasFailure(brazeIdentifyResp.response, destination.ID); } function processTrackWithUserAttributes( diff --git a/src/v0/destinations/braze/util.js b/src/v0/destinations/braze/util.js index ce83ebc244..f131c40f5f 100644 --- a/src/v0/destinations/braze/util.js +++ b/src/v0/destinations/braze/util.js @@ -1,6 +1,7 @@ /* eslint-disable */ const _ = require('lodash'); const get = require('get-value'); +const { structuredLogger: logger } = require('@rudderstack/integrations-lib'); const stats = require('../../../util/stats'); const { handleHttpRequest } = require('../../../adapters/network'); const { @@ -655,6 +656,47 @@ function getPurchaseObjs(message, config) { return purchaseObjs; } +const collectStatsForAliasFailure = (brazeResponse, destinationId) => { + /** + * Braze Response for Alias failure + * { + * "aliases_processed": 0, + * "message": "success", + * "errors": [ + * { + * "type": "'external_id' is required", + * "input_array": "user_identifiers", + * "index": 0 + * } + * ] + * } + */ + + /** + * Braze Response for Alias success + * { + * "aliases_processed": 1, + * "message": "success" + * } + */ + + // Should not happen but still checking for unhandled exceptions + if (!isDefinedAndNotNull(brazeResponse)) { + return; + } + const { aliases_processed: aliasesProcessed, errors } = brazeResponse; + if (aliasesProcessed === 0) { + stats.increment('braze_alias_failure_count', { destination_id: destinationId }); + + if (Array.isArray(errors)) { + logger.info('Braze Alias Failure Errors:', { + destinationId, + errors, + }); + } + } +}; + module.exports = { BrazeDedupUtility, CustomAttributeOperationUtil, @@ -667,4 +709,5 @@ module.exports = { setExternalId, setAliasObjectWithAnonId, addMandatoryPurchaseProperties, + collectStatsForAliasFailure, }; From 92953bf2c8ba23854bcc46f21e8b42cd52caaf28 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 10 Jun 2024 11:10:55 +0000 Subject: [PATCH 204/240] chore(release): 1.69.0 --- CHANGELOG.md | 15 +++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b2fff8bce..42b22827da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.69.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.68.2...v1.69.0) (2024-06-10) + + +### Features + +* add request_ip as fallback for mixpanel group call ([#3421](https://github.com/rudderlabs/rudder-transformer/issues/3421)) ([a73ab75](https://github.com/rudderlabs/rudder-transformer/commit/a73ab75032d753b35cb0e18234dcd7289dd1e644)) +* add v3 api support to appsflyer ([#3412](https://github.com/rudderlabs/rudder-transformer/issues/3412)) ([e124470](https://github.com/rudderlabs/rudder-transformer/commit/e124470e82b6aa9934094146d4050af02bb62fff)), closes [#3395](https://github.com/rudderlabs/rudder-transformer/issues/3395) [#3402](https://github.com/rudderlabs/rudder-transformer/issues/3402) +* changes for supporting record event in FB audience ([#3351](https://github.com/rudderlabs/rudder-transformer/issues/3351)) ([ac4a32a](https://github.com/rudderlabs/rudder-transformer/commit/ac4a32ab5e0c7e02a149e81d455666ed24fa01a3)) + + +### Bug Fixes + +* allowing traffic type dynamically for split.io ([#3425](https://github.com/rudderlabs/rudder-transformer/issues/3425)) ([3bea186](https://github.com/rudderlabs/rudder-transformer/commit/3bea186e725ec473ad757760355d6cc9670c4f8c)) +* bugsnag issue fix for zendesk ([#3439](https://github.com/rudderlabs/rudder-transformer/issues/3439)) ([775e8ee](https://github.com/rudderlabs/rudder-transformer/commit/775e8ee55a62ecddb58ff505302e4aabb8bffe24)) + ### [1.68.2](https://github.com/rudderlabs/rudder-transformer/compare/v1.68.1...v1.68.2) (2024-06-06) diff --git a/package-lock.json b/package-lock.json index 5effe0c249..7fa0d6d3d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.68.2", + "version": "1.69.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.68.2", + "version": "1.69.0", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index 3aa3c017ff..2c7c6711e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.68.2", + "version": "1.69.0", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From 2de972c924a6cba3437b4e5b88ed1958a8f10635 Mon Sep 17 00:00:00 2001 From: Utsab Chowdhury Date: Tue, 11 Jun 2024 15:51:32 +0530 Subject: [PATCH 205/240] chore: onboard custom mappings for GA4_v2 (#3289) --- package-lock.json | 160 +++- package.json | 5 +- src/v0/destinations/ga4/transform.js | 119 +-- src/v0/destinations/ga4/utils.js | 142 +++- .../ga4_v2/customMappingsHandler.js | 165 ++++ src/v0/destinations/ga4_v2/transform.ts | 25 + src/v0/util/index.js | 26 + src/v0/util/mapWithJSONPath.js | 58 ++ .../destinations/ga4/processor/data.ts | 14 +- .../ga4/processor/exisitngTests.ts | 13 + .../integrations/destinations/ga4_v2/mocks.ts | 5 + .../ga4_v2/processor/customMappings.ts | 721 ++++++++++++++++++ .../destinations/ga4_v2/processor/data.ts | 3 + 13 files changed, 1324 insertions(+), 132 deletions(-) create mode 100644 src/v0/destinations/ga4_v2/customMappingsHandler.js create mode 100644 src/v0/destinations/ga4_v2/transform.ts create mode 100644 src/v0/util/mapWithJSONPath.js create mode 100644 test/integrations/destinations/ga4/processor/exisitngTests.ts create mode 100644 test/integrations/destinations/ga4_v2/mocks.ts create mode 100644 test/integrations/destinations/ga4_v2/processor/customMappings.ts create mode 100644 test/integrations/destinations/ga4_v2/processor/data.ts diff --git a/package-lock.json b/package-lock.json index 5effe0c249..4681bd1661 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,8 @@ "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.8", - "@rudderstack/workflow-engine": "^0.7.9", + "@rudderstack/json-template-engine": "^0.11.0", + "@rudderstack/workflow-engine": "^0.8.0", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", @@ -60,6 +61,7 @@ "parse-static-imports": "^1.1.0", "prom-client": "^14.2.0", "qs": "^6.11.1", + "rs-jsonpath": "^1.1.2", "rudder-transformer-cdk": "^1.4.11", "set-value": "^4.1.0", "sha256": "^0.2.0", @@ -78,6 +80,7 @@ "@digitalroute/cz-conventional-changelog-for-jira": "^8.0.1", "@types/fast-json-stable-stringify": "^2.1.0", "@types/jest": "^29.5.1", + "@types/jsonpath": "^0.2.4", "@types/koa": "^2.13.6", "@types/koa-bodyparser": "^4.3.10", "@types/lodash": "^4.14.197", @@ -118,6 +121,27 @@ "typescript": "^5.0.4" } }, + "../jsonpath": { + "name": "rs-jsonpath", + "version": "1.1.2", + "extraneous": true, + "license": "MIT", + "dependencies": { + "esprima": "1.2.2", + "static-eval": "2.0.2", + "underscore": "1.12.1" + }, + "devDependencies": { + "grunt": "0.4.5", + "grunt-browserify": "3.8.0", + "grunt-cli": "0.1.13", + "grunt-contrib-uglify": "0.9.1", + "jison": "0.4.13", + "jscs": "1.10.0", + "jshint": "2.6.0", + "mocha": "2.1.0" + } + }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", @@ -4458,17 +4482,17 @@ } }, "node_modules/@rudderstack/json-template-engine": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.10.5.tgz", - "integrity": "sha512-PasCK5RDwiRHsFhAb3w0n+8JPRYcZTffe2l+M/wtzvqU+12NPj3YTEIaMWkhogY6AmPYswAaMX/kr+4j7dKiUA==" + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.11.0.tgz", + "integrity": "sha512-9XrzY7W9mL2lYro2NOSInuDElW7Qk0nP61UbrfJiTQfrzbyaH7ml663eD07a/4ia3uQynITPsSIGHpMgP3qlEw==" }, "node_modules/@rudderstack/workflow-engine": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.7.9.tgz", - "integrity": "sha512-uMELZk7UXs40bgQkIk7fIVrfHo/5ld+5I5kYgZt5rcT65H9aNpWjnNRnsKH9dgu+oxiBFAMassZq5ko4hpEdIQ==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.8.0.tgz", + "integrity": "sha512-oBRucBNR29E2PzwHX3hANT0c6V0yFKNMWxDg0jr8Hin4co6KZjxi4FdpkzTNWvk2h+8iXT8NCSPdJBjt03hTrw==", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", - "@rudderstack/json-template-engine": "^0.10.5", + "@rudderstack/json-template-engine": "^0.11.0", "jsonata": "^2.0.5", "lodash": "^4.17.21", "object-sizeof": "^2.6.4", @@ -5407,6 +5431,12 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "node_modules/@types/jsonpath": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@types/jsonpath/-/jsonpath-0.2.4.tgz", + "integrity": "sha512-K3hxB8Blw0qgW6ExKgMbXQv2UPZBoE2GqLpVY+yr7nMD2Pq86lsuIzyAaiQ7eMqFL5B6di6pxSkogLJEyEHoGA==", + "dev": true + }, "node_modules/@types/keygrip": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.6.tgz", @@ -10016,7 +10046,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -18603,6 +18632,28 @@ "node": ">=18.0" } }, + "node_modules/rs-jsonpath": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/rs-jsonpath/-/rs-jsonpath-1.1.2.tgz", + "integrity": "sha512-IQzlqtVyZniK7aOtpKGrv7BvkamSvLJkIhRGoKKDQLppNJe94BVHqpxNRjw/2042nGjtC3vyfCWyHe+3DlWgWA==", + "dependencies": { + "esprima": "1.2.2", + "static-eval": "2.0.2", + "underscore": "1.12.1" + } + }, + "node_modules/rs-jsonpath/node_modules/esprima": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", + "integrity": "sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/rudder-transformer-cdk": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/rudder-transformer-cdk/-/rudder-transformer-cdk-1.4.11.tgz", @@ -19480,6 +19531,91 @@ "node": ">=10" } }, + "node_modules/static-eval": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", + "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==", + "dependencies": { + "escodegen": "^1.8.1" + } + }, + "node_modules/static-eval/node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/static-eval/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/static-eval/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/static-eval/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/static-eval/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-eval/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/stats-accumulator": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/stats-accumulator/-/stats-accumulator-1.1.3.tgz", @@ -20420,6 +20556,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/underscore": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -20810,7 +20951,6 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, "engines": { "node": ">=0.10.0" } diff --git a/package.json b/package.json index 3aa3c017ff..afac6edcb2 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,8 @@ "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.8", - "@rudderstack/workflow-engine": "^0.7.9", + "@rudderstack/json-template-engine": "^0.11.0", + "@rudderstack/workflow-engine": "^0.8.0", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", @@ -105,6 +106,7 @@ "parse-static-imports": "^1.1.0", "prom-client": "^14.2.0", "qs": "^6.11.1", + "rs-jsonpath": "^1.1.2", "rudder-transformer-cdk": "^1.4.11", "set-value": "^4.1.0", "sha256": "^0.2.0", @@ -123,6 +125,7 @@ "@digitalroute/cz-conventional-changelog-for-jira": "^8.0.1", "@types/fast-json-stable-stringify": "^2.1.0", "@types/jest": "^29.5.1", + "@types/jsonpath": "^0.2.4", "@types/koa": "^2.13.6", "@types/koa-bodyparser": "^4.3.10", "@types/lodash": "^4.14.197", diff --git a/src/v0/destinations/ga4/transform.js b/src/v0/destinations/ga4/transform.js index 5280a46dab..e4dad80564 100644 --- a/src/v0/destinations/ga4/transform.js +++ b/src/v0/destinations/ga4/transform.js @@ -1,25 +1,15 @@ const get = require('get-value'); -const { - ConfigurationError, - InstrumentationError, - UnsupportedEventError, -} = require('@rudderstack/integrations-lib'); +const { InstrumentationError, UnsupportedEventError } = require('@rudderstack/integrations-lib'); const { EventType } = require('../../../constants'); const { isEmptyObject, constructPayload, getIntegrationsObj, isHybridModeEnabled, - isDefinedAndNotNull, - defaultRequestConfig, - defaultPostRequestConfig, - getDestinationExternalID, removeUndefinedAndNullValues, } = require('../../util'); const { - ENDPOINT, mappingConfig, - DEBUG_ENDPOINT, ConfigCategory, trackCommonConfig, VALID_ITEM_OR_PRODUCT_PROPERTIES, @@ -36,33 +26,12 @@ const { GA4_PARAMETERS_EXCLUSION, GA4_RESERVED_PARAMETER_EXCLUSION, removeReservedParameterPrefixNames, + basicValidation, + addClientDetails, + buildDeliverablePayload, + basicConfigvalidaiton, } = require('./utils'); -const { JSON_MIME_TYPE } = require('../../util/constant'); - -/** - * returns client_id - * @param {*} message - * @returns - */ -const getGA4ClientId = (message, Config) => { - let clientId; - - if (isHybridModeEnabled(Config)) { - const integrationsObj = getIntegrationsObj(message, 'ga4'); - if (integrationsObj?.clientId) { - clientId = integrationsObj.clientId; - } - } - - if (!clientId) { - clientId = - getDestinationExternalID(message, 'ga4ClientId') || - get(message, 'anonymousId') || - get(message, 'rudderId'); - } - - return clientId; -}; +require('../../util/constant'); /** * Returns response for GA4 destination @@ -72,14 +41,9 @@ const getGA4ClientId = (message, Config) => { */ const responseBuilder = (message, { Config }) => { let event = get(message, 'event'); - if (!event) { - throw new InstrumentationError('Event name is required'); - } + basicValidation(event); // trim and replace spaces with "_" - if (typeof event !== 'string') { - throw new InstrumentationError('track:: event name should be string'); - } event = event.trim().replace(/\s+/g, '_'); // reserved event names are not allowed @@ -90,25 +54,7 @@ const responseBuilder = (message, { Config }) => { // get common top level rawPayload let rawPayload = constructPayload(message, trackCommonConfig); - switch (Config.typesOfClient) { - case 'gtag': - // gtag.js uses client_id - // GA4 uses it as an identifier to distinguish site visitors. - rawPayload.client_id = getGA4ClientId(message, Config); - if (!isDefinedAndNotNull(rawPayload.client_id)) { - throw new ConfigurationError('ga4ClientId, anonymousId or messageId must be provided'); - } - break; - case 'firebase': - // firebase uses app_instance_id - rawPayload.app_instance_id = getDestinationExternalID(message, 'ga4AppInstanceId'); - if (!isDefinedAndNotNull(rawPayload.app_instance_id)) { - throw new InstrumentationError('ga4AppInstanceId must be provided under externalId'); - } - break; - default: - throw new ConfigurationError('Invalid type of client'); - } + rawPayload = addClientDetails(rawPayload, message, Config); let payload = {}; const eventConfig = ConfigCategory[`${event.toUpperCase()}`]; @@ -248,62 +194,21 @@ const responseBuilder = (message, { Config }) => { payload = removeUndefinedAndNullValues(payload); rawPayload = { ...rawPayload, events: [payload] }; - // build response - const response = defaultRequestConfig(); - response.method = defaultPostRequestConfig.requestMethod; - // if debug_mode is true, we need to send the event to debug validation server - // ref: https://developers.google.com/analytics/devguides/collection/protocol/ga4/validating-events?client_type=firebase#sending_events_for_validation - if (Config.debugMode) { - response.endpoint = DEBUG_ENDPOINT; - } else { - response.endpoint = ENDPOINT; - } - response.headers = { - HOST: 'www.google-analytics.com', - 'Content-Type': JSON_MIME_TYPE, - }; - response.params = { - api_secret: Config.apiSecret, - }; - - // setting response params as per client type - switch (Config.typesOfClient) { - case 'gtag': - response.params.measurement_id = Config.measurementId; - break; - case 'firebase': - response.params.firebase_app_id = Config.firebaseAppId; - break; - default: - break; - } - - response.body.JSON = rawPayload; - return response; + return buildDeliverablePayload(rawPayload, Config); }; const process = (event) => { const { message, destination } = event; const { Config } = destination; - if (!Config.typesOfClient) { - throw new ConfigurationError('Client type not found. Aborting '); - } - if (!Config.apiSecret) { - throw new ConfigurationError('API Secret not found. Aborting '); - } - if (Config.typesOfClient === 'gtag' && !Config.measurementId) { - throw new ConfigurationError('measurementId must be provided. Aborting'); - } - if (Config.typesOfClient === 'firebase' && !Config.firebaseAppId) { - throw new ConfigurationError('firebaseAppId must be provided. Aborting'); - } - if (!message.type) { throw new InstrumentationError('Message Type is not present. Aborting message.'); } + basicConfigvalidaiton(Config); + const messageType = message.type.toLowerCase(); + let response; switch (messageType) { case EventType.TRACK: diff --git a/src/v0/destinations/ga4/utils.js b/src/v0/destinations/ga4/utils.js index ce8afda560..77f78fbfdb 100644 --- a/src/v0/destinations/ga4/utils.js +++ b/src/v0/destinations/ga4/utils.js @@ -1,5 +1,8 @@ +/* eslint-disable no-param-reassign */ +/* eslint-disable no-plusplus */ const get = require('get-value'); -const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); +const { cloneDeep } = require('lodash'); const { isEmpty, constructPayload, @@ -8,9 +11,14 @@ const { extractCustomFields, isDefinedAndNotNull, getIntegrationsObj, + getDestinationExternalID, + isHybridModeEnabled, + defaultPostRequestConfig, + defaultRequestConfig, } = require('../../util'); -const { mappingConfig, ConfigCategory } = require('./config'); +const { mappingConfig, ConfigCategory, DEBUG_ENDPOINT, ENDPOINT } = require('./config'); const { finaliseAnalyticsConsents } = require('../../util/googleUtils'); +const { JSON_MIME_TYPE } = require('../../util/constant'); /** * Reserved event names cannot be used @@ -452,7 +460,136 @@ const prepareUserConsents = (message) => { return consents; }; +const basicValidation = (event) => { + if (!event) { + throw new InstrumentationError('Event name is required'); + } + if (typeof event !== 'string') { + throw new InstrumentationError('track:: event name should be string'); + } +}; + +/** + * returns client_id + * @param {*} message + * @returns + */ +const getGA4ClientId = (message, Config) => { + let clientId; + + if (isHybridModeEnabled(Config)) { + const integrationsObj = getIntegrationsObj(message, 'ga4'); + if (integrationsObj?.clientId) { + clientId = integrationsObj.clientId; + } + } + + if (!clientId) { + clientId = + getDestinationExternalID(message, 'ga4ClientId') || + get(message, 'anonymousId') || + get(message, 'rudderId'); + } + + return clientId; +}; + +const addClientDetails = (payload, message, Config) => { + const { typesOfClient } = Config; + const rawPayload = cloneDeep(payload); + switch (typesOfClient) { + case 'gtag': + // gtag.js uses client_id + // GA4 uses it as an identifier to distinguish site visitors. + rawPayload.client_id = getGA4ClientId(message, Config); + if (!isDefinedAndNotNull(rawPayload.client_id)) { + throw new ConfigurationError('ga4ClientId, anonymousId or messageId must be provided'); + } + break; + case 'firebase': + // firebase uses app_instance_id + rawPayload.app_instance_id = getDestinationExternalID(message, 'ga4AppInstanceId'); + if (!isDefinedAndNotNull(rawPayload.app_instance_id)) { + throw new InstrumentationError('ga4AppInstanceId must be provided under externalId'); + } + break; + default: + throw new ConfigurationError('Invalid type of client'); + } + return rawPayload; +}; + +const buildDeliverablePayload = (payload, Config) => { + // build response + const response = defaultRequestConfig(); + response.method = defaultPostRequestConfig.requestMethod; + // if debug_mode is true, we need to send the event to debug validation server + // ref: https://developers.google.com/analytics/devguides/collection/protocol/ga4/validating-events?client_type=firebase#sending_events_for_validation + if (Config.debugMode) { + response.endpoint = DEBUG_ENDPOINT; + } else { + response.endpoint = ENDPOINT; + } + response.headers = { + HOST: 'www.google-analytics.com', + 'Content-Type': JSON_MIME_TYPE, + }; + response.params = { + api_secret: Config.apiSecret, + }; + + // setting response params as per client type + switch (Config.typesOfClient) { + case 'gtag': + response.params.measurement_id = Config.measurementId; + break; + case 'firebase': + response.params.firebase_app_id = Config.firebaseAppId; + break; + default: + break; + } + + response.body.JSON = payload; + return response; +}; + +const sanitizeUserProperties = (userProperties) => { + Object.keys(userProperties).forEach((key) => { + const propetyValue = userProperties[key]; + if ( + typeof propetyValue === 'string' || + typeof propetyValue === 'number' || + typeof propetyValue === 'boolean' + ) { + delete userProperties[key]; + userProperties[key] = { + value: propetyValue, + }; + } + }); +}; + +const basicConfigvalidaiton = (Config) => { + if (!Config.typesOfClient) { + throw new ConfigurationError('Client type not found. Aborting '); + } + if (!Config.apiSecret) { + throw new ConfigurationError('API Secret not found. Aborting '); + } + if (Config.typesOfClient === 'gtag' && !Config.measurementId) { + throw new ConfigurationError('measurementId must be provided. Aborting'); + } + if (Config.typesOfClient === 'firebase' && !Config.firebaseAppId) { + throw new ConfigurationError('firebaseAppId must be provided. Aborting'); + } +}; + module.exports = { + addClientDetails, + basicValidation, + buildDeliverablePayload, + basicConfigvalidaiton, getItem, getItemList, getItemsArray, @@ -463,6 +600,7 @@ module.exports = { getGA4ExclusionList, prepareUserProperties, getGA4CustomParameters, + sanitizeUserProperties, GA4_PARAMETERS_EXCLUSION, isReservedWebCustomEventName, isReservedWebCustomPrefixName, diff --git a/src/v0/destinations/ga4_v2/customMappingsHandler.js b/src/v0/destinations/ga4_v2/customMappingsHandler.js new file mode 100644 index 0000000000..1eb1c2c868 --- /dev/null +++ b/src/v0/destinations/ga4_v2/customMappingsHandler.js @@ -0,0 +1,165 @@ +const get = require('get-value'); +const { + validateEventName, + basicValidation, + isReservedEventName, + addClientDetails, + removeReservedParameterPrefixNames, + prepareUserConsents, + removeInvalidParams, + GA4_RESERVED_PARAMETER_EXCLUSION, + getGA4CustomParameters, + buildDeliverablePayload, + GA4_PARAMETERS_EXCLUSION, + prepareUserProperties, +} = require('../ga4/utils'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { + removeUndefinedAndNullRecurse, + constructPayload, + isDefinedAndNotNull, + isEmptyObject, + removeUndefinedAndNullValues, + isHybridModeEnabled, + getIntegrationsObj, + applyCustomMappings, +} = require('../../util'); +const { trackCommonConfig, ConfigCategory, mappingConfig } = require('../ga4/config'); + +const findGA4Events = (eventsMapping, event) => { + // Find the event using destructuring and early return + + const validMappings = eventsMapping.filter( + (mapping) => + mapping.rsEventName?.trim().toLowerCase() === event.trim().toLowerCase() && + mapping.destEventName, + ); + // Return an empty object if event not found + return validMappings; +}; + +const handleCustomMappings = (message, Config) => { + const { eventsMapping } = Config; + + let rsEvent = ''; + if (message.type.toString().toLowerCase() === 'track') { + rsEvent = get(message, 'event'); + basicValidation(rsEvent); + } else { + const messageType = get(message, 'type'); + if (typeof messageType !== 'string') { + throw new InstrumentationError(`[GA4]:: Message type ${messageType} is not supported`); + } + // for events other than track we will search with $eventType + // example $track / $page + rsEvent = `$${messageType}`; + } + + const validMappings = findGA4Events(eventsMapping, rsEvent); + + if (validMappings.length === 0) { + // trim and replace spaces with "_" + rsEvent = rsEvent.trim().replace(/\s+/g, '_'); + // reserved event names are not allowed + if (isReservedEventName(rsEvent)) { + throw new InstrumentationError(`[GA4]:: Reserved event name: ${rsEvent} are not allowed`); + } + // validation for ga4 event name + validateEventName(rsEvent); + + // Default mapping + + let rawPayload = constructPayload(message, trackCommonConfig); + + const ga4EventPayload = {}; + + // take optional params parameters for custom events + ga4EventPayload.params = { + ...ga4EventPayload.params, + ...constructPayload(message, mappingConfig[ConfigCategory.TrackPageCommonParamsConfig.name]), + }; + + // all extra parameters passed is incorporated inside params + ga4EventPayload.params = getGA4CustomParameters( + message, + ['properties'], + GA4_RESERVED_PARAMETER_EXCLUSION.concat(GA4_PARAMETERS_EXCLUSION), + ga4EventPayload, + ); + + // Prepare GA4 user properties + const userProperties = prepareUserProperties(message, Config.piiPropertiesToIgnore); + if (!isEmptyObject(userProperties)) { + rawPayload.user_properties = userProperties; + } + + rawPayload = removeUndefinedAndNullValues(rawPayload); + rawPayload = { ...rawPayload, events: [ga4EventPayload] }; + + boilerplateOperations(rawPayload, message, Config, rsEvent); + + return buildDeliverablePayload(rawPayload, Config); + } + + const processedPayloads = validMappings.map((mapping) => { + const eventName = mapping.destEventName; + // reserved event names are not allowed + if (isReservedEventName(eventName)) { + throw new InstrumentationError(`[GA4]:: Reserved event name: ${eventName} are not allowed`); + } + // validation for ga4 event name + validateEventName(eventName); + + // Add common top level payload + let ga4BasicPayload = constructPayload(message, trackCommonConfig); + ga4BasicPayload = addClientDetails(ga4BasicPayload, message, Config); + + const eventPropertiesMappings = mapping.eventProperties || []; + + const ga4MappedPayload = applyCustomMappings(message, eventPropertiesMappings); + + removeUndefinedAndNullRecurse(ga4MappedPayload); + + boilerplateOperations(ga4MappedPayload, message, Config, eventName); + + if (isDefinedAndNotNull(ga4BasicPayload)) { + return { ...ga4BasicPayload, ...ga4MappedPayload }; + } else { + return ga4MappedPayload; + } + }); + + return processedPayloads.map((processedPayload) => + buildDeliverablePayload(processedPayload, Config), + ); +}; + +const boilerplateOperations = (ga4Payload, message, Config, eventName) => { + removeReservedParameterPrefixNames(ga4Payload.events[0].params); + ga4Payload.events[0].name = eventName; + const integrationsObj = getIntegrationsObj(message, 'ga4'); + + if (isHybridModeEnabled(Config) && integrationsObj?.sessionId) { + ga4Payload.events[0].params.session_id = integrationsObj.sessionId; + } + + if (ga4Payload.events[0].params) { + ga4Payload.events[0].params = removeInvalidParams( + removeUndefinedAndNullValues(ga4Payload.events[0].params), + ); + } + + if (isEmptyObject(ga4Payload.events[0].params)) { + delete ga4Payload.events[0].params; + } + + // Prepare GA4 consents + const consents = prepareUserConsents(message); + if (!isEmptyObject(consents)) { + ga4Payload.consent = consents; + } +}; + +module.exports = { + handleCustomMappings, +}; diff --git a/src/v0/destinations/ga4_v2/transform.ts b/src/v0/destinations/ga4_v2/transform.ts new file mode 100644 index 0000000000..76adc00e00 --- /dev/null +++ b/src/v0/destinations/ga4_v2/transform.ts @@ -0,0 +1,25 @@ +import { InstrumentationError, RudderStackEvent } from '@rudderstack/integrations-lib'; +import { ProcessorTransformationRequest } from '../../../types'; +import { handleCustomMappings } from './customMappingsHandler'; +import { process as ga4Process } from '../ga4/transform'; +import { basicConfigvalidaiton } from '../ga4/utils'; + +export function process(event: ProcessorTransformationRequest) { + const { message, destination } = event; + const { Config } = destination; + + const eventPayload = message as RudderStackEvent; + + if (!eventPayload.type) { + throw new InstrumentationError('Message Type is not present. Aborting message.'); + } + + if (eventPayload.type !== 'track') { + return ga4Process(event); + } + + basicConfigvalidaiton(Config); + + // custom mappings flow + return handleCustomMappings(message, Config); +} diff --git a/src/v0/util/index.js b/src/v0/util/index.js index ac1bacf404..389b93a7af 100644 --- a/src/v0/util/index.js +++ b/src/v0/util/index.js @@ -24,6 +24,8 @@ const { OAuthSecretError, getErrorRespEvents, } = require('@rudderstack/integrations-lib'); + +const { JsonTemplateEngine, PathType } = require('@rudderstack/json-template-engine'); const logger = require('../../logger'); const stats = require('../../util/stats'); const { DestCanonicalNames, DestHandlerMap } = require('../../constants/destinationCanonicalNames'); @@ -57,6 +59,18 @@ const isNull = (x) => lodash.isNull(x); // GENERIC UTLITY // ======================================================================== +const removeUndefinedAndNullRecurse = (obj) => { + // eslint-disable-next-line no-restricted-syntax + for (const key in obj) { + if (obj[key] === null || obj[key] === undefined) { + // eslint-disable-next-line no-param-reassign + delete obj[key]; + } else if (typeof obj[key] === 'object') { + removeUndefinedAndNullRecurse(obj[key]); + } + } +}; + const getEventTime = (message) => { try { return new Date(message.timestamp).toISOString(); @@ -2234,6 +2248,16 @@ const validateEventAndLowerCaseConversion = (event, isMandatory, convertToLowerC return convertToLowerCase ? event.toString().toLowerCase() : event.toString(); }; +const applyCustomMappings = (message, mappings) => { + const flatMappings = mappings.map((mapping) => ({ + input: mapping.from, + output: mapping.to, + })); + return JsonTemplateEngine.createAsSync(flatMappings, { defaultPathType: PathType.JSON }).evaluate( + message, + ); +}; + // ======================================================================== // EXPORTS // ======================================================================== @@ -2242,6 +2266,7 @@ module.exports = { ErrorMessage, addExternalIdToTraits, adduserIdFromExternalId, + applyCustomMappings, base64Convertor, batchMultiplexedEvents, checkEmptyStringInarray, @@ -2318,6 +2343,7 @@ module.exports = { removeUndefinedNullEmptyExclBoolInt, removeUndefinedNullValuesAndEmptyObjectArray, removeUndefinedValues, + removeUndefinedAndNullRecurse, returnArrayOfSubarrays, stripTrailingSlash, toTitleCase, diff --git a/src/v0/util/mapWithJSONPath.js b/src/v0/util/mapWithJSONPath.js new file mode 100644 index 0000000000..7265eb2c85 --- /dev/null +++ b/src/v0/util/mapWithJSONPath.js @@ -0,0 +1,58 @@ +/* eslint-disable no-plusplus */ +const jsonpath = require('rs-jsonpath'); + +function mapWithJsonPath(message, targetObject, sourcePath, targetPath) { + const values = jsonpath.query(message, sourcePath); + const matchTargetPath = targetPath.split('$.events[0].')[1] || targetPath; + const regexMatch = /\[[^\n\]]*]/; + if (regexMatch.test(sourcePath) && regexMatch.test(matchTargetPath)) { + // both paths are arrays + // eslint-disable-next-line unicorn/no-for-loop + for (let i = 0; i < values.length; i++) { + const targetPathWithIndex = targetPath.replace(/\[\*]/g, `[${i}]`); + const tragetValue = values[i] ? values[i] : null; + jsonpath.value(targetObject, targetPathWithIndex, tragetValue); + } + } else if (!regexMatch.test(sourcePath) && regexMatch.test(matchTargetPath)) { + // source path is not array and target path is + const targetPathArr = targetPath.split('.'); + const holdingArr = []; + // eslint-disable-next-line unicorn/no-for-loop + for (let i = 0; i < targetPathArr.length; i++) { + if (/\[\*]/.test(targetPathArr[i])) { + holdingArr.push(targetPathArr[i]); + break; + } else { + holdingArr.push(targetPathArr[i]); + } + } + const parentTargetPath = holdingArr.join('.'); + const exisitngTargetValues = jsonpath.query(targetObject, parentTargetPath); + if (exisitngTargetValues.length > 0) { + for (let i = 0; i < exisitngTargetValues.length; i++) { + const targetPathWithIndex = targetPath.replace(/\[\*]/g, `[${i}]`); + jsonpath.value(targetObject, targetPathWithIndex, values[0]); + } + } else { + const targetPathWithIndex = targetPath.replace(/\[\*]/g, '[0]'); + jsonpath.value(targetObject, targetPathWithIndex, values[0]); + } + } else if (regexMatch.test(sourcePath)) { + // source path is an array but target path is not + + // filter out null values + const filteredValues = values.filter((value) => value !== null); + if (filteredValues.length > 1) { + jsonpath.value(targetObject, targetPath, filteredValues); + } else { + jsonpath.value(targetObject, targetPath, filteredValues[0]); + } + } else { + // both paths are not arrays + jsonpath.value(targetObject, targetPath, values[0]); + } +} + +module.exports = { + mapWithJsonPath, +}; diff --git a/test/integrations/destinations/ga4/processor/data.ts b/test/integrations/destinations/ga4/processor/data.ts index ba5b53c7d2..fb65787214 100644 --- a/test/integrations/destinations/ga4/processor/data.ts +++ b/test/integrations/destinations/ga4/processor/data.ts @@ -1,13 +1,3 @@ -import { pageTestData } from './pageTestData'; -import { ecommTestData } from './ecomTestData'; -import { trackTestData } from './trackTestData'; -import { groupTestData } from './groupTestData'; -import { validationTestData } from './validationTestData'; +import { existingTests } from './exisitngTests'; -export const data = [ - ...pageTestData, - ...trackTestData, - ...ecommTestData, - ...groupTestData, - ...validationTestData, -]; +export const data = [...existingTests]; diff --git a/test/integrations/destinations/ga4/processor/exisitngTests.ts b/test/integrations/destinations/ga4/processor/exisitngTests.ts new file mode 100644 index 0000000000..2913004ca6 --- /dev/null +++ b/test/integrations/destinations/ga4/processor/exisitngTests.ts @@ -0,0 +1,13 @@ +import { pageTestData } from './pageTestData'; +import { ecommTestData } from './ecomTestData'; +import { trackTestData } from './trackTestData'; +import { groupTestData } from './groupTestData'; +import { validationTestData } from './validationTestData'; + +export const existingTests = [ + ...pageTestData, + ...trackTestData, + ...ecommTestData, + ...groupTestData, + ...validationTestData, +]; diff --git a/test/integrations/destinations/ga4_v2/mocks.ts b/test/integrations/destinations/ga4_v2/mocks.ts new file mode 100644 index 0000000000..3a27349ff7 --- /dev/null +++ b/test/integrations/destinations/ga4_v2/mocks.ts @@ -0,0 +1,5 @@ +export const defaultMockFns = () => { + return jest + .spyOn(Date, 'now') + .mockImplementation(() => new Date('2022-04-29T05:17:09Z').valueOf()); +}; diff --git a/test/integrations/destinations/ga4_v2/processor/customMappings.ts b/test/integrations/destinations/ga4_v2/processor/customMappings.ts new file mode 100644 index 0000000000..b1db2121ea --- /dev/null +++ b/test/integrations/destinations/ga4_v2/processor/customMappings.ts @@ -0,0 +1,721 @@ +import { defaultMockFns } from '../mocks'; + +const traits = { + firstName: 'John', + lastName: 'Gomes', + city: 'London', + state: 'UK', + streetAddress: '71 Cherry Court SOUTHAMPTON SO53 5PD UK', + group: 'test group', +}; + +const device = { + adTrackingEnabled: 'true', + advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', + id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, +}; + +const properties = { + list_id: 'random_list_id', + category: 'random_category', + storePrice: 456, + prices: [ + { + id: 'store-price', + value: 456, + }, + { + id: 'desk-price', + value: 567, + }, + ], + products: [ + { + product_id: 883213, + name: 'Salt', + coupon: 'HHH', + price: 100, + position: 1, + quantity: 10, + affiliation: 'NADA', + currency: 'INR', + discount: '2%', + item_category3: 'grocery', + }, + { + product_id: 213123, + name: 'Sugar', + coupon: 'III', + price: 200, + position: 2, + quantity: 20, + affiliation: 'ADNA', + currency: 'INR', + discount: '5%', + item_category2: 'regulars', + item_category3: 'grocery', + some_data: 'someValue', + }, + ], +}; + +const integrations = { + GA4: { + consents: { + ad_personalization: 'GRANTED', + ad_user_data: 'DENIED', + }, + }, +}; + +const eventsMapping = [ + { + rsEventName: 'Product List Viewed', + destEventName: 'view_item_list', + eventProperties: [ + { + to: '$.client_id', + from: '$.context.traits.anonymousId', + }, + { + to: '$.events[0].params.items[*].name', + from: '$.properties.products[*].name', + }, + { + to: '$.events[0].params.prices', + from: '$.properties.storePrice', + }, + { + to: '$.events[0].params.items[*].id', + from: '$.properties.products[*].product_id', + }, + { + to: '$.events[0].params.items[*].key', + from: '$.properties.products[*].some_data', + }, + { + to: '$.events[0].params.items[*].list_id', + from: '$.properties.list_id', + }, + { + to: '$.userProperties.firstName.value', + from: '$.context.traits.firstName', + }, + { + to: '$.userProperties.lastName.value', + from: '$.context.traits.lastName', + }, + ], + }, + { + rsEventName: 'Product Added', + destEventName: 'add_to_cart', + eventProperties: [ + { + to: '$.client_id', + from: '$.context.traits.anonymousId', + }, + { + to: '$.events[0].params.items[*].name', + from: '$.properties.products[*].name', + }, + { + to: '$.events[0].params.prices', + from: '$.properties.storePrice', + }, + { + to: '$.events[0].params.items[*].id', + from: '$.properties.products[*].product_id', + }, + { + to: '$.events[0].params.items[*].key', + from: '$.properties.products[*].some_data', + }, + { + to: '$.events[0].params.items[*].list_id', + from: '$.properties.list_id', + }, + { + to: '$.userProperties.firstName.value', + from: '$.context.traits.firstName', + }, + { + to: '$.userProperties.lastName.value', + from: '$.context.traits.lastName', + }, + ], + }, + { + rsEventName: 'Product Added', + destEventName: 'checkout_started', + eventProperties: [ + { + to: '$.client_id', + from: '$.context.traits.anonymousId', + }, + { + to: '$.events[0].params.items[*].name', + from: '$.properties.products[*].name', + }, + { + to: '$.events[0].params.prices', + from: '$.properties.storePrice', + }, + { + to: '$.events[0].params.items[*].id', + from: '$.properties.products[*].product_id', + }, + { + to: '$.events[0].params.items[*].key', + from: '$.properties.products[*].some_data', + }, + { + to: '$.events[0].params.items[*].list_id', + from: '$.properties.list_id', + }, + { + to: '$.userProperties.firstName.value', + from: '$.context.traits.firstName', + }, + { + to: '$.userProperties.lastName.value', + from: '$.context.traits.lastName', + }, + ], + }, + { + rsEventName: '$group', + destEventName: 'join_group', + eventProperties: [ + { + to: '$.client_id', + from: '$.context.traits.anonymousId', + }, + { + to: '$.events[0].params.group_id', + from: '$.context.traits.group_id', + }, + { + to: '$.userProperties.firstName.value', + from: '$.context.traits.firstName', + }, + { + to: '$.userProperties.lastName.value', + from: '$.context.traits.lastName', + }, + ], + }, +]; + +const destination = { + Config: { + apiSecret: 'dummyApiSecret', + measurementId: 'G-T40PE6KET4', + firebaseAppId: '', + blockPageViewEvent: false, + typesOfClient: 'gtag', + extendPageViewParams: false, + sendUserId: false, + eventFilteringOption: 'disable', + eventsMapping, + }, +}; +export const customMappingTestCases = [ + { + name: 'ga4_v2', + id: 'ga4_custom_mapping_test_0', + description: 'Custom Mapping Test 0', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Product List Viewed', + userId: 'root_user', + anonymousId: 'root_anonId', + context: { + device, + traits, + }, + properties, + originalTimestamp: '2022-04-28T00:23:09.544Z', + integrations, + }, + destination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.google-analytics.com/mp/collect', + headers: { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', + }, + params: { + api_secret: 'dummyApiSecret', + measurement_id: 'G-T40PE6KET4', + }, + body: { + JSON: { + user_id: 'root_user', + timestamp_micros: 1651105389000000, + non_personalized_ads: false, + client_id: 'root_anonId', + events: [ + { + name: 'view_item_list', + params: { + items: [ + { + name: 'Salt', + id: 883213, + list_id: 'random_list_id', + }, + { + id: 213123, + key: 'someValue', + list_id: 'random_list_id', + name: 'Sugar', + }, + ], + prices: 456, + }, + }, + ], + userProperties: { + firstName: { + value: 'John', + }, + lastName: { + value: 'Gomes', + }, + }, + consent: { + ad_user_data: 'DENIED', + ad_personalization: 'GRANTED', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + name: 'ga4_v2', + id: 'ga4_custom_mapping_test_1', + description: 'Custom Mapping Test for multiplexing', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Product Added', + userId: 'root_user', + anonymousId: 'root_anonId', + context: { + device, + traits, + }, + properties, + originalTimestamp: '2022-04-28T00:23:09.544Z', + integrations, + }, + destination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.google-analytics.com/mp/collect', + headers: { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', + }, + params: { + api_secret: 'dummyApiSecret', + measurement_id: 'G-T40PE6KET4', + }, + body: { + JSON: { + user_id: 'root_user', + timestamp_micros: 1651105389000000, + non_personalized_ads: false, + client_id: 'root_anonId', + events: [ + { + name: 'add_to_cart', + params: { + items: [ + { + name: 'Salt', + id: 883213, + list_id: 'random_list_id', + }, + { + name: 'Sugar', + id: 213123, + key: 'someValue', + list_id: 'random_list_id', + }, + ], + prices: 456, + }, + }, + ], + userProperties: { + firstName: { + value: 'John', + }, + lastName: { + value: 'Gomes', + }, + }, + consent: { + ad_user_data: 'DENIED', + ad_personalization: 'GRANTED', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.google-analytics.com/mp/collect', + headers: { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', + }, + params: { + api_secret: 'dummyApiSecret', + measurement_id: 'G-T40PE6KET4', + }, + body: { + JSON: { + user_id: 'root_user', + timestamp_micros: 1651105389000000, + non_personalized_ads: false, + client_id: 'root_anonId', + events: [ + { + name: 'checkout_started', + params: { + items: [ + { + name: 'Salt', + id: 883213, + list_id: 'random_list_id', + }, + { + name: 'Sugar', + id: 213123, + key: 'someValue', + list_id: 'random_list_id', + }, + ], + prices: 456, + }, + }, + ], + userProperties: { + firstName: { + value: 'John', + }, + lastName: { + value: 'Gomes', + }, + }, + consent: { + ad_user_data: 'DENIED', + ad_personalization: 'GRANTED', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + name: 'ga4_v2', + id: 'ga4_custom_mapping_test_2', + description: 'Custom Mapping Test For mapping not present in events mapping', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Product Viewed', + userId: 'root_user', + anonymousId: 'root_anonId', + context: { + device, + traits, + }, + properties, + originalTimestamp: '2022-04-28T00:23:09.544Z', + integrations, + }, + destination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.google-analytics.com/mp/collect', + headers: { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', + }, + params: { + api_secret: 'dummyApiSecret', + measurement_id: 'G-T40PE6KET4', + }, + body: { + JSON: { + user_id: 'root_user', + timestamp_micros: 1651105389000000, + non_personalized_ads: false, + user_properties: { + firstName: { + value: 'John', + }, + lastName: { + value: 'Gomes', + }, + city: { + value: 'London', + }, + state: { + value: 'UK', + }, + group: { + value: 'test group', + }, + }, + events: [ + { + name: 'Product_Viewed', + params: { + engagement_time_msec: 1, + list_id: 'random_list_id', + category: 'random_category', + storePrice: 456, + prices_0_id: 'store-price', + prices_0_value: 456, + prices_1_id: 'desk-price', + prices_1_value: 567, + products_0_product_id: 883213, + products_0_name: 'Salt', + products_0_coupon: 'HHH', + products_0_price: 100, + products_0_position: 1, + products_0_quantity: 10, + products_0_affiliation: 'NADA', + products_0_currency: 'INR', + products_0_discount: '2%', + products_0_item_category3: 'grocery', + products_1_product_id: 213123, + products_1_name: 'Sugar', + products_1_coupon: 'III', + products_1_price: 200, + products_1_position: 2, + products_1_quantity: 20, + products_1_affiliation: 'ADNA', + products_1_currency: 'INR', + products_1_discount: '5%', + products_1_item_category2: 'regulars', + products_1_item_category3: 'grocery', + products_1_some_data: 'someValue', + }, + }, + ], + consent: { + ad_user_data: 'DENIED', + ad_personalization: 'GRANTED', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + name: 'ga4_v2', + id: 'ga4_custom_mapping_test_3', + description: 'Custom Mapping Test For Group Event Type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'group', + userId: 'root_user', + anonymousId: 'root_anonId', + context: { + device, + traits, + }, + properties, + originalTimestamp: '2022-04-28T00:23:09.544Z', + integrations, + }, + destination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.google-analytics.com/mp/collect', + headers: { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', + }, + params: { + api_secret: 'dummyApiSecret', + measurement_id: 'G-T40PE6KET4', + }, + body: { + JSON: { + user_id: 'root_user', + timestamp_micros: 1651105389000000, + non_personalized_ads: false, + client_id: 'root_anonId', + events: [ + { + name: 'join_group', + params: { + city: 'London', + engagement_time_msec: 1, + firstName: 'John', + group: 'test group', + lastName: 'Gomes', + state: 'UK', + streetAddress: '71 Cherry Court SOUTHAMPTON SO53 5PD UK', + }, + }, + ], + user_properties: { + firstName: { + value: 'John', + }, + lastName: { + value: 'Gomes', + }, + city: { + value: 'London', + }, + state: { + value: 'UK', + }, + group: { + value: 'test group', + }, + }, + consent: { + ad_user_data: 'DENIED', + ad_personalization: 'GRANTED', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, +]; diff --git a/test/integrations/destinations/ga4_v2/processor/data.ts b/test/integrations/destinations/ga4_v2/processor/data.ts new file mode 100644 index 0000000000..ba82792f31 --- /dev/null +++ b/test/integrations/destinations/ga4_v2/processor/data.ts @@ -0,0 +1,3 @@ +import { customMappingTestCases } from './customMappings'; + +export const data = [...customMappingTestCases]; From 546a9379eba6d44e5ed2ed4451e14229dbce82fd Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Wed, 12 Jun 2024 10:57:35 +0530 Subject: [PATCH 206/240] chore: added check for multiple arguments --- src/util/ivmFactory.js | 2 +- test/__tests__/user_transformation.test.js | 29 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/util/ivmFactory.js b/src/util/ivmFactory.js index 9e1207f844..50bf4caa49 100644 --- a/src/util/ivmFactory.js +++ b/src/util/ivmFactory.js @@ -247,7 +247,7 @@ async function createIvm(code, libraryVersionIds, versionId, credentials, secret if (_.isNil(credentials) || !_.isObject(credentials)) { throw new Error('Credentials in incorrect format'); } - if (_.isNil(key[0]) || !_.isString(key[0])) { + if (key.length > 1 || _.isNil(key[0]) || !_.isString(key[0])) { throw new Error('Key should be a string'); } return credentials[key]; diff --git a/test/__tests__/user_transformation.test.js b/test/__tests__/user_transformation.test.js index 31a07b6716..710b21e6d6 100644 --- a/test/__tests__/user_transformation.test.js +++ b/test/__tests__/user_transformation.test.js @@ -1174,6 +1174,35 @@ describe("User transformation", () => { expect(output[0].error).toMatch(/Key should be a string/); }); + it(`Simple ${name} Test with credentials with multiple arguements for codeVersion 1`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformEvent(event, metadata) { + event.credentialValue = credential('arg1', 'arg2'); + return event; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].error).toMatch(/Key should be a string/); + }); + it(`Simple ${name} Test with credentials with non string key for codeVersion 1`, async () => { const versionId = randomID(); From bb94c81c90610e2d27413a054834137e87cf7161 Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Wed, 12 Jun 2024 12:23:09 +0530 Subject: [PATCH 207/240] chore: change in lodash import --- src/util/customTransformer.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/util/customTransformer.js b/src/util/customTransformer.js index fcc71b82a3..3b3a3539f3 100644 --- a/src/util/customTransformer.js +++ b/src/util/customTransformer.js @@ -8,8 +8,7 @@ const { parserForImport } = require('./parser'); const stats = require('./stats'); const { fetchWithDnsWrapper } = require('./utils'); const { getMetadata, getTransformationMetadata } = require('../v0/util'); -const _ = require('lodash'); - +const { isNil } = require('lodash'); const ISOLATE_VM_MEMORY = parseInt(process.env.ISOLATE_VM_MEMORY || '128', 10); const GEOLOCATION_TIMEOUT_IN_MS = parseInt(process.env.GEOLOCATION_TIMEOUT_IN_MS || '1000', 10); @@ -298,7 +297,7 @@ async function userTransformHandler( credentialsMap[cred.key] = cred.value; }); events.forEach((ev) => { - if (_.isNil(ev?.credentials)) { + if (isNil(ev?.credentials)) { ev.credentials = credentials; } }); From 6a99c8971e954327ef74bcb5bf3fc1e9b9d055d6 Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Thu, 13 Jun 2024 17:47:35 +0530 Subject: [PATCH 208/240] chore: upgrade json template packages --- package-lock.json | 32 ++++++++++++++++---------------- package.json | 4 ++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6a35944fb4..04ee1c5a38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,8 +20,8 @@ "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.8", - "@rudderstack/json-template-engine": "^0.11.0", - "@rudderstack/workflow-engine": "^0.8.0", + "@rudderstack/json-template-engine": "^0.13.0", + "@rudderstack/workflow-engine": "^0.8.1", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", @@ -4482,17 +4482,17 @@ } }, "node_modules/@rudderstack/json-template-engine": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.11.0.tgz", - "integrity": "sha512-9XrzY7W9mL2lYro2NOSInuDElW7Qk0nP61UbrfJiTQfrzbyaH7ml663eD07a/4ia3uQynITPsSIGHpMgP3qlEw==" + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.13.0.tgz", + "integrity": "sha512-nROhLP8Q8wYdeb7N1qpJvkJRI1b7J9jUFxCcBqeN8C/j1kHTAHgomilQjOnGFb1H+OP6IxI93b+JJ24mIDHXnw==" }, "node_modules/@rudderstack/workflow-engine": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.8.0.tgz", - "integrity": "sha512-oBRucBNR29E2PzwHX3hANT0c6V0yFKNMWxDg0jr8Hin4co6KZjxi4FdpkzTNWvk2h+8iXT8NCSPdJBjt03hTrw==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.8.1.tgz", + "integrity": "sha512-6oBG6lLljwci8C9MFqoKyJf+MTwsOyUMWg9yQD5/49nbNdTvazXQ7zpvjzEGgTxe7Ub1ppoDe5eSqZYSoWIAvg==", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", - "@rudderstack/json-template-engine": "^0.11.0", + "@rudderstack/json-template-engine": "^0.13.0", "jsonata": "^2.0.5", "lodash": "^4.17.21", "object-sizeof": "^2.6.4", @@ -6559,11 +6559,11 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -10391,9 +10391,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dependencies": { "to-regex-range": "^5.0.1" }, diff --git a/package.json b/package.json index 2acfed56ff..12ea084bf4 100644 --- a/package.json +++ b/package.json @@ -65,8 +65,8 @@ "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.8", - "@rudderstack/json-template-engine": "^0.11.0", - "@rudderstack/workflow-engine": "^0.8.0", + "@rudderstack/json-template-engine": "^0.13.0", + "@rudderstack/workflow-engine": "^0.8.1", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", From 3aee494b3902f6e598bc9fb8d1f165b637cfcf02 Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Fri, 14 Jun 2024 10:39:28 +0530 Subject: [PATCH 209/240] chore: add verify server start workflow --- .github/workflows/verify-server-start.yml | 34 +++++++++++++++++++++++ package-lock.json | 18 ++++++------ package.json | 4 +-- 3 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/verify-server-start.yml diff --git a/.github/workflows/verify-server-start.yml b/.github/workflows/verify-server-start.yml new file mode 100644 index 0000000000..6ac5b7be05 --- /dev/null +++ b/.github/workflows/verify-server-start.yml @@ -0,0 +1,34 @@ +name: Verify Server start + +on: + pull_request: + types: ['opened', 'reopened', 'synchronize'] + +jobs: + check-health: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4.1.1 + with: + fetch-depth: 1 + + - name: Setup Node + uses: actions/setup-node@v4.0.2 + with: + node-version-file: '.nvmrc' + cache: 'npm' + + - name: Install Dependencies + run: npm ci + + - name: Start server + run: npm run build:start & + + - name: Wait for server to start + run: sleep 10 # Adjust the time as necessary for your server to start + + - name: Check server health + run: | + curl --fail http://localhost:9090/health || exit 1 diff --git a/package-lock.json b/package-lock.json index 04ee1c5a38..3166828d24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,8 +20,8 @@ "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.8", - "@rudderstack/json-template-engine": "^0.13.0", - "@rudderstack/workflow-engine": "^0.8.1", + "@rudderstack/json-template-engine": "^0.13.2", + "@rudderstack/workflow-engine": "^0.8.2", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", @@ -4482,17 +4482,17 @@ } }, "node_modules/@rudderstack/json-template-engine": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.13.0.tgz", - "integrity": "sha512-nROhLP8Q8wYdeb7N1qpJvkJRI1b7J9jUFxCcBqeN8C/j1kHTAHgomilQjOnGFb1H+OP6IxI93b+JJ24mIDHXnw==" + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.13.2.tgz", + "integrity": "sha512-uEyMv/qjm/mP5V8EifJzolvFLtka/dacmvwo9Xk3+MnEbsNN0YLu7Z/qWeyXeDF5chvy8JfaqV8lNgO3SxVG7g==" }, "node_modules/@rudderstack/workflow-engine": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.8.1.tgz", - "integrity": "sha512-6oBG6lLljwci8C9MFqoKyJf+MTwsOyUMWg9yQD5/49nbNdTvazXQ7zpvjzEGgTxe7Ub1ppoDe5eSqZYSoWIAvg==", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.8.2.tgz", + "integrity": "sha512-cjn3J8CUarAE3cbASRvkf7A2745Clzkw/ffqGLzD8+9KvTN6mC28Pm9c5169LPDmt+NMUMw0W5xHgNO3cV9eqQ==", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", - "@rudderstack/json-template-engine": "^0.13.0", + "@rudderstack/json-template-engine": "^0.13.2", "jsonata": "^2.0.5", "lodash": "^4.17.21", "object-sizeof": "^2.6.4", diff --git a/package.json b/package.json index 12ea084bf4..855c7ae3a3 100644 --- a/package.json +++ b/package.json @@ -65,8 +65,8 @@ "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.8", - "@rudderstack/json-template-engine": "^0.13.0", - "@rudderstack/workflow-engine": "^0.8.1", + "@rudderstack/json-template-engine": "^0.13.2", + "@rudderstack/workflow-engine": "^0.8.2", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", From d4d5a89951414f02d02101a6582b6e9fa8f9a7a5 Mon Sep 17 00:00:00 2001 From: Paul K <59660521+zenpaul@users.noreply.github.com> Date: Fri, 14 Jun 2024 03:21:24 -0500 Subject: [PATCH 210/240] feat(integrations/auth0): add Auth0 event type to event context (#3433) * feat(integrations/auth0): add Auth0 event type to event context * feat(integrations/auth0): move Auth0 event type to event properties * feat(integrations): better --- src/v0/sources/auth0/mapping.json | 2 +- test/integrations/sources/auth0/data.ts | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/v0/sources/auth0/mapping.json b/src/v0/sources/auth0/mapping.json index bc5869a19b..dcbd389945 100644 --- a/src/v0/sources/auth0/mapping.json +++ b/src/v0/sources/auth0/mapping.json @@ -65,6 +65,6 @@ }, { "sourceKeys": "type", - "destKeys": "source_type" + "destKeys": "properties.source_type" } ] diff --git a/test/integrations/sources/auth0/data.ts b/test/integrations/sources/auth0/data.ts index b115f0e05e..44b511cad2 100644 --- a/test/integrations/sources/auth0/data.ts +++ b/test/integrations/sources/auth0/data.ts @@ -254,7 +254,6 @@ export const data = [ batch: [ { type: 'identify', - source_type: 'ss', sentAt: '2022-10-31T05:57:06.859Z', traits: { connection: 'Username-Password-Authentication', @@ -291,6 +290,7 @@ export const data = [ client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', client_name: 'All Applications', description: '', + source_type: 'ss', }, integrations: { Auth0: false, @@ -305,7 +305,6 @@ export const data = [ batch: [ { type: 'track', - source_type: 'sapi', event: 'Success API Operation', sentAt: '2022-10-31T05:57:06.874Z', userId: 'auth0|dummyPassword', @@ -511,6 +510,7 @@ export const data = [ client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', client_name: '', description: 'Create a User', + source_type: 'sapi', }, integrations: { Auth0: false, @@ -596,7 +596,6 @@ export const data = [ batch: [ { type: 'group', - source_type: 'sapi', sentAt: '2022-10-31T06:09:59.135Z', userId: 'google-oauth2|123456', anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', @@ -650,6 +649,7 @@ export const data = [ client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', client_name: '', description: 'Add members to an organization', + source_type: 'sapi', }, integrations: { Auth0: false, @@ -939,7 +939,6 @@ export const data = [ batch: [ { type: 'track', - source_type: 'sapi', event: 'Success API Operation', sentAt: '2022-10-31T06:15:25.201Z', userId: 'google-oauth2|123456', @@ -1147,6 +1146,7 @@ export const data = [ client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', client_name: '', description: 'Update tenant settings', + source_type: 'sapi', }, integrations: { Auth0: false, @@ -1161,7 +1161,6 @@ export const data = [ batch: [ { type: 'track', - source_type: 'gd_tenant_update', event: 'Guardian tenant update', sentAt: '2022-10-31T06:15:25.196Z', userId: 'google-oauth2|123456', @@ -1221,6 +1220,7 @@ export const data = [ }, }, description: 'Guardian - Updates tenant settings', + source_type: 'gd_tenant_update', }, integrations: { Auth0: false, @@ -1290,7 +1290,6 @@ export const data = [ batch: [ { type: 'identify', - source_type: 'ss', sentAt: '2022-10-31T05:57:06.859Z', traits: { connection: 'Username-Password-Authentication', @@ -1327,6 +1326,7 @@ export const data = [ client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', client_name: 'All Applications', description: '', + source_type: 'ss', }, integrations: { Auth0: false, @@ -1410,7 +1410,6 @@ export const data = [ batch: [ { type: 'identify', - source_type: 'ss', userId: '', anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', sentAt: '2022-10-31T05:57:06.859Z', @@ -1447,6 +1446,7 @@ export const data = [ client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', client_name: 'All Applications', description: '', + source_type: 'ss', }, integrations: { Auth0: false, @@ -1461,7 +1461,6 @@ export const data = [ batch: [ { type: 'track', - source_type: 'sapi', event: 'Success API Operation', sentAt: '2022-10-31T05:57:06.874Z', anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', @@ -1482,6 +1481,7 @@ export const data = [ client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', client_name: '', description: 'Create a User', + source_type: 'sapi', }, integrations: { Auth0: false, From 04d078310d4b102588f35cf9eb2bc34bf18b23ca Mon Sep 17 00:00:00 2001 From: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:11:36 +0530 Subject: [PATCH 211/240] feat: cm360 enhanced conversions (#3414) * feat: cm360 enhanced conversions * refactor: added null, empty checks --- package-lock.json | 6 + package.json | 1 + .../destinations/campaign_manager/config.js | 4 + ...mpaignManagerEnhancedConversionConfig.json | 59 ++ .../campaign_manager/transform.js | 15 +- src/v0/destinations/campaign_manager/util.js | 106 ++++ .../campaign_manager/util.test.js | 36 +- src/v0/util/data/GenericFieldMapping.json | 8 +- .../campaign_manager/processor/data.ts | 513 ++++++++++++++++++ 9 files changed, 744 insertions(+), 4 deletions(-) create mode 100644 src/v0/destinations/campaign_manager/data/CampaignManagerEnhancedConversionConfig.json diff --git a/package-lock.json b/package-lock.json index 3166828d24..1e6b2c7341 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "koa": "^2.14.1", "koa-bodyparser": "^4.4.0", "koa2-swagger-ui": "^5.7.0", + "libphonenumber-js": "^1.11.1", "lodash": "^4.17.21", "match-json": "^1.3.5", "md5": "^2.3.0", @@ -14839,6 +14840,11 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.1.tgz", + "integrity": "sha512-Wze1LPwcnzvcKGcRHFGFECTaLzxOtujwpf924difr5zniyYv1C2PiW0419qDR7m8lKDxsImu5mwxFuXhXpjmvw==" + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", diff --git a/package.json b/package.json index 855c7ae3a3..304ed36d13 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "koa": "^2.14.1", "koa-bodyparser": "^4.4.0", "koa2-swagger-ui": "^5.7.0", + "libphonenumber-js": "^1.11.1", "lodash": "^4.17.21", "match-json": "^1.3.5", "md5": "^2.3.0", diff --git a/src/v0/destinations/campaign_manager/config.js b/src/v0/destinations/campaign_manager/config.js index b3a9531347..5ea1972a84 100644 --- a/src/v0/destinations/campaign_manager/config.js +++ b/src/v0/destinations/campaign_manager/config.js @@ -7,6 +7,10 @@ const ConfigCategories = { type: 'track', name: 'CampaignManagerTrackConfig', }, + ENHANCED_CONVERSION: { + type: 'track', + name: 'CampaignManagerEnhancedConversionConfig', + }, }; const MAX_BATCH_CONVERSATIONS_SIZE = 1000; diff --git a/src/v0/destinations/campaign_manager/data/CampaignManagerEnhancedConversionConfig.json b/src/v0/destinations/campaign_manager/data/CampaignManagerEnhancedConversionConfig.json new file mode 100644 index 0000000000..0bce0019dd --- /dev/null +++ b/src/v0/destinations/campaign_manager/data/CampaignManagerEnhancedConversionConfig.json @@ -0,0 +1,59 @@ +[ + { + "destKey": "hashedEmail", + "sourceKeys": "emailOnly", + "sourceFromGenericMap": true + }, + { + "destKey": "hashedPhoneNumber", + "sourceKeys": "phone", + "sourceFromGenericMap": true + }, + { + "destKey": "addressInfo.hashedFirstName", + "sourceKeys": "firstName", + "sourceFromGenericMap": true + }, + { + "destKey": "addressInfo.hashedLastName", + "sourceKeys": "lastName", + "sourceFromGenericMap": true + }, + { + "destKey": "addressInfo.hashedStreetAddress", + "sourceKeys": "street", + "sourceFromGenericMap": true + }, + { + "destKey": "addressInfo.city", + "sourceKeys": [ + "traits.city", + "traits.address.city", + "context.traits.city", + "context.traits.address.city" + ] + }, + { + "destKey": "addressInfo.state", + "sourceKeys": [ + "traits.state", + "traits.address.state", + "context.traits.state", + "context.traits.address.state" + ] + }, + { + "destKey": "addressInfo.countryCode", + "sourceKeys": [ + "traits.country", + "traits.address.country", + "context.traits.country", + "context.traits.address.country" + ] + }, + { + "destKey": "addressInfo.postalCode", + "sourceKeys": "zipcode", + "sourceFromGenericMap": true + } +] diff --git a/src/v0/destinations/campaign_manager/transform.js b/src/v0/destinations/campaign_manager/transform.js index 403a79a971..ef208df5d1 100644 --- a/src/v0/destinations/campaign_manager/transform.js +++ b/src/v0/destinations/campaign_manager/transform.js @@ -12,6 +12,7 @@ const { handleRtTfSingleEventError, getAccessToken, } = require('../../util'); +const { CommonUtils } = require('../../../util/common'); const { ConfigCategories, @@ -22,7 +23,7 @@ const { MAX_BATCH_CONVERSATIONS_SIZE, } = require('./config'); -const { convertToMicroseconds } = require('./util'); +const { convertToMicroseconds, prepareUserIdentifiers } = require('./util'); const { JSON_MIME_TYPE } = require('../../util/constant'); function isEmptyObject(obj) { @@ -105,6 +106,18 @@ function processTrack(message, metadata, destination) { } } + if ( + destination.Config.enableEnhancedConversions && + message.properties.requestType === 'batchupdate' + ) { + const userIdentifiers = CommonUtils.toArray( + prepareUserIdentifiers(message, destination.Config.isHashingRequired ?? true), + ); + if (userIdentifiers.length > 0) { + requestJson.userIdentifiers = userIdentifiers; + } + } + const endpointUrl = prepareUrl(message, destination); return buildResponse( requestJson, diff --git a/src/v0/destinations/campaign_manager/util.js b/src/v0/destinations/campaign_manager/util.js index 434322440f..fee4c8188d 100644 --- a/src/v0/destinations/campaign_manager/util.js +++ b/src/v0/destinations/campaign_manager/util.js @@ -1,3 +1,14 @@ +const { parsePhoneNumber } = require('libphonenumber-js'); +const sha256 = require('sha256'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { + constructPayload, + isDefinedAndNotNull, + removeUndefinedAndNullValues, + isEmptyObject, +} = require('../../util'); +const { ConfigCategories, mappingConfig } = require('./config'); + function convertToMicroseconds(input) { const timestamp = Date.parse(input); @@ -28,6 +39,101 @@ function convertToMicroseconds(input) { return timestamp; } +const normalizeEmail = (email) => { + const domains = ['@gmail.com', '@googlemail.com']; + + const matchingDomain = domains.find((domain) => email.endsWith(domain)); + + if (matchingDomain) { + const localPart = email.split('@')[0].replace(/\./g, ''); + return `${localPart}${matchingDomain}`; + } + + return email; +}; + +const normalizePhone = (phone, countryCode) => { + const phoneNumberObject = parsePhoneNumber(phone, countryCode); + if (phoneNumberObject && phoneNumberObject.isValid()) { + return phoneNumberObject.format('E.164'); + } + throw new InstrumentationError('Invalid phone number'); +}; + +// ref:- https://developers.google.com/doubleclick-advertisers/guides/conversions_ec#hashing +const normalizeAndHash = (key, value, options) => { + if (!isDefinedAndNotNull(value)) return value; + + let normalizedValue; + const trimmedValue = value.trim().toLowerCase(); + switch (key) { + case 'hashedEmail': + normalizedValue = normalizeEmail(trimmedValue); + break; + case 'hashedPhoneNumber': + normalizedValue = normalizePhone(trimmedValue, options.countryCode); + break; + case 'hashedFirstName': + case 'hashedLastName': + case 'hashedStreetAddress': + normalizedValue = trimmedValue; + break; + default: + return value; + } + return sha256(normalizedValue); +}; + +const prepareUserIdentifiers = (message, isHashingRequired) => { + const payload = constructPayload( + message, + mappingConfig[ConfigCategories.ENHANCED_CONVERSION.name], + ); + + if (isHashingRequired) { + payload.hashedEmail = normalizeAndHash('hashedEmail', payload.hashedEmail); + payload.hashedPhoneNumber = normalizeAndHash('hashedPhoneNumber', payload.hashedPhoneNumber, { + options: payload.addressInfo?.countryCode, + }); + + if (!isEmptyObject(payload.addressInfo)) { + payload.addressInfo.hashedFirstName = normalizeAndHash( + 'hashedFirstName', + payload.addressInfo.hashedFirstName, + ); + + payload.addressInfo.hashedLastName = normalizeAndHash( + 'hashedLastName', + payload.addressInfo.hashedLastName, + ); + + payload.addressInfo.hashedStreetAddress = normalizeAndHash( + 'hashedStreetAddress', + payload.addressInfo.hashedStreetAddress, + ); + } + } + + const userIdentifiers = []; + if (isDefinedAndNotNull(payload.hashedEmail)) { + userIdentifiers.push({ hashedEmail: payload.hashedEmail }); + } + if (isDefinedAndNotNull(payload.hashedPhoneNumber)) { + userIdentifiers.push({ hashedPhoneNumber: payload.hashedPhoneNumber }); + } + + payload.addressInfo = removeUndefinedAndNullValues(payload.addressInfo); + if (!isEmptyObject(payload.addressInfo)) { + userIdentifiers.push({ addressInfo: payload.addressInfo }); + } + + return userIdentifiers; +}; + module.exports = { convertToMicroseconds, + normalizeEmail, + normalizePhone, + normalizeAndHash, + prepareUserIdentifiers, }; diff --git a/src/v0/destinations/campaign_manager/util.test.js b/src/v0/destinations/campaign_manager/util.test.js index 8f69b57a9f..bda63f5806 100644 --- a/src/v0/destinations/campaign_manager/util.test.js +++ b/src/v0/destinations/campaign_manager/util.test.js @@ -1,4 +1,10 @@ -const { convertToMicroseconds } = require('./util'); +const sha256 = require('sha256'); +const { + convertToMicroseconds, + normalizeEmail, + normalizePhone, + normalizeAndHash, +} = require('./util'); describe('convertToMicroseconds utility test', () => { it('ISO 8601 input', () => { @@ -21,3 +27,31 @@ describe('convertToMicroseconds utility test', () => { expect(convertToMicroseconds('1697013935000')).toEqual(1697013935000000); }); }); + +describe('normalizeEmail', () => { + it('should remove dots from the local part for gmail.com addresses', () => { + const email = 'example.user@gmail.com'; + const normalized = normalizeEmail(email); + expect(normalized).toBe('exampleuser@gmail.com'); + }); + + it('should return the same email if no google domain is present', () => { + const email = 'exampleuser@exampl.com'; + const normalized = normalizeEmail(email); + expect(normalized).toBe('exampleuser@exampl.com'); + }); +}); + +describe('normalizePhone', () => { + it('should return a valid E.164 formatted phone number when provided with correct inputs', () => { + const validPhone = '4155552671'; + const countryCode = 'US'; + expect(normalizePhone(validPhone, countryCode)).toBe('+14155552671'); + }); + + it('should throw an InstrumentationError when the phone number is too short or too long', () => { + const invalidPhone = '123'; + const countryCode = 'US'; + expect(() => normalizePhone(invalidPhone, countryCode)).toThrow('Invalid phone number'); + }); +}); diff --git a/src/v0/util/data/GenericFieldMapping.json b/src/v0/util/data/GenericFieldMapping.json index 0a7b309d89..b903e6587d 100644 --- a/src/v0/util/data/GenericFieldMapping.json +++ b/src/v0/util/data/GenericFieldMapping.json @@ -72,12 +72,16 @@ "traits.DOB", "context.traits.DOB" ], - "state": ["traits.state", "context.traits.state"], "country": ["traits.country", "context.traits.country"], "region": ["traits.region", "context.traits.region"], "city": ["traits.address.city", "context.traits.address.city"], - + "street": [ + "traits.street", + "traits.address.street", + "context.traits.street", + "context.traits.address.street" + ], "avatar": [ "traits.avatar", "context.traits.avatar", diff --git a/test/integrations/destinations/campaign_manager/processor/data.ts b/test/integrations/destinations/campaign_manager/processor/data.ts index beff44c928..9aa41691c6 100644 --- a/test/integrations/destinations/campaign_manager/processor/data.ts +++ b/test/integrations/destinations/campaign_manager/processor/data.ts @@ -827,4 +827,517 @@ export const data = [ }, }, }, + { + name: 'campaign_manager', + description: 'Test 6: Enhanced Conversions with un-hashed data in request payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + device: { + id: '0572f78fa49c648e', + name: 'generic_x86_arm', + type: 'Android', + model: 'AOSP on IA Emulator', + manufacturer: 'Google', + adTrackingEnabled: true, + advertisingId: '44c97318-9040-4361-8bc7-4eb30f665ca8', + }, + traits: { + email: 'alex@example.com', + phone: '+1-202-555-0146', + firstName: 'John', + lastName: 'Gomes', + city: 'London', + state: 'England', + country: 'GB', + postalCode: 'EC3M', + street: '71 Cherry Court SOUTHAMPTON SO53 5PD UK', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + originalTimestamp: '2022-11-17T00:22:02.903+05:30', + properties: { + profileId: '34245', + floodlightConfigurationId: '213123123', + ordinal: 'string', + floodlightActivityId: '456543345245', + value: '756', + encryptedUserIdCandidates: ['dfghjbnm'], + quantity: '455678', + encryptionSource: 'AD_SERVING', + encryptionEntityId: '3564523', + encryptionEntityType: 'DCM_ACCOUNT', + requestType: 'batchupdate', + }, + type: 'track', + event: 'event test', + anonymousId: 'randomId', + integrations: { + All: true, + }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + }, + metadata: { + secret: { + access_token: 'dummyApiToken', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + destination: { + Config: { + profileId: '5343234', + treatmentForUnderage: false, + limitAdTracking: false, + childDirectedTreatment: false, + nonPersonalizedAd: false, + rudderAccountId: '2EOknn1JNH7WK1MfNku4fGYKkRK', + enableEnhancedConversions: true, + isHashingRequired: true, + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://dfareporting.googleapis.com/dfareporting/v4/userprofiles/34245/conversions/batchupdate', + headers: { + Authorization: 'Bearer dummyApiToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + kind: 'dfareporting#conversionsBatchUpdateRequest', + encryptionInfo: { + encryptionEntityType: 'DCM_ACCOUNT', + encryptionSource: 'AD_SERVING', + encryptionEntityId: '3564523', + kind: 'dfareporting#encryptionInfo', + }, + conversions: [ + { + floodlightConfigurationId: '213123123', + ordinal: 'string', + timestampMicros: '1668624722903000', + floodlightActivityId: '456543345245', + quantity: '455678', + value: 756, + encryptedUserIdCandidates: ['dfghjbnm'], + nonPersonalizedAd: false, + treatmentForUnderage: false, + userIdentifiers: [ + { + hashedEmail: + '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', + }, + { + hashedPhoneNumber: + 'ec7e6b85f24fa6b796f1017236463f1b7160fbdc5e663e39ab363b6d6fe30b9f', + }, + { + addressInfo: { + hashedFirstName: + '96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a', + hashedLastName: + '12918b23d69d698324a78a8ab8f5060fdb25537ea9620f956d39adca151c3ef9', + hashedStreetAddress: + '5c100d86e9f40bb62a85ca821ff93d96aff6b0dc4c792794c4a4d51ec9246eff', + city: 'London', + state: 'England', + postalCode: 'EC3M', + countryCode: 'GB', + }, + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: { + secret: { + access_token: 'dummyApiToken', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'campaign_manager', + description: 'Test 7: Enhanced Conversions with hashed data in request payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + device: { + id: '0572f78fa49c648e', + name: 'generic_x86_arm', + type: 'Android', + model: 'AOSP on IA Emulator', + manufacturer: 'Google', + adTrackingEnabled: true, + advertisingId: '44c97318-9040-4361-8bc7-4eb30f665ca8', + }, + traits: { + email: '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', + phone: '', + firstName: '96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a', + lastName: '12918b23d69d698324a78a8ab8f5060fdb25537ea9620f956d39adca151c3ef9', + city: 'London', + state: 'England', + country: 'GB', + postalCode: 'EC3M', + street: '5c100d86e9f40bb62a85ca821ff93d96aff6b0dc4c792794c4a4d51ec9246eff', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + originalTimestamp: '2022-11-17T00:22:02.903+05:30', + properties: { + profileId: '34245', + floodlightConfigurationId: '213123123', + ordinal: 'string', + floodlightActivityId: '456543345245', + value: '756', + encryptedUserIdCandidates: ['dfghjbnm'], + quantity: '455678', + encryptionSource: 'AD_SERVING', + encryptionEntityId: '3564523', + encryptionEntityType: 'DCM_ACCOUNT', + requestType: 'batchupdate', + }, + type: 'track', + event: 'event test', + anonymousId: 'randomId', + integrations: { + All: true, + }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + }, + metadata: { + secret: { + access_token: 'dummyApiToken', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + destination: { + Config: { + profileId: '5343234', + treatmentForUnderage: false, + limitAdTracking: false, + childDirectedTreatment: false, + nonPersonalizedAd: false, + rudderAccountId: '2EOknn1JNH7WK1MfNku4fGYKkRK', + enableEnhancedConversions: true, + isHashingRequired: false, + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://dfareporting.googleapis.com/dfareporting/v4/userprofiles/34245/conversions/batchupdate', + headers: { + Authorization: 'Bearer dummyApiToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + kind: 'dfareporting#conversionsBatchUpdateRequest', + encryptionInfo: { + encryptionEntityType: 'DCM_ACCOUNT', + encryptionSource: 'AD_SERVING', + encryptionEntityId: '3564523', + kind: 'dfareporting#encryptionInfo', + }, + conversions: [ + { + floodlightConfigurationId: '213123123', + ordinal: 'string', + timestampMicros: '1668624722903000', + floodlightActivityId: '456543345245', + quantity: '455678', + value: 756, + encryptedUserIdCandidates: ['dfghjbnm'], + nonPersonalizedAd: false, + treatmentForUnderage: false, + userIdentifiers: [ + { + hashedEmail: + '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', + }, + { + addressInfo: { + hashedFirstName: + '96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a', + hashedLastName: + '12918b23d69d698324a78a8ab8f5060fdb25537ea9620f956d39adca151c3ef9', + hashedStreetAddress: + '5c100d86e9f40bb62a85ca821ff93d96aff6b0dc4c792794c4a4d51ec9246eff', + city: 'London', + state: 'England', + postalCode: 'EC3M', + countryCode: 'GB', + }, + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: { + secret: { + access_token: 'dummyApiToken', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'campaign_manager', + description: 'Test 8: Enhanced Conversions with no traits in request payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + device: { + id: '0572f78fa49c648e', + name: 'generic_x86_arm', + type: 'Android', + model: 'AOSP on IA Emulator', + manufacturer: 'Google', + adTrackingEnabled: true, + advertisingId: '44c97318-9040-4361-8bc7-4eb30f665ca8', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + originalTimestamp: '2022-11-17T00:22:02.903+05:30', + properties: { + profileId: '34245', + floodlightConfigurationId: '213123123', + ordinal: 'string', + floodlightActivityId: '456543345245', + value: '756', + encryptedUserIdCandidates: ['dfghjbnm'], + quantity: '455678', + encryptionSource: 'AD_SERVING', + encryptionEntityId: '3564523', + encryptionEntityType: 'DCM_ACCOUNT', + requestType: 'batchupdate', + }, + type: 'track', + event: 'event test', + anonymousId: 'randomId', + integrations: { + All: true, + }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + }, + metadata: { + secret: { + access_token: 'dummyApiToken', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + destination: { + Config: { + profileId: '5343234', + treatmentForUnderage: false, + limitAdTracking: false, + childDirectedTreatment: false, + nonPersonalizedAd: false, + rudderAccountId: '2EOknn1JNH7WK1MfNku4fGYKkRK', + enableEnhancedConversions: true, + isHashingRequired: true, + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://dfareporting.googleapis.com/dfareporting/v4/userprofiles/34245/conversions/batchupdate', + headers: { + Authorization: 'Bearer dummyApiToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + kind: 'dfareporting#conversionsBatchUpdateRequest', + encryptionInfo: { + encryptionEntityType: 'DCM_ACCOUNT', + encryptionSource: 'AD_SERVING', + encryptionEntityId: '3564523', + kind: 'dfareporting#encryptionInfo', + }, + conversions: [ + { + floodlightConfigurationId: '213123123', + ordinal: 'string', + timestampMicros: '1668624722903000', + floodlightActivityId: '456543345245', + quantity: '455678', + value: 756, + encryptedUserIdCandidates: ['dfghjbnm'], + nonPersonalizedAd: false, + treatmentForUnderage: false, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: { + secret: { + access_token: 'dummyApiToken', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + statusCode: 200, + }, + ], + }, + }, + }, ]; From a80d403d0b88259c837564f9f0cd1b7969c5d4ca Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Mon, 17 Jun 2024 18:50:54 +0530 Subject: [PATCH 212/240] chore: changes in transformation error handling --- src/util/ivmFactory.js | 11 +++++++---- src/util/prometheus.js | 6 ++++++ test/__tests__/user_transformation.test.js | 8 ++++---- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/util/ivmFactory.js b/src/util/ivmFactory.js index 50bf4caa49..9bb5606b6f 100644 --- a/src/util/ivmFactory.js +++ b/src/util/ivmFactory.js @@ -243,12 +243,15 @@ async function createIvm(code, libraryVersionIds, versionId, credentials, secret }), ); - await jail.set('_credential', function (key) { + await jail.set('_credential', function (...args) { if (_.isNil(credentials) || !_.isObject(credentials)) { - throw new Error('Credentials in incorrect format'); + logger.error('Error fetching credentials map'); + stats.increment('credential_error', { versionId }); + return undefined; } - if (key.length > 1 || _.isNil(key[0]) || !_.isString(key[0])) { - throw new Error('Key should be a string'); + const key = args[0][0]; + if (_.isNil(key)) { + throw new Error('Key should be valid and defined'); } return credentials[key]; }); diff --git a/src/util/prometheus.js b/src/util/prometheus.js index bc4c6f2eb9..e37fe249ae 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -569,6 +569,12 @@ class Prometheus { 'module', ], }, + { + name: 'credentials_error', + help: 'Error in fetching credentials count', + type: 'counter', + labelNames: ['versionId'], + }, // Gauges { diff --git a/test/__tests__/user_transformation.test.js b/test/__tests__/user_transformation.test.js index 710b21e6d6..e3c6122c0c 100644 --- a/test/__tests__/user_transformation.test.js +++ b/test/__tests__/user_transformation.test.js @@ -1171,7 +1171,7 @@ describe("User transformation", () => { expect(fetch).toHaveBeenCalledWith( `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` ); - expect(output[0].error).toMatch(/Key should be a string/); + expect(output[0].error).toMatch(/Key should be valid and defined/); }); it(`Simple ${name} Test with credentials with multiple arguements for codeVersion 1`, async () => { @@ -1185,7 +1185,7 @@ describe("User transformation", () => { name, code: ` export function transformEvent(event, metadata) { - event.credentialValue = credential('arg1', 'arg2'); + event.credentialValue = credential('key1', 'key2'); return event; } ` @@ -1200,7 +1200,7 @@ describe("User transformation", () => { expect(fetch).toHaveBeenCalledWith( `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` ); - expect(output[0].error).toMatch(/Key should be a string/); + expect(output[0].transformedEvent.credentialValue).toEqual("value1"); }); it(`Simple ${name} Test with credentials with non string key for codeVersion 1`, async () => { @@ -1229,7 +1229,7 @@ describe("User transformation", () => { expect(fetch).toHaveBeenCalledWith( `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` ); - expect(output[0].error).toMatch(/Key should be a string/); + expect(output[0].transformedEvent.credentialValue).toEqual(undefined); }); it(`Simple ${name} Test with credentials without value for codeVersion 1`, async () => { From 67e9277120e39402b32eac55ffa34a0053b6af61 Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Tue, 18 Jun 2024 12:19:20 +0530 Subject: [PATCH 213/240] fix: fixes in javascript transformation --- src/util/customTransformer-v1.js | 2 + src/util/customTransformer.js | 10 ++-- src/util/ivmFactory.js | 51 ++++++++++++++----- src/util/prometheus.js | 4 +- ...user_transformation_input_credentials.json | 14 ++--- .../user_transformation.integration.test.js | 6 +-- test/__tests__/user_transformation.test.js | 24 +++++---- .../user_transformation_fetch.test.js | 24 ++++----- 8 files changed, 85 insertions(+), 50 deletions(-) diff --git a/src/util/customTransformer-v1.js b/src/util/customTransformer-v1.js index 4d846687b5..ea6dec7759 100644 --- a/src/util/customTransformer-v1.js +++ b/src/util/customTransformer-v1.js @@ -63,6 +63,8 @@ async function userTransformHandlerV1( userTransformation.code, libraryVersionIds, userTransformation.versionId, + userTransformation.id, + userTransformation.workspaceId || events[0].metadata?.workspaceId, credentials, userTransformation.secrets || {}, testMode, diff --git a/src/util/customTransformer.js b/src/util/customTransformer.js index 3b3a3539f3..5107d7e29a 100644 --- a/src/util/customTransformer.js +++ b/src/util/customTransformer.js @@ -289,15 +289,15 @@ async function userTransformHandler( }); const credentialsMap = {}; if (testMode === false) { - events[0]?.credentials?.forEach((cred) => { - credentialsMap[cred.key] = cred.value; + (events[0]?.credentials || []).forEach((cred) => { + credentialsMap[cred.Key] = cred.Value; }); } else { - credentials?.forEach((cred) => { - credentialsMap[cred.key] = cred.value; + (credentials || []).forEach((cred) => { + credentialsMap[cred.Key] = cred.Value; }); events.forEach((ev) => { - if (isNil(ev?.credentials)) { + if (isNil(ev.credentials)) { ev.credentials = credentials; } }); diff --git a/src/util/ivmFactory.js b/src/util/ivmFactory.js index 9bb5606b6f..216c6d15c6 100644 --- a/src/util/ivmFactory.js +++ b/src/util/ivmFactory.js @@ -30,7 +30,16 @@ async function loadModule(isolateInternal, contextInternal, moduleName, moduleCo return module; } -async function createIvm(code, libraryVersionIds, versionId, credentials, secrets, testMode) { +async function createIvm( + code, + libraryVersionIds, + versionId, + transformationId, + workspaceId, + credentials, + secrets, + testMode, +) { const createIvmStartTime = new Date(); const logs = []; const libraries = await Promise.all( @@ -243,16 +252,12 @@ async function createIvm(code, libraryVersionIds, versionId, credentials, secret }), ); - await jail.set('_credential', function (...args) { + await jail.set('_credential', function (key) { if (_.isNil(credentials) || !_.isObject(credentials)) { - logger.error('Error fetching credentials map'); - stats.increment('credential_error', { versionId }); + logger.error('Error fetching credentials map', versionId); + stats.increment('credential_error', { transformationId, workspaceId }); return undefined; } - const key = args[0][0]; - if (_.isNil(key)) { - throw new Error('Key should be valid and defined'); - } return credentials[key]; }); @@ -337,9 +342,11 @@ async function createIvm(code, libraryVersionIds, versionId, credentials, secret let credential = _credential; delete _credential; global.credential = function(...args) { - return credential([ - ...args.map(arg => new ivm.ExternalCopy(arg).copyInto()) - ]); + const key = args[0]; + if (key === null || key === undefined) { + throw new Error('Key should be valid and defined'+ JSON.stringify(args)); + } + return credential(new ivm.ExternalCopy(key).copyInto()); }; return new ivm.Reference(function forwardMainPromise( @@ -432,10 +439,28 @@ async function compileUserLibrary(code) { return evaluateModule(isolate, context, code); } -async function getFactory(code, libraryVersionIds, versionId, credentials, secrets, testMode) { +async function getFactory( + code, + libraryVersionIds, + transformationId, + workspaceId, + versionId, + credentials, + secrets, + testMode, +) { const factory = { create: async () => { - return createIvm(code, libraryVersionIds, versionId, credentials, secrets, testMode); + return createIvm( + code, + libraryVersionIds, + versionId, + transformationId, + workspaceId, + credentials, + secrets, + testMode, + ); }, destroy: async (client) => { client.fnRef.release(); diff --git a/src/util/prometheus.js b/src/util/prometheus.js index e37fe249ae..3b9a9d5d44 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -570,10 +570,10 @@ class Prometheus { ], }, { - name: 'credentials_error', + name: 'credential_error', help: 'Error in fetching credentials count', type: 'counter', - labelNames: ['versionId'], + labelNames: ['transformationId', 'workspaceId'], }, // Gauges diff --git a/test/__tests__/data/user_transformation_input_credentials.json b/test/__tests__/data/user_transformation_input_credentials.json index 49b224e560..bd2593f56d 100644 --- a/test/__tests__/data/user_transformation_input_credentials.json +++ b/test/__tests__/data/user_transformation_input_credentials.json @@ -51,14 +51,16 @@ }, "credentials": [ { - "key": "key1", - "value": "value1", - "isPublic": true + "Id": "id1", + "Key": "key1", + "Value": "value1", + "IsSecret": false }, { - "key": "key2", - "value": "value2", - "isPublic": true + "Id": "id2", + "Key": "key2", + "Value": "value2", + "IsSecret": true } ] } diff --git a/test/__tests__/user_transformation.integration.test.js b/test/__tests__/user_transformation.integration.test.js index 1e2fafeb73..712e151a45 100644 --- a/test/__tests__/user_transformation.integration.test.js +++ b/test/__tests__/user_transformation.integration.test.js @@ -159,14 +159,14 @@ describe("Function invocation & creation tests", () => { versionId, [], trRevCode, - null, + [], true ); expect(response).toEqual(outputEvents); // Test with language python; should return same output trRevCode = contructTrRevCode(versionId, 'python'); - response = await userTransformHandler(inputEvents, versionId, [], trRevCode, null, true); + response = await userTransformHandler(inputEvents, versionId, [], trRevCode, [], true); expect(response).toEqual(outputEvents); }); @@ -185,7 +185,7 @@ describe("Function invocation & creation tests", () => { }); await expect(async () => { - await userTransformHandler(inputEvents, respBody.versionId, null, []); + await userTransformHandler(inputEvents, respBody.versionId, []); }).rejects.toThrow(RetryRequestError); // If function is not found, it will be created diff --git a/test/__tests__/user_transformation.test.js b/test/__tests__/user_transformation.test.js index e3c6122c0c..1abece8f0c 100644 --- a/test/__tests__/user_transformation.test.js +++ b/test/__tests__/user_transformation.test.js @@ -1073,7 +1073,7 @@ describe("User transformation", () => { trRevCode.versionId, [libraryVersionId], trRevCode, - null, + [], true ); @@ -1110,7 +1110,7 @@ describe("User transformation", () => { trRevCode.versionId, [], trRevCode, - null, + [], true ); expect(output).toEqual(expectedData); @@ -1128,7 +1128,7 @@ describe("User transformation", () => { name, code: ` export function transformEvent(event, metadata) { - event.credentialValue = credential("key1"); + event.credentialValue = credential('key1'); return event; } ` @@ -1174,7 +1174,7 @@ describe("User transformation", () => { expect(output[0].error).toMatch(/Key should be valid and defined/); }); - it(`Simple ${name} Test with credentials with multiple arguements for codeVersion 1`, async () => { + it(`Simple ${name} Test with credentials with multiple arguments for codeVersion 1`, async () => { const versionId = randomID(); const inputData = require(`./data/${integration}_input_credentials.json`); @@ -1214,7 +1214,10 @@ describe("User transformation", () => { name, code: ` export function transformEvent(event, metadata) { - event.credentialValue = credential(1); + event.credentialValueForNumkey = credential(1); + event.credentialValueForBoolkey = credential(true); + event.credentialValueForArraykey = credential([]); + event.credentialValueForObjkey = credential({}); return event; } ` @@ -1229,7 +1232,10 @@ describe("User transformation", () => { expect(fetch).toHaveBeenCalledWith( `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` ); - expect(output[0].transformedEvent.credentialValue).toEqual(undefined); + expect(output[0].transformedEvent.credentialValueForNumkey).toEqual(undefined); + expect(output[0].transformedEvent.credentialValueForBoolkey).toEqual(undefined); + expect(output[0].transformedEvent.credentialValueForArraykey).toEqual(undefined); + expect(output[0].transformedEvent.credentialValueForObjkey).toEqual(undefined); }); it(`Simple ${name} Test with credentials without value for codeVersion 1`, async () => { @@ -1707,7 +1713,7 @@ describe("Python transformations", () => { trRevCode.versionId, [], trRevCode, - null, + [], true ); expect(output).toEqual(outputData); @@ -1753,7 +1759,7 @@ describe("Python transformations", () => { trRevCode.versionId, Object.values(importNameLibraryVersionIdsMap), trRevCode, - null, + [], true ); expect(output).toEqual(outputData); @@ -1795,7 +1801,7 @@ describe("Python transformations", () => { trRevCode.versionId, Object.values(importNameLibraryVersionIdsMap), trRevCode, - null, + [], true ); expect(output).toEqual(outputData); diff --git a/test/__tests__/user_transformation_fetch.test.js b/test/__tests__/user_transformation_fetch.test.js index edbac04cf3..5bf9d54935 100644 --- a/test/__tests__/user_transformation_fetch.test.js +++ b/test/__tests__/user_transformation_fetch.test.js @@ -43,7 +43,7 @@ describe("User transformation fetch tests", () => { ` }; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); expect(output).toEqual({ logs: [], transformedEvents: expectedData.map(ev => (ev.transformedEvent)) @@ -67,7 +67,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "ERROR"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); expect(mockResolver).toHaveBeenCalledTimes(0); output.transformedEvents.forEach(ev => { @@ -92,7 +92,7 @@ describe("User transformation fetch tests", () => { const errMsg = "ERROR"; mockResolver.mockResolvedValue([ '127.0.0.1' ]); - const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); expect(mockResolver).toHaveBeenCalledTimes(inputData.length); expect(mockResolver).toHaveBeenCalledWith('abc.xyz.com'); @@ -122,7 +122,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "invalid url, localhost requests are not allowed"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); expect(mockResolver).toHaveBeenCalledTimes(0); output.transformedEvents.forEach(ev => { @@ -152,7 +152,7 @@ describe("User transformation fetch tests", () => { const errMsg = "request to https://abc.xyz.com/dummyUrl failed, reason: Invalid IP address: unable to resolve IP address for abc.xyz.com"; mockResolver.mockRejectedValue('invalid host'); - const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); expect(mockResolver).toHaveBeenCalledTimes(inputData.length); expect(mockResolver).toHaveBeenCalledWith('abc.xyz.com'); @@ -182,7 +182,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "invalid protocol, only http and https are supported"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); output.transformedEvents.forEach(ev => { expect(ev.errMsg).toEqual(errMsg); @@ -204,7 +204,7 @@ describe("User transformation fetch tests", () => { versionId, }; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); expect(output).toEqual({ logs: [], transformedEvents: expectedData.map(ev => (ev.transformedEvent)) @@ -226,7 +226,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "ERROR"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); expect(mockResolver).toHaveBeenCalledTimes(0); output.transformedEvents.forEach(ev => { @@ -250,7 +250,7 @@ describe("User transformation fetch tests", () => { const errMsg = "ERROR"; mockResolver.mockRejectedValue('invalid host'); - const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); expect(mockResolver).toHaveBeenCalledTimes(inputData.length); expect(mockResolver).toHaveBeenCalledWith('abc.xyz.com'); @@ -279,7 +279,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "invalid url, localhost requests are not allowed"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); expect(mockResolver).toHaveBeenCalledTimes(0); output.transformedEvents.forEach(ev => { @@ -308,7 +308,7 @@ describe("User transformation fetch tests", () => { const errMsg = "request to https://abc.xyz.com/dummyUrl failed, reason: Invalid IP address: cannot use 127.0.0.1 as IP address"; mockResolver.mockResolvedValue(['3.122.122.122', '127.0.0.1']); - const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); expect(mockResolver).toHaveBeenCalledTimes(inputData.length); expect(mockResolver).toHaveBeenCalledWith('abc.xyz.com'); @@ -337,7 +337,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "fetch url is required"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, null, true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); output.transformedEvents.forEach(ev => { expect(ev.errMsg).toEqual(errMsg); From e7a59f977ff29e53f7b6e7cc39f7f0654aeac120 Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Tue, 18 Jun 2024 13:09:57 +0530 Subject: [PATCH 214/240] chore: added tests for transform batch and code version 0 --- test/__tests__/user_transformation.test.js | 91 ++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/test/__tests__/user_transformation.test.js b/test/__tests__/user_transformation.test.js index 1abece8f0c..e94df4f4df 100644 --- a/test/__tests__/user_transformation.test.js +++ b/test/__tests__/user_transformation.test.js @@ -1295,6 +1295,97 @@ describe("User transformation", () => { ); expect(output[0].transformedEvent.credentialValue).toEqual(undefined); }); + + it(`Simple ${name} Batch Test with credentials for codeVersion 1`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformBatch(events, metadata) { + events.forEach((event) => { + event.credentialValue1 = credential("key1"); + event.credentialValue2 = credential("key3"); + }); + return events; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].transformedEvent.credentialValue1).toEqual("value1"); + expect(output[0].transformedEvent.credentialValue2).toEqual(undefined); + }); + + it(`Simple ${name} Batch Test with credentials without key for codeVersion 1`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformBatch(events, metadata) { + events.forEach((event) => { + event.credentialValue = credential(); + }); + return events; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].error).toMatch(/Key should be valid and defined/); + }); + + it(`Simple ${name} Test with credentials for codeVersion 0`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "0", + name, + code: ` + function transform(events) { + events.forEach((event) => { + event.credentialValue = credential + }); + return events; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + try { + await userTransformHandler(inputData, versionId, []); + } catch (e) { + expect(e).toEqual('credential is not defined'); + } + }); }); }); From 46cd3413af065b76e4f89fb5b2720cbf6213e51b Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Tue, 18 Jun 2024 13:39:57 +0530 Subject: [PATCH 215/240] fix: pr comments --- src/controllers/userTransform.ts | 2 +- src/util/customTransformer-v1.js | 2 +- src/util/ivmFactory.js | 8 ++++---- src/util/prometheus.js | 2 +- test/__tests__/user_transformation.test.js | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/controllers/userTransform.ts b/src/controllers/userTransform.ts index 33e1507832..8f6e3447a1 100644 --- a/src/controllers/userTransform.ts +++ b/src/controllers/userTransform.ts @@ -33,7 +33,7 @@ export class UserTransformController { '(User transform - router:/transformation/test ):: Request to transformer', ctx.request.body, ); - const { events, trRevCode, libraryVersionIDs = [], credentials } = ctx.request.body as any; + const { events, trRevCode, libraryVersionIDs = [], credentials = [] } = ctx.request.body as any; const response = await UserTransformService.testTransformRoutine( events, trRevCode, diff --git a/src/util/customTransformer-v1.js b/src/util/customTransformer-v1.js index ea6dec7759..e0212831ca 100644 --- a/src/util/customTransformer-v1.js +++ b/src/util/customTransformer-v1.js @@ -64,7 +64,7 @@ async function userTransformHandlerV1( libraryVersionIds, userTransformation.versionId, userTransformation.id, - userTransformation.workspaceId || events[0].metadata?.workspaceId, + userTransformation.workspaceId, credentials, userTransformation.secrets || {}, testMode, diff --git a/src/util/ivmFactory.js b/src/util/ivmFactory.js index 216c6d15c6..b31c87c049 100644 --- a/src/util/ivmFactory.js +++ b/src/util/ivmFactory.js @@ -1,6 +1,6 @@ const ivm = require('isolated-vm'); const fetch = require('node-fetch'); -const _ = require('lodash'); +const { isNil, isObject, camelCase } = require('lodash'); const { getLibraryCodeV1, getRudderLibByImportName } = require('./customTransforrmationsStore-v1'); const { extractStackTraceUptoLastSubstringMatch } = require('./utils'); @@ -60,7 +60,7 @@ async function createIvm( // TODO: Check if this should this be && libraries.forEach((library) => { - const libHandleName = _.camelCase(library.name); + const libHandleName = camelCase(library.name); if (extractedLibraries.includes(libHandleName)) { librariesMap[libHandleName] = library.code; } @@ -253,9 +253,9 @@ async function createIvm( ); await jail.set('_credential', function (key) { - if (_.isNil(credentials) || !_.isObject(credentials)) { + if (isNil(credentials) || !isObject(credentials)) { logger.error('Error fetching credentials map', versionId); - stats.increment('credential_error', { transformationId, workspaceId }); + stats.increment('credential_errors', { transformationId, workspaceId }); return undefined; } return credentials[key]; diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 3b9a9d5d44..0f8a1f4e0c 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -570,7 +570,7 @@ class Prometheus { ], }, { - name: 'credential_error', + name: 'credential_errors', help: 'Error in fetching credentials count', type: 'counter', labelNames: ['transformationId', 'workspaceId'], diff --git a/test/__tests__/user_transformation.test.js b/test/__tests__/user_transformation.test.js index e94df4f4df..200895f480 100644 --- a/test/__tests__/user_transformation.test.js +++ b/test/__tests__/user_transformation.test.js @@ -1370,7 +1370,7 @@ describe("User transformation", () => { code: ` function transform(events) { events.forEach((event) => { - event.credentialValue = credential + event.credentialValue = credential('key1'); }); return events; } From 819f508b017f988e302b6e2579cb3da4030dcfa0 Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Tue, 18 Jun 2024 13:49:08 +0530 Subject: [PATCH 216/240] fix: undefined tests --- test/__tests__/user_transformation.test.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/__tests__/user_transformation.test.js b/test/__tests__/user_transformation.test.js index 200895f480..574a5417ca 100644 --- a/test/__tests__/user_transformation.test.js +++ b/test/__tests__/user_transformation.test.js @@ -1232,10 +1232,10 @@ describe("User transformation", () => { expect(fetch).toHaveBeenCalledWith( `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` ); - expect(output[0].transformedEvent.credentialValueForNumkey).toEqual(undefined); - expect(output[0].transformedEvent.credentialValueForBoolkey).toEqual(undefined); - expect(output[0].transformedEvent.credentialValueForArraykey).toEqual(undefined); - expect(output[0].transformedEvent.credentialValueForObjkey).toEqual(undefined); + expect(output[0].transformedEvent.credentialValueForNumkey).toBeUndefined(); + expect(output[0].transformedEvent.credentialValueForBoolkey).toBeUndefined(); + expect(output[0].transformedEvent.credentialValueForArraykey).toBeUndefined(); + expect(output[0].transformedEvent.credentialValueForObjkey).toBeUndefined(); }); it(`Simple ${name} Test with credentials without value for codeVersion 1`, async () => { @@ -1264,7 +1264,7 @@ describe("User transformation", () => { expect(fetch).toHaveBeenCalledWith( `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` ); - expect(output[0].transformedEvent.credentialValue).toEqual(undefined); + expect(output[0].transformedEvent.credentialValue).toBeUndefined(); }); it(`Simple ${name} Test without credentials for codeVersion 1`, async () => { @@ -1293,7 +1293,7 @@ describe("User transformation", () => { expect(fetch).toHaveBeenCalledWith( `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` ); - expect(output[0].transformedEvent.credentialValue).toEqual(undefined); + expect(output[0].transformedEvent.credentialValue).toBeUndefined(); }); it(`Simple ${name} Batch Test with credentials for codeVersion 1`, async () => { @@ -1325,7 +1325,7 @@ describe("User transformation", () => { `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` ); expect(output[0].transformedEvent.credentialValue1).toEqual("value1"); - expect(output[0].transformedEvent.credentialValue2).toEqual(undefined); + expect(output[0].transformedEvent.credentialValue2).toBeUndefined(); }); it(`Simple ${name} Batch Test with credentials without key for codeVersion 1`, async () => { From f55c4818155cfbbfeda761153b036563e0877329 Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Tue, 18 Jun 2024 16:29:37 +0530 Subject: [PATCH 217/240] feat: onboard klaviyo bulk upload destination (#3348) * feat: onboard klaviyo bulk upload destination, initial commit * chore: update router transformation logic * chore: refactor logic * chore: refactor logic * add location object support * chore: refactor klaviyo id and anonymous id logic * chore: add processor tests * chore: add jobidentifier logic * chore: fix exports, tests, rtflow removed * chore: address review comments, refactor * chore: add custom properties * chore: add email identifier for klaviyo bulk --- .../klaviyo_bulk_upload/config.js | 5 + .../klaviyo_bulk_upload/procWorkflow.yaml | 35 ++ .../destinations/klaviyo_bulk_upload/utils.js | 145 ++++++ src/constants/destinationCanonicalNames.js | 7 + .../klaviyo_bulk_upload/processor/data.ts | 490 ++++++++++++++++++ 5 files changed, 682 insertions(+) create mode 100644 src/cdk/v2/destinations/klaviyo_bulk_upload/config.js create mode 100644 src/cdk/v2/destinations/klaviyo_bulk_upload/procWorkflow.yaml create mode 100644 src/cdk/v2/destinations/klaviyo_bulk_upload/utils.js create mode 100644 test/integrations/destinations/klaviyo_bulk_upload/processor/data.ts diff --git a/src/cdk/v2/destinations/klaviyo_bulk_upload/config.js b/src/cdk/v2/destinations/klaviyo_bulk_upload/config.js new file mode 100644 index 0000000000..43a904499b --- /dev/null +++ b/src/cdk/v2/destinations/klaviyo_bulk_upload/config.js @@ -0,0 +1,5 @@ +const KLAVIYO_BULK_UPLOAD = 'klaviyo_bulk_upload'; + +module.exports = { + KLAVIYO_BULK_UPLOAD, +}; diff --git a/src/cdk/v2/destinations/klaviyo_bulk_upload/procWorkflow.yaml b/src/cdk/v2/destinations/klaviyo_bulk_upload/procWorkflow.yaml new file mode 100644 index 0000000000..44290ff520 --- /dev/null +++ b/src/cdk/v2/destinations/klaviyo_bulk_upload/procWorkflow.yaml @@ -0,0 +1,35 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + - name: defaultRequestConfig + path: ../../../../v0/util + - name: removeUndefinedAndNullValues + path: ../../../../v0/util + - path: ./utils + - path: ../../bindings/jsontemplate + exportAll: true + - path: ./config + +steps: + - name: messageType + template: | + .message.type.toLowerCase(); + - name: validateInput + template: | + let messageType = .message.type; + $.assert(messageType, "message Type is not present. Aborting"); + $.assert(.message.type.toLowerCase() ==='identify', "Event type " + .message.type.toLowerCase() + " is not supported. Aborting message."); + $.assertConfig(.destination.Config.privateApiKey, "Private Api Key is not present. Aborting"); + + - name: generatePayload + template: | + const transformedPayload = $.combinePayloads(^.{.message.type === $.EventType.IDENTIFY}[], ^[0].destination) + transformedPayload + + - name: buildResponseForProcessTransformation + description: build response + template: | + const response = $.defaultRequestConfig(); + response.body.JSON = $.outputs.generatePayload + response diff --git a/src/cdk/v2/destinations/klaviyo_bulk_upload/utils.js b/src/cdk/v2/destinations/klaviyo_bulk_upload/utils.js new file mode 100644 index 0000000000..c068c4762c --- /dev/null +++ b/src/cdk/v2/destinations/klaviyo_bulk_upload/utils.js @@ -0,0 +1,145 @@ +/* eslint-disable no-param-reassign */ +const { removeUndefinedValues } = require('../../../../v0/util'); + +const locationAttributes = [ + 'address1', + 'address2', + 'city', + 'country', + 'latitude', + 'longitude', + 'region', + 'zip', + 'ip', +]; + +const standardTraits = [ + 'email', + 'phone_number', + 'external_id', + 'anonymous_id', + '_kx', + 'first_name', + 'last_name', + 'organization', + 'title', + 'image', +]; + +function setStandardAndCustomTraits(traits) { + const standardAttributes = {}; + const customAttributes = {}; + return Object.keys(traits).reduce( + (result, key) => { + if (!locationAttributes.includes(key) && standardTraits.includes(key)) { + result.standardAttributes[key] = traits[key]; + } else if (!locationAttributes.includes(key)) { + result.customAttributes[key] = traits[key]; + } + return result; + }, + { standardAttributes, customAttributes }, + ); +} + +function generateLocationObject({ + traits: { address1, address2, city, country, latitude, longitude, region, zip, ip }, +}) { + const locationObject = { + address1, + address2, + city, + country, + latitude, + longitude, + region, + zip, + ip, + }; + + return removeUndefinedValues(locationObject); +} + +function transformSingleMessage(data, metadata) { + const { context, traits } = data; + const location = generateLocationObject(data); + const { jobId } = metadata; + const { standardAttributes, customAttributes } = setStandardAndCustomTraits(traits); + const transformedSinglePayload = { + type: 'profile', + attributes: { + ...standardAttributes, + location, + properties: customAttributes, + anonymous_id: context.externalId[0].id, + jobIdentifier: `${context.externalId[0].id}:${jobId}`, + }, + }; + if (context.externalId[0].identifierType === 'id') { + transformedSinglePayload.id = context.externalId[0].id || traits.id; + transformedSinglePayload.attributes.anonymous_id = context.externalId[0].id; + } else if (context.externalId[0].identifierType === 'email') { + transformedSinglePayload.attributes.email = context.externalId[0].id; + } + if (Object.keys(transformedSinglePayload.attributes.location).length === 0) { + delete transformedSinglePayload.attributes.location; + } + if (Object.keys(transformedSinglePayload.attributes.properties).length === 0) { + delete transformedSinglePayload.attributes.properties; + } + return removeUndefinedValues(transformedSinglePayload); +} + +function wrapCombinePayloads(transformedInputs, destinationObj) { + if (destinationObj.Config.listId) { + return { + payload: { + data: { + type: 'profile-bulk-import-job', + attributes: { + profiles: { + data: transformedInputs, + }, + }, + relationships: { + lists: { + data: [ + { + type: 'list', + id: destinationObj.Config.listId, + }, + ], + }, + }, + }, + }, + destination: destinationObj, + }; + } + return { + payload: { + data: { + type: 'profile-bulk-import-job', + attributes: { + profiles: { + data: transformedInputs, + }, + }, + }, + }, + destination: destinationObj, + }; +} + +function combinePayloads(inputs) { + const transformedInputs = inputs.map((input) => { + const { message, metadata } = input; + return transformSingleMessage(message, metadata); + }); + const destinationObj = inputs[inputs.length - 1].destination; + + const { payload } = wrapCombinePayloads(transformedInputs, destinationObj); + return { ...payload }; +} + +module.exports = { transformSingleMessage, combinePayloads }; diff --git a/src/constants/destinationCanonicalNames.js b/src/constants/destinationCanonicalNames.js index 19136eab59..419c56d2c6 100644 --- a/src/constants/destinationCanonicalNames.js +++ b/src/constants/destinationCanonicalNames.js @@ -166,6 +166,13 @@ const DestCanonicalNames = { ], koala: ['Koala', 'koala', 'KOALA'], bloomreach: ['Bloomreach', 'bloomreach', 'BLOOMREACH'], + KLAVIYO_BULK_UPLOAD: [ + 'klaviyo bulk upload', + 'klaviyo_bulk_upload', + 'klaviyoBulkUpload', + 'Klaviyo Bulk Upload', + 'klaviyobulkupload', + ], emarsys: ['EMARSYS', 'Emarsys', 'emarsys'], }; diff --git a/test/integrations/destinations/klaviyo_bulk_upload/processor/data.ts b/test/integrations/destinations/klaviyo_bulk_upload/processor/data.ts new file mode 100644 index 0000000000..0a97f9035d --- /dev/null +++ b/test/integrations/destinations/klaviyo_bulk_upload/processor/data.ts @@ -0,0 +1,490 @@ +export const data = [ + { + name: 'klaviyo_bulk_upload', + description: 'Successful identify event with location data', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'sources', + context: { + externalId: [ + { + id: 'user1', + identifierType: 'userId', + type: 'KLAVIYO_BULK_UPLOAD-userProfiles', + }, + ], + mappedToDestination: 'true', + sources: { + job_id: '2gif2bMzsX1Nt0rbV1vcbAE3cxC', + job_run_id: 'cp5p5ilq47pqg38v2nfg', + task_run_id: 'cp5p5ilq47pqg38v2ng0', + version: '2051/merge', + }, + }, + traits: { + address1: 'dallas street', + address2: 'oppenheimer market', + city: 'delhi', + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + country: 'India', + phone_number: '+919902330123', + ip: '213.5.6.41', + }, + type: 'identify', + userId: '1', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + privateApiKey: 'pk_dummy_123', + listId: 'list101', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + data: { + type: 'profile-bulk-import-job', + attributes: { + profiles: { + data: [ + { + type: 'profile', + attributes: { + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + phone_number: '+919902330123', + location: { + address1: 'dallas street', + address2: 'oppenheimer market', + city: 'delhi', + country: 'India', + ip: '213.5.6.41', + }, + anonymous_id: 'user1', + jobIdentifier: 'user1:1', + }, + }, + ], + }, + }, + relationships: { + lists: { + data: [ + { + type: 'list', + id: 'list101', + }, + ], + }, + }, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + }, + { + name: 'klaviyo_bulk_upload', + description: 'Successful identify event without location data', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'sources', + context: { + externalId: [ + { + id: 'user1', + identifierType: 'userId', + type: 'KLAVIYO_BULK_UPLOAD-userProfiles', + }, + ], + mappedToDestination: 'true', + sources: { + job_id: '2gif2bMzsX1Nt0rbV1vcbAE3cxC', + job_run_id: 'cp5p5ilq47pqg38v2nfg', + task_run_id: 'cp5p5ilq47pqg38v2ng0', + version: '2051/merge', + }, + }, + traits: { + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + phone_number: '+919902330123', + ip: '213.5.6.41', + }, + type: 'identify', + userId: '1', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + privateApiKey: 'pk_dummy_123', + listId: 'list101', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + data: { + type: 'profile-bulk-import-job', + attributes: { + profiles: { + data: [ + { + type: 'profile', + attributes: { + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + phone_number: '+919902330123', + location: { + ip: '213.5.6.41', + }, + anonymous_id: 'user1', + jobIdentifier: 'user1:1', + }, + }, + ], + }, + }, + relationships: { + lists: { + data: [ + { + type: 'list', + id: 'list101', + }, + ], + }, + }, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + }, + { + name: 'klaviyo_bulk_upload', + description: 'Successful identify event without listId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'sources', + context: { + externalId: [ + { + id: 'user1', + identifierType: 'userId', + type: 'KLAVIYO_BULK_UPLOAD-userProfiles', + }, + ], + mappedToDestination: 'true', + sources: { + job_id: '2gif2bMzsX1Nt0rbV1vcbAE3cxC', + job_run_id: 'cp5p5ilq47pqg38v2nfg', + task_run_id: 'cp5p5ilq47pqg38v2ng0', + version: '2051/merge', + }, + }, + traits: { + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + phone_number: '+919902330123', + ip: '213.5.6.41', + }, + type: 'identify', + userId: '1', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + privateApiKey: 'pk_dummy_123', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + data: { + type: 'profile-bulk-import-job', + attributes: { + profiles: { + data: [ + { + type: 'profile', + attributes: { + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + phone_number: '+919902330123', + location: { + ip: '213.5.6.41', + }, + anonymous_id: 'user1', + jobIdentifier: 'user1:1', + }, + }, + ], + }, + }, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + }, + { + name: 'klaviyo_bulk_upload', + description: 'Successful identify event with custom properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'sources', + context: { + externalId: [ + { + id: 'user1', + identifierType: 'userId', + type: 'KLAVIYO_BULK_UPLOAD-userProfiles', + }, + ], + mappedToDestination: 'true', + sources: { + job_id: '2gif2bMzsX1Nt0rbV1vcbAE3cxC', + job_run_id: 'cp5p5ilq47pqg38v2nfg', + task_run_id: 'cp5p5ilq47pqg38v2ng0', + version: '2051/merge', + }, + }, + traits: { + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + phone_number: '+919902330123', + ip: '213.5.6.41', + last_visit_date: '2020-10-01T00:00:00Z', + lastVisitService: ['Brazilian'], + }, + type: 'identify', + userId: '1', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + privateApiKey: 'pk_dummy_123', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + data: { + type: 'profile-bulk-import-job', + attributes: { + profiles: { + data: [ + { + type: 'profile', + attributes: { + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + phone_number: '+919902330123', + location: { + ip: '213.5.6.41', + }, + anonymous_id: 'user1', + jobIdentifier: 'user1:1', + properties: { + lastVisitService: ['Brazilian'], + last_visit_date: '2020-10-01T00:00:00Z', + }, + }, + }, + ], + }, + }, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + }, +]; From abeb9e77d684a61c98ea766efc0b7dc9f9ddb18b Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Tue, 18 Jun 2024 17:24:45 +0530 Subject: [PATCH 218/240] chore: add logger at integration level (#3401) --- benchmark/metaLogger.js | 18 +- package-lock.json | 51 +++++- package.json | 2 +- src/adapters/network.js | 102 ++++++++--- src/adapters/utils/networkUtils.js | 6 +- .../v2/destinations/gladly/procWorkflow.yaml | 3 +- src/cdk/v2/handler.ts | 6 +- src/controllers/bulkUpload.ts | 2 +- src/controllers/delivery.ts | 14 +- src/controllers/destination.ts | 18 +- src/controllers/regulation.ts | 2 +- src/controllers/source.ts | 10 +- src/controllers/userTransform.ts | 2 +- src/index.ts | 6 +- src/interfaces/DestinationService.ts | 3 - src/interfaces/SourceService.ts | 2 - src/logger.js | 163 +++++++++++++++-- src/services/comparator.ts | 5 +- .../__tests__/nativeIntegration.test.ts | 3 - .../__tests__/postTransformation.test.ts | 2 +- src/services/destination/cdkV2Integration.ts | 11 +- src/services/destination/nativeIntegration.ts | 24 +-- .../destination/postTransformation.ts | 19 +- src/services/misc.ts | 20 +-- .../__tests__/nativeIntegration.test.ts | 17 +- src/services/source/nativeIntegration.ts | 10 +- src/util/errorNotifier/bugsnag.js | 2 +- src/util/errorNotifier/default.js | 2 +- src/util/prometheus.js | 18 +- src/util/redis/redisConnector.js | 12 +- src/util/redis/redisConnector.test.js | 4 +- src/util/utils.js | 3 +- src/v0/destinations/braze/util.js | 2 +- .../campaign_manager/transform.js | 3 +- .../config.js | 1 + .../networkHandler.js | 68 +++++-- .../config.js | 1 + .../networkHandler.js | 166 ++++++++++++++---- .../utils.js | 30 +++- .../config.js | 1 + .../networkHandler.js | 106 ++++++++--- src/v0/destinations/klaviyo/config.js | 2 + src/v0/destinations/klaviyo/transform.js | 20 ++- src/v0/destinations/klaviyo/util.js | 23 ++- src/v0/destinations/mailchimp/utils.js | 7 +- src/v0/sources/adjust/transform.js | 3 +- src/v0/sources/canny/transform.js | 11 +- src/v0/sources/shopify/transform.js | 13 +- src/v0/sources/shopify/util.js | 3 +- .../campaign_manager/networkHandler.js | 12 +- src/v1/destinations/monday/networkHandler.js | 11 +- .../__tests__/pinterestConversion-cdk.test.ts | 2 +- .../destinations/am/dataDelivery/data.ts | 18 ++ 53 files changed, 750 insertions(+), 315 deletions(-) diff --git a/benchmark/metaLogger.js b/benchmark/metaLogger.js index 2af1f599c9..b89ad71066 100644 --- a/benchmark/metaLogger.js +++ b/benchmark/metaLogger.js @@ -2,30 +2,30 @@ const logger = require('../src/logger'); -logger.setLogLevel(Number.POSITIVE_INFINITY); +logger.setLogLevel('random'); const debug = (...args) => { - logger.setLogLevel(logger.levelDebug); + logger.setLogLevel('debug'); logger.debug(...args); - logger.setLogLevel(Number.POSITIVE_INFINITY); + logger.setLogLevel('random'); }; const info = (...args) => { - logger.setLogLevel(logger.levelInfo); + logger.setLogLevel('info'); logger.info(...args); - logger.setLogLevel(Number.POSITIVE_INFINITY); + logger.setLogLevel('random'); }; const warn = (...args) => { - logger.setLogLevel(logger.levelWarn); + logger.setLogLevel('warn'); logger.warn(...args); - logger.setLogLevel(Number.POSITIVE_INFINITY); + logger.setLogLevel('random'); }; const error = (...args) => { - logger.setLogLevel(logger.levelError); + logger.setLogLevel('error'); logger.error(...args); - logger.setLogLevel(Number.POSITIVE_INFINITY); + logger.setLogLevel('random'); }; module.exports = { diff --git a/package-lock.json b/package-lock.json index 1e6b2c7341..066dac2c88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "@koa/router": "^12.0.0", "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", - "@rudderstack/integrations-lib": "^0.2.8", + "@rudderstack/integrations-lib": "^0.2.10", "@rudderstack/json-template-engine": "^0.13.2", "@rudderstack/workflow-engine": "^0.8.2", "@shopify/jest-koa-mocks": "^5.1.1", @@ -143,6 +143,49 @@ "mocha": "2.1.0" } }, + "../rudder-integrations-lib": { + "name": "@rudderstack/integrations-lib", + "version": "0.2.9", + "extraneous": true, + "license": "MIT", + "dependencies": { + "axios": "^1.4.0", + "axios-mock-adapter": "^1.22.0", + "crypto": "^1.0.1", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-airbnb-typescript": "^17.1.0", + "get-value": "^3.0.1", + "handlebars": "^4.7.8", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "set-value": "^4.1.0", + "sha256": "^0.2.0", + "tslib": "^2.4.0", + "winston": "^3.11.0" + }, + "devDependencies": { + "@commitlint/config-conventional": "^18.5.0", + "@types/get-value": "^3.0.3", + "@types/jest": "^29.5.4", + "@types/lodash": "^4.14.195", + "@types/node": "^20.3.3", + "@types/set-value": "^4.0.1", + "@types/sha256": "^0.2.0", + "@typescript-eslint/eslint-plugin": "^6.20.0", + "@typescript-eslint/parser": "^6.20.0", + "commitlint": "^18.6.0", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "husky": "^8.0.0", + "jest": "^29.4.3", + "pre-commit": "^1.2.2", + "prettier": "^2.8.4", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "typescript": "^5.1.6" + } + }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", @@ -4462,9 +4505,9 @@ } }, "node_modules/@rudderstack/integrations-lib": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@rudderstack/integrations-lib/-/integrations-lib-0.2.8.tgz", - "integrity": "sha512-5CJoFFCRDhG7busCGVktKqEEXO0DbFqJ56TOT+jyDdoTf8sZ7SsSJ4NCZYmSplZrbQGj2R+aArnQnpxA4hPGmA==", + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@rudderstack/integrations-lib/-/integrations-lib-0.2.10.tgz", + "integrity": "sha512-PVlRIxO9PVYpR+UNm1qQt85wo0wO9oX0PvoC9XqzYO+C0PfRvkMqac8ghA5ytqeCYNfSIye7DtidaII5ZoCQCA==", "dependencies": { "axios": "^1.4.0", "axios-mock-adapter": "^1.22.0", diff --git a/package.json b/package.json index 304ed36d13..4c558430d9 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@koa/router": "^12.0.0", "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", - "@rudderstack/integrations-lib": "^0.2.8", + "@rudderstack/integrations-lib": "^0.2.10", "@rudderstack/json-template-engine": "^0.13.2", "@rudderstack/workflow-engine": "^0.8.2", "@shopify/jest-koa-mocks": "^5.1.1", diff --git a/src/adapters/network.js b/src/adapters/network.js index 0720638d12..850ba649c8 100644 --- a/src/adapters/network.js +++ b/src/adapters/network.js @@ -45,14 +45,20 @@ const networkClientConfigs = { httpsAgent: new https.Agent({ keepAlive: true }), }; -const fireHTTPStats = (clientResponse, startTime, statTags) => { - const destType = statTags.destType ? statTags.destType : ''; - const feature = statTags.feature ? statTags.feature : ''; - const endpointPath = statTags.endpointPath ? statTags.endpointPath : ''; - const requestMethod = statTags.requestMethod ? statTags.requestMethod : ''; - const module = statTags.module ? statTags.module : ''; - const statusCode = clientResponse.success ? clientResponse.response.status : ''; +const fireOutgoingReqStats = ({ + destType, + feature, + endpointPath, + requestMethod, + module, + metadata = {}, + startTime, + statusCode, + clientResponse, +}) => { + const logMetaInfo = log.getLogMetadata(metadata); stats.timing('outgoing_request_latency', startTime, { + ...logMetaInfo, feature, destType, endpointPath, @@ -60,6 +66,7 @@ const fireHTTPStats = (clientResponse, startTime, statTags) => { module, }); stats.counter('outgoing_request_count', 1, { + ...logMetaInfo, feature, destType, endpointPath, @@ -70,6 +77,36 @@ const fireHTTPStats = (clientResponse, startTime, statTags) => { }); }; +const fireHTTPStats = (clientResponse, startTime, statTags) => { + const destType = statTags.destType ? statTags.destType : ''; + const feature = statTags.feature ? statTags.feature : ''; + const endpointPath = statTags.endpointPath ? statTags.endpointPath : ''; + const requestMethod = statTags.requestMethod ? statTags.requestMethod : ''; + const module = statTags.module ? statTags.module : ''; + const statusCode = clientResponse.success ? clientResponse.response.status : ''; + const defArgs = { + destType, + endpointPath, + feature, + module, + requestMethod, + statusCode, + startTime, + clientResponse, + }; + if (statTags?.metadata) { + const metadata = !Array.isArray(statTags?.metadata) ? [statTags.metadata] : statTags.metadata; + metadata?.forEach((m) => { + fireOutgoingReqStats({ + ...defArgs, + metadata: m, + }); + }); + return; + } + fireOutgoingReqStats(defArgs); +}; + const enhanceRequestOptions = (options) => { const requestOptions = { ...networkClientConfigs, @@ -322,25 +359,6 @@ const prepareProxyRequest = (request) => { return removeUndefinedValues({ endpoint, data, params, headers, method, config }); }; -/** - * depricating: handles proxying requests to destinations from server, expects requsts in "defaultRequestConfig" - * note: needed for test api - * @param {*} request - * @returns - */ -const proxyRequest = async (request, destType) => { - const { endpoint, data, method, params, headers } = prepareProxyRequest(request); - const requestOptions = { - url: endpoint, - data, - params, - headers, - method, - }; - const response = await httpSend(requestOptions, { feature: 'proxy', destType }); - return response; -}; - /** * handles http request and sends the response in a simple format that is followed in transformer * @@ -392,6 +410,38 @@ const handleHttpRequest = async (requestType = 'post', ...httpArgs) => { return { httpResponse, processedResponse }; }; +/** + * depricating: handles proxying requests to destinations from server, expects requsts in "defaultRequestConfig" + * note: needed for test api + * @param {*} request + * @returns + */ +const proxyRequest = async (request, destType) => { + const { metadata } = request; + const { endpoint, data, method, params, headers } = prepareProxyRequest(request); + const requestOptions = { + url: endpoint, + data, + params, + headers, + method, + }; + log.requestLog(`[${destType.toUpperCase()}] delivering data`, { + metadata, + requestDetails: { + body: data, + url: endpoint, + method, + }, + }); + const response = await httpSend(requestOptions, { + feature: 'proxy', + destType, + metadata, + }); + return response; +}; + module.exports = { httpSend, httpGET, diff --git a/src/adapters/utils/networkUtils.js b/src/adapters/utils/networkUtils.js index 0dcb9931e9..4b8dd4fc39 100644 --- a/src/adapters/utils/networkUtils.js +++ b/src/adapters/utils/networkUtils.js @@ -144,10 +144,11 @@ const processAxiosResponse = (clientResponse) => { } // non 2xx status handling for axios response if (response) { - const { data, status } = response; + const { data, status, headers } = response; return { response: data || '', status: status || 500, + ...(isDefinedAndNotNullAndNotEmpty(headers) ? { headers } : {}), }; } // (edge case) response and code is not present @@ -157,10 +158,11 @@ const processAxiosResponse = (clientResponse) => { }; } // success(2xx) axios response - const { data, status } = clientResponse.response; + const { data, status, headers } = clientResponse.response; return { response: data || '', status: status || 500, + ...(isDefinedAndNotNullAndNotEmpty(headers) ? { headers } : {}), }; }; diff --git a/src/cdk/v2/destinations/gladly/procWorkflow.yaml b/src/cdk/v2/destinations/gladly/procWorkflow.yaml index a53a0ca8f5..dcefc9d774 100644 --- a/src/cdk/v2/destinations/gladly/procWorkflow.yaml +++ b/src/cdk/v2/destinations/gladly/procWorkflow.yaml @@ -61,7 +61,8 @@ steps: headers: $.getHeaders(.destination) } const endpoint = $.getEndpoint(.destination) + "?" + $.getQueryParams($.context.payload); - const rawResponse = await $.httpGET(endpoint,requestOptions) + const reqStats = {metadata:.metadata, module: 'router',feature: "transformation", destType:"gladly",requestMethod:"get",endpointPath:"/api/v1/customer-profiles"} + const rawResponse = await $.httpGET(endpoint,requestOptions, reqStats) const processedResponse = $.processAxiosResponse(rawResponse) processedResponse diff --git a/src/cdk/v2/handler.ts b/src/cdk/v2/handler.ts index c437247f74..74ebb716e6 100644 --- a/src/cdk/v2/handler.ts +++ b/src/cdk/v2/handler.ts @@ -17,6 +17,8 @@ import { isCdkV2Destination, } from './utils'; +import logger from '../../logger'; + const defTags = { [tags.TAG_NAMES.IMPLEMENTATION]: tags.IMPLEMENTATIONS.CDK_V2, }; @@ -82,12 +84,12 @@ export async function processCdkV2Workflow( destType: string, parsedEvent: FixMe, feature: string, - logger: FixMe, requestMetadata: NonNullable = {}, bindings: Record = {}, ) { try { - logger.debug(`Processing cdkV2 workflow`); + logger.debug(`Processing cdkV2 workflow`, { destType }); + const workflowEngine = await getCachedWorkflowEngine(destType, feature, bindings); return await executeWorkflow(workflowEngine, parsedEvent, requestMetadata); } catch (error) { diff --git a/src/controllers/bulkUpload.ts b/src/controllers/bulkUpload.ts index 28556dd5df..cb0bcfed3c 100644 --- a/src/controllers/bulkUpload.ts +++ b/src/controllers/bulkUpload.ts @@ -1,5 +1,4 @@ /* eslint-disable global-require, import/no-dynamic-require, @typescript-eslint/no-unused-vars */ -import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import { client as errNotificationClient } from '../util/errorNotifier'; import { getDestFileUploadHandler, @@ -7,6 +6,7 @@ import { getPollStatusHandler, } from '../util/fetchDestinationHandlers'; import { CatchErr, ContextBodySimple } from '../util/types'; +import logger from '../logger'; // TODO: To be refactored and redisgned const ERROR_MESSAGE_PROCESSOR_STRING = 'Error occurred while processing payload.'; diff --git a/src/controllers/delivery.ts b/src/controllers/delivery.ts index 0dc27553cb..9e06b23f4d 100644 --- a/src/controllers/delivery.ts +++ b/src/controllers/delivery.ts @@ -1,9 +1,6 @@ /* eslint-disable prefer-destructuring */ /* eslint-disable sonarjs/no-duplicate-string */ -import { - isDefinedAndNotNullAndNotEmpty, - structuredLogger as logger, -} from '@rudderstack/integrations-lib'; +import { isDefinedAndNotNullAndNotEmpty } from '@rudderstack/integrations-lib'; import { Context } from 'koa'; import { ServiceSelector } from '../helpers/serviceSelector'; import { DeliveryTestService } from '../services/delivertTest/deliveryTest'; @@ -19,12 +16,13 @@ import { import { FixMe } from '../util/types'; import tags from '../v0/util/tags'; import { ControllerUtility } from './util'; +import logger from '../logger'; const NON_DETERMINABLE = 'Non-determinable'; export class DeliveryController { public static async deliverToDestination(ctx: Context) { - logger.debug('Native(Delivery):: Request to transformer::', ctx.request.body); + logger.debug('Native(Delivery):: Request to transformer for delivery::', ctx.request.body); let deliveryResponse: DeliveryV0Response; const requestMetadata = MiscService.getRequestMetadata(ctx); const deliveryRequest = ctx.request.body as ProxyV0Request; @@ -54,12 +52,12 @@ export class DeliveryController { ctx.body = { output: deliveryResponse }; ControllerUtility.deliveryPostProcess(ctx, deliveryResponse.status); - logger.debug('Native(Delivery):: Response from transformer::', ctx.body); + logger.debug('Native(Delivery):: Response from transformer after delivery::', ctx.body); return ctx; } public static async deliverToDestinationV1(ctx: Context) { - logger.debug('Native(Delivery):: Request to transformer::', ctx.request.body); + logger.debug('Native(Delivery):: Request to transformer for delivery::', ctx.request.body); let deliveryResponse: DeliveryV1Response; const requestMetadata = MiscService.getRequestMetadata(ctx); const deliveryRequest = ctx.request.body as ProxyV1Request; @@ -116,7 +114,7 @@ export class DeliveryController { ); ctx.body = { output: response }; ControllerUtility.postProcess(ctx); - logger.debug('Native(Delivery-Test):: Response from transformer::', ctx.body); + logger.debug('Native(Delivery-Test):: Response from transformer after delivery::', ctx.body); return ctx; } } diff --git a/src/controllers/destination.ts b/src/controllers/destination.ts index 92ef4b4c19..998cab67bb 100644 --- a/src/controllers/destination.ts +++ b/src/controllers/destination.ts @@ -1,11 +1,9 @@ -import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import { Context } from 'koa'; import { ServiceSelector } from '../helpers/serviceSelector'; import { DestinationPostTransformationService } from '../services/destination/postTransformation'; import { DestinationPreTransformationService } from '../services/destination/preTransformation'; import { MiscService } from '../services/misc'; import { - ErrorDetailer, ProcessorTransformationRequest, ProcessorTransformationResponse, RouterTransformationRequest, @@ -17,6 +15,7 @@ import { getIntegrationVersion } from '../util/utils'; import { checkInvalidRtTfEvents } from '../v0/util'; import tags from '../v0/util/tags'; import { ControllerUtility } from './util'; +import logger from '../logger'; export class DestinationController { public static async destinationTransformAtProcessor(ctx: Context) { @@ -33,9 +32,6 @@ export class DestinationController { ...metaTags, }); const integrationService = ServiceSelector.getDestinationService(events); - const loggerWithCtx = logger.child({ - ...MiscService.getLoggableData(events[0]?.metadata as unknown as ErrorDetailer), - }); try { integrationService.init(); events = DestinationPreTransformationService.preProcess( @@ -51,7 +47,6 @@ export class DestinationController { destination, version, requestMetadata, - loggerWithCtx, ); } catch (error: any) { resplist = events.map((ev) => { @@ -71,7 +66,6 @@ export class DestinationController { } ctx.body = resplist; ControllerUtility.postProcess(ctx); - loggerWithCtx.debug('Native(Process-Transform):: Response from transformer::', ctx.body); stats.histogram('dest_transform_output_events', resplist.length, { destination, version, @@ -113,9 +107,6 @@ export class DestinationController { return ctx; } const metaTags = MiscService.getMetaTags(events[0].metadata); - const loggerWithCtx = logger.child({ - ...MiscService.getLoggableData(events[0]?.metadata as unknown as ErrorDetailer), - }); stats.histogram('dest_transform_input_events', events.length, { destination, version: 'v0', @@ -132,7 +123,6 @@ export class DestinationController { destination, getIntegrationVersion(), requestMetadata, - loggerWithCtx, ); } catch (error: any) { const metaTO = integrationService.getTags( @@ -155,7 +145,6 @@ export class DestinationController { version: 'v0', ...metaTags, }); - loggerWithCtx.debug('Native(Router-Transform):: Response from transformer::', ctx.body); stats.timing('dest_transform_request_latency', startTime, { destination, version: 'v0', @@ -172,9 +161,6 @@ export class DestinationController { const routerRequest = ctx.request.body as RouterTransformationRequest; const destination = routerRequest.destType; let events = routerRequest.input; - const loggerWithCtx = logger.child({ - ...MiscService.getLoggableData(events[0]?.metadata as unknown as ErrorDetailer), - }); const integrationService = ServiceSelector.getDestinationService(events); try { events = DestinationPreTransformationService.preProcess(events, ctx); @@ -184,7 +170,6 @@ export class DestinationController { destination, getIntegrationVersion(), requestMetadata, - loggerWithCtx, ); ctx.body = resplist; } catch (error: any) { @@ -202,7 +187,6 @@ export class DestinationController { ctx.body = [errResp]; } ControllerUtility.postProcess(ctx); - loggerWithCtx.debug('Native(Process-Transform-Batch):: Response from transformer::', ctx.body); stats.timing('dest_transform_request_latency', startTime, { destination, feature: tags.FEATURES.BATCH, diff --git a/src/controllers/regulation.ts b/src/controllers/regulation.ts index 4b8f87e3fa..2d40c518f4 100644 --- a/src/controllers/regulation.ts +++ b/src/controllers/regulation.ts @@ -1,4 +1,3 @@ -import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import { Context } from 'koa'; import { ServiceSelector } from '../helpers/serviceSelector'; import { DestinationPostTransformationService } from '../services/destination/postTransformation'; @@ -7,6 +6,7 @@ import stats from '../util/stats'; import tags from '../v0/util/tags'; // eslint-disable-next-line @typescript-eslint/no-unused-vars import { CatchErr } from '../util/types'; +import logger from '../logger'; export class RegulationController { public static async deleteUsers(ctx: Context) { diff --git a/src/controllers/source.ts b/src/controllers/source.ts index e1a4931371..bc4b77bd3d 100644 --- a/src/controllers/source.ts +++ b/src/controllers/source.ts @@ -1,9 +1,9 @@ -import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import { Context } from 'koa'; import { ServiceSelector } from '../helpers/serviceSelector'; import { MiscService } from '../services/misc'; import { SourcePostTransformationService } from '../services/source/postTransformation'; import { ControllerUtility } from './util'; +import logger from '../logger'; export class SourceController { public static async sourceTransform(ctx: Context) { @@ -12,7 +12,6 @@ export class SourceController { const events = ctx.request.body as object[]; const { version, source }: { version: string; source: string } = ctx.params; const integrationService = ServiceSelector.getNativeSourceService(); - const loggerWithCtx = logger.child({ version, source }); try { const { implementationVersion, input } = ControllerUtility.adaptInputToVersion( source, @@ -24,7 +23,6 @@ export class SourceController { source, implementationVersion, requestMetadata, - loggerWithCtx, ); ctx.body = resplist; } catch (err: any) { @@ -33,7 +31,11 @@ export class SourceController { ctx.body = [resp]; } ControllerUtility.postProcess(ctx); - loggerWithCtx.debug('Native(Source-Transform):: Response from transformer::', ctx.body); + logger.debug('Native(Source-Transform):: Response from transformer::', { + srcResponse: ctx.body, + version, + source, + }); return ctx; } } diff --git a/src/controllers/userTransform.ts b/src/controllers/userTransform.ts index 0e288c6f04..c81fb1dcb6 100644 --- a/src/controllers/userTransform.ts +++ b/src/controllers/userTransform.ts @@ -1,4 +1,3 @@ -import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import { Context } from 'koa'; import { UserTransformService } from '../services/userTransform'; import { ProcessorTransformationRequest, UserTransformationServiceResponse } from '../types/index'; @@ -8,6 +7,7 @@ import { validateCode, } from '../util/customTransformer'; import { ControllerUtility } from './util'; +import logger from '../logger'; export class UserTransformController { public static async transform(ctx: Context) { diff --git a/src/index.ts b/src/index.ts index 5557994b2e..c5de26c776 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,3 @@ -import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import dotenv from 'dotenv'; import gracefulShutdown from 'http-graceful-shutdown'; import Koa from 'koa'; @@ -11,6 +10,10 @@ import { RedisDB } from './util/redis/redisConnector'; import { logProcessInfo } from './util/utils'; dotenv.config(); + +// eslint-disable-next-line import/first +import logger from './logger'; + const clusterEnabled = process.env.CLUSTER_ENABLED !== 'false'; const port = parseInt(process.env.PORT ?? '9090', 10); const metricsPort = parseInt(process.env.METRICS_PORT || '9091', 10); @@ -32,7 +35,6 @@ app.use( addRequestSizeMiddleware(app); addSwaggerRoutes(app); -logger.info('Using new routes'); applicationRoutes(app); function finalFunction() { diff --git a/src/interfaces/DestinationService.ts b/src/interfaces/DestinationService.ts index 5d7596dac5..b45d9a427c 100644 --- a/src/interfaces/DestinationService.ts +++ b/src/interfaces/DestinationService.ts @@ -28,7 +28,6 @@ export interface DestinationService { destinationType: string, version: string, requestMetadata: NonNullable, - logger: NonNullable, ): Promise; doRouterTransformation( @@ -36,7 +35,6 @@ export interface DestinationService { destinationType: string, version: string, requestMetadata: NonNullable, - logger: NonNullable, ): Promise; doBatchTransformation( @@ -44,7 +42,6 @@ export interface DestinationService { destinationType: string, version: string, requestMetadata: NonNullable, - logger: NonNullable, ): RouterTransformationResponse[]; deliver( diff --git a/src/interfaces/SourceService.ts b/src/interfaces/SourceService.ts index fab6490264..c7de8cfe8b 100644 --- a/src/interfaces/SourceService.ts +++ b/src/interfaces/SourceService.ts @@ -1,5 +1,4 @@ import { MetaTransferObject, SourceTransformationResponse } from '../types/index'; -import { FixMe } from '../util/types'; export interface SourceService { getTags(): MetaTransferObject; @@ -9,6 +8,5 @@ export interface SourceService { sourceType: string, version: string, requestMetadata: NonNullable, - logger: FixMe, ): Promise; } diff --git a/src/logger.js b/src/logger.js index 0685df3387..6daff56c67 100644 --- a/src/logger.js +++ b/src/logger.js @@ -1,38 +1,162 @@ /* istanbul ignore file */ +const { LOGLEVELS, structuredLogger } = require('@rudderstack/integrations-lib'); -const levelDebug = 0; // Most verbose logging level -const levelInfo = 1; // Logs about state of the application -const levelWarn = 2; // Logs about warnings which dont immediately halt the application -const levelError = 3; // Logs about errors which dont immediately halt the application -// any value greater than levelError will work as levelNone +// LOGGER_IMPL can be `console` or `winston` +const loggerImpl = process.env.LOGGER_IMPL ?? 'winston'; -let logLevel = process.env.LOG_LEVEL ? parseInt(process.env.LOG_LEVEL, 10) : levelInfo; +let logLevel = process.env.LOG_LEVEL ?? 'error'; + +const logger = structuredLogger({ + level: logLevel, + fillExcept: [ + 'destinationId', + 'sourceId', + 'destinationType', + 'workspaceId', + 'module', + 'implementation', + 'feature', + 'destType', + ], +}); + +const getLogger = () => { + switch (loggerImpl) { + case 'winston': + return logger; + case 'console': + return console; + } +}; const setLogLevel = (level) => { + const logger = getLogger(); logLevel = level || logLevel; + logger?.setLogLevel(logLevel); +}; + +/** + * obtains the metadata for logging + * + * @param {*} metadata + * @returns { destinationId:string, sourceId:string, workspaceId: string, destType:string, module:string, implementation:string, feature:string } + */ +const getLogMetadata = (metadata) => { + let reqMeta = metadata; + if (Array.isArray(metadata)) { + [reqMeta] = metadata; + } + const destType = reqMeta?.destType || reqMeta?.destinationType; + return { + ...(reqMeta?.destinationId && { destinationId: reqMeta.destinationId }), + ...(reqMeta?.sourceId && { sourceId: reqMeta.sourceId }), + ...(reqMeta?.workspaceId && { workspaceId: reqMeta.workspaceId }), + ...(destType && { destType }), + ...(reqMeta?.module && { module: reqMeta.module }), + ...(reqMeta?.implementation && { implementation: reqMeta.implementation }), + ...(reqMeta?.feature && { feature: reqMeta.feature }), + }; +}; + +const formLogArgs = (args) => { + let msg = ''; + let otherArgs = []; + args.forEach((arg) => { + if (typeof arg !== 'object') { + msg += ' ' + arg; + return; + } + otherArgs.push(arg); + }); + return [msg, ...otherArgs]; +}; + +/** + * Perform logging operation on logMethod passed + * + * **Good practices**: + * - Do not have more than one array args in logger + * @param {*} logMethod + * - instance method reference + * - The logger should implement all of debug/info/warn/error methods + * @param {*} logArgs + * - the arguments that needs to be passed to logger instance method + */ +const log = (logMethod, logArgs) => { + const [message, ...args] = formLogArgs(logArgs); + const [logInfo, ...otherArgs] = args; + if (logInfo) { + const { metadata, ...otherLogInfoArgs } = logInfo; + if (Array.isArray(metadata)) { + metadata.forEach((m) => { + logMethod( + message, + { + ...getLogMetadata(m), + ...otherLogInfoArgs, + }, + ...otherArgs, + ); + }); + return; + } + logMethod( + message, + { + ...getLogMetadata(metadata), + ...otherLogInfoArgs, + }, + ...otherArgs, + ); + return; + } + logMethod(message); }; const debug = (...args) => { - if (levelDebug >= logLevel) { - console.debug(...args); + const logger = getLogger(); + if (LOGLEVELS.debug <= LOGLEVELS[logLevel]) { + log(logger.debug, args); } }; const info = (...args) => { - if (levelInfo >= logLevel) { - console.info(...args); + const logger = getLogger(); + if (LOGLEVELS.info <= LOGLEVELS[logLevel]) { + log(logger.info, args); } }; const warn = (...args) => { - if (levelWarn >= logLevel) { - console.warn(...args); + const logger = getLogger(); + if (LOGLEVELS.warn <= LOGLEVELS[logLevel]) { + log(logger.warn, args); } }; const error = (...args) => { - if (levelError >= logLevel) { - console.error(...args); + const logger = getLogger(); + if (LOGLEVELS.error <= LOGLEVELS[logLevel]) { + log(logger.error, args); + } +}; + +const requestLog = (identifierMsg, { metadata, requestDetails: { url, body, method } }) => { + const logger = getLogger(); + if (LOGLEVELS[logLevel] === LOGLEVELS.warn) { + const reqLogArgs = [identifierMsg, { metadata, url, body, method }]; + log(logger.warn, reqLogArgs); + } +}; + +const responseLog = ( + identifierMsg, + { metadata, responseDetails: { response: body, status, headers } }, +) => { + const logger = getLogger(); + if (LOGLEVELS[logLevel] === LOGLEVELS.warn) { + const resLogArgs = [identifierMsg, { metadata, body, status, headers }]; + log(logger.warn, resLogArgs); } }; @@ -42,8 +166,11 @@ module.exports = { warn, error, setLogLevel, - levelDebug, - levelInfo, - levelWarn, - levelError, + // levelDebug, + // levelInfo, + // levelWarn, + // levelError, + responseLog, + getLogMetadata, + requestLog, }; diff --git a/src/services/comparator.ts b/src/services/comparator.ts index 511436dfd1..0e28339797 100644 --- a/src/services/comparator.ts +++ b/src/services/comparator.ts @@ -1,5 +1,4 @@ /* eslint-disable class-methods-use-this */ -import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import { DestinationService } from '../interfaces/DestinationService'; import { DeliveryV0Response, @@ -18,6 +17,7 @@ import { import { CommonUtils } from '../util/common'; import stats from '../util/stats'; import tags from '../v0/util/tags'; +import logger from '../logger'; const NS_PER_SEC = 1e9; @@ -204,7 +204,6 @@ export class ComparatorService implements DestinationService { destinationType, version, requestMetadata, - logger, ); const primaryTimeDiff = process.hrtime(primaryStartTime); const primaryTime = primaryTimeDiff[0] * NS_PER_SEC + primaryTimeDiff[1]; @@ -263,7 +262,6 @@ export class ComparatorService implements DestinationService { destinationType, version, requestMetadata, - logger, ); const primaryTimeDiff = process.hrtime(primaryStartTime); const primaryTime = primaryTimeDiff[0] * NS_PER_SEC + primaryTimeDiff[1]; @@ -322,7 +320,6 @@ export class ComparatorService implements DestinationService { destinationType, version, requestMetadata, - {}, ); const primaryTimeDiff = process.hrtime(primaryStartTime); const primaryTime = primaryTimeDiff[0] * NS_PER_SEC + primaryTimeDiff[1]; diff --git a/src/services/destination/__tests__/nativeIntegration.test.ts b/src/services/destination/__tests__/nativeIntegration.test.ts index 85d099d292..3ec3222b9d 100644 --- a/src/services/destination/__tests__/nativeIntegration.test.ts +++ b/src/services/destination/__tests__/nativeIntegration.test.ts @@ -1,4 +1,3 @@ -import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import { FetchHandler } from '../../../helpers/fetchHandlers'; import { ProcessorTransformationOutput, @@ -48,7 +47,6 @@ describe('NativeIntegration Service', () => { destType, version, requestMetadata, - logger, ); expect(resp).toEqual(tresponse); @@ -79,7 +77,6 @@ describe('NativeIntegration Service', () => { destType, version, requestMetadata, - logger, ); const expected = [ diff --git a/src/services/destination/__tests__/postTransformation.test.ts b/src/services/destination/__tests__/postTransformation.test.ts index f961dcbce7..050ae57b7b 100644 --- a/src/services/destination/__tests__/postTransformation.test.ts +++ b/src/services/destination/__tests__/postTransformation.test.ts @@ -1,4 +1,4 @@ -import { MetaTransferObject, ProcessorTransformationRequest } from '../../../types/index'; +import { MetaTransferObject } from '../../../types/index'; import { DestinationPostTransformationService } from '../postTransformation'; import { ProcessorTransformationResponse } from '../../../types'; diff --git a/src/services/destination/cdkV2Integration.ts b/src/services/destination/cdkV2Integration.ts index a649da9154..a91bc5674b 100644 --- a/src/services/destination/cdkV2Integration.ts +++ b/src/services/destination/cdkV2Integration.ts @@ -19,9 +19,8 @@ import { UserDeletionResponse, } from '../../types/index'; import stats from '../../util/stats'; -import { CatchErr, FixMe } from '../../util/types'; +import { CatchErr } from '../../util/types'; import tags from '../../v0/util/tags'; -import { MiscService } from '../misc'; import { DestinationPostTransformationService } from './postTransformation'; export class CDKV2DestinationService implements DestinationService { @@ -56,7 +55,6 @@ export class CDKV2DestinationService implements DestinationService { destinationType: string, _version: string, requestMetadata: NonNullable, - logger: any, ): Promise { // TODO: Change the promise type const respList: ProcessorTransformationResponse[][] = await Promise.all( @@ -68,7 +66,6 @@ export class CDKV2DestinationService implements DestinationService { tags.FEATURES.PROCESSOR, ); metaTo.metadata = event.metadata; - const loggerWithCtx = logger.child({ ...MiscService.getLoggableData(metaTo.errorDetails) }); try { const transformedPayloads: | ProcessorTransformationOutput @@ -76,7 +73,6 @@ export class CDKV2DestinationService implements DestinationService { destinationType, event, tags.FEATURES.PROCESSOR, - loggerWithCtx, requestMetadata, ); stats.increment('event_transform_success', { @@ -115,7 +111,6 @@ export class CDKV2DestinationService implements DestinationService { destinationType: string, _version: string, requestMetadata: NonNullable, - logger: FixMe, ): Promise { const allDestEvents: object = groupBy( events, @@ -131,16 +126,12 @@ export class CDKV2DestinationService implements DestinationService { tags.FEATURES.ROUTER, ); metaTo.metadata = destInputArray[0].metadata; - const loggerWithCtx = logger.child({ - ...MiscService.getLoggableData(metaTo.errorDetails), - }); try { const doRouterTransformationResponse: RouterTransformationResponse[] = await processCdkV2Workflow( destinationType, destInputArray, tags.FEATURES.ROUTER, - loggerWithCtx, requestMetadata, ); return DestinationPostTransformationService.handleRouterTransformSuccessEvents( diff --git a/src/services/destination/nativeIntegration.ts b/src/services/destination/nativeIntegration.ts index 8fd0f09857..38a27ea71d 100644 --- a/src/services/destination/nativeIntegration.ts +++ b/src/services/destination/nativeIntegration.ts @@ -25,7 +25,6 @@ import { } from '../../types/index'; import stats from '../../util/stats'; import tags from '../../v0/util/tags'; -import { MiscService } from '../misc'; import { DestinationPostTransformationService } from './postTransformation'; export class NativeIntegrationDestinationService implements DestinationService { @@ -60,7 +59,6 @@ export class NativeIntegrationDestinationService implements DestinationService { destinationType: string, version: string, requestMetadata: NonNullable, - logger: any, ): Promise { const destHandler = FetchHandler.getDestHandler(destinationType, version); const respList: ProcessorTransformationResponse[][] = await Promise.all( @@ -72,15 +70,10 @@ export class NativeIntegrationDestinationService implements DestinationService { tags.FEATURES.PROCESSOR, ); metaTO.metadata = event.metadata; - const loggerWithCtx = logger.child({ ...MiscService.getLoggableData(metaTO.errorDetails) }); try { const transformedPayloads: | ProcessorTransformationOutput - | ProcessorTransformationOutput[] = await destHandler.process( - event, - requestMetadata, - loggerWithCtx, - ); + | ProcessorTransformationOutput[] = await destHandler.process(event, requestMetadata); return DestinationPostTransformationService.handleProcessorTransformSucessEvents( event, transformedPayloads, @@ -104,7 +97,6 @@ export class NativeIntegrationDestinationService implements DestinationService { destinationType: string, version: string, requestMetadata: NonNullable, - logger: any, ): Promise { const destHandler = FetchHandler.getDestHandler(destinationType, version); const allDestEvents: NonNullable = groupBy( @@ -120,16 +112,9 @@ export class NativeIntegrationDestinationService implements DestinationService { destInputArray[0].metadata?.workspaceId, tags.FEATURES.ROUTER, ); - const loggerWithCtx = logger.child({ - ...MiscService.getLoggableData(metaTO.errorDetails), - }); try { const doRouterTransformationResponse: RouterTransformationResponse[] = - await destHandler.processRouterDest( - cloneDeep(destInputArray), - requestMetadata, - loggerWithCtx, - ); + await destHandler.processRouterDest(cloneDeep(destInputArray), requestMetadata); metaTO.metadata = destInputArray[0].metadata; return DestinationPostTransformationService.handleRouterTransformSuccessEvents( doRouterTransformationResponse, @@ -156,7 +141,6 @@ export class NativeIntegrationDestinationService implements DestinationService { destinationType: string, version: any, requestMetadata: NonNullable, - logger: any, ): RouterTransformationResponse[] { const destHandler = FetchHandler.getDestHandler(destinationType, version); if (!destHandler.batch) { @@ -175,14 +159,10 @@ export class NativeIntegrationDestinationService implements DestinationService { tags.FEATURES.BATCH, ); metaTO.metadatas = events.map((event) => event.metadata); - const loggerWithCtx = logger.child({ - ...MiscService.getLoggableData(metaTO.errorDetails), - }); try { const destBatchedRequests: RouterTransformationResponse[] = destHandler.batch( destEvents, requestMetadata, - loggerWithCtx, ); return destBatchedRequests; } catch (error: any) { diff --git a/src/services/destination/postTransformation.ts b/src/services/destination/postTransformation.ts index 40cee61e66..7ab0d96af8 100644 --- a/src/services/destination/postTransformation.ts +++ b/src/services/destination/postTransformation.ts @@ -19,7 +19,7 @@ import { FixMe } from '../../util/types'; import { generateErrorObject } from '../../v0/util'; import tags from '../../v0/util/tags'; import { ErrorReportingService } from '../errorReporting'; -import { MiscService } from '../misc'; +import logger from '../../logger'; const defaultErrorMessages = { router: '[Router Transform] Error occurred while processing the payload.', @@ -68,7 +68,7 @@ export class DestinationPostTransformationService { error: errObj.message || '[Processor Transform] Error occurred while processing the payload.', statTags: errObj.statTags, } as ProcessorTransformationResponse; - MiscService.logError( + logger.error( errObj.message || '[Processor Transform] Error occurred while processing the payload.', metaTo.errorDetails, ); @@ -109,7 +109,7 @@ export class DestinationPostTransformationService { ...resp.statTags, ...metaTo.errorDetails, }; - MiscService.logError(resp.error || defaultErrorMessages.router, metaTo.errorDetails); + logger.error(resp.error || defaultErrorMessages.router, metaTo.errorDetails); stats.increment('event_transform_failure', metaTo.errorDetails); } else { stats.increment('event_transform_success', { @@ -138,7 +138,7 @@ export class DestinationPostTransformationService { error: errObj.message || defaultErrorMessages.router, statTags: errObj.statTags, } as RouterTransformationResponse; - MiscService.logError(errObj.message || defaultErrorMessages.router, metaTo.errorDetails); + logger.error(errObj.message || defaultErrorMessages.router, metaTo.errorDetails); ErrorReportingService.reportError(error, metaTo.errorContext, resp); stats.increment('event_transform_failure', metaTo.errorDetails); return resp; @@ -156,7 +156,7 @@ export class DestinationPostTransformationService { error: errObj.message || defaultErrorMessages.delivery, statTags: errObj.statTags, } as RouterTransformationResponse; - MiscService.logError(error as string, metaTo.errorDetails); + logger.error(error as string, metaTo.errorDetails); ErrorReportingService.reportError(error, metaTo.errorContext, resp); return resp; } @@ -187,10 +187,7 @@ export class DestinationPostTransformationService { const errObj = generateErrorObject(error, metaTo.errorDetails, false); const metadataArray = metaTo.metadatas; if (!Array.isArray(metadataArray)) { - MiscService.logError( - 'Proxy v1 endpoint error : metadataArray is not an array', - metaTo.errorDetails, - ); + logger.error('Proxy v1 endpoint error : metadataArray is not an array', metaTo.errorDetails); // Panic throw new PlatformError('Proxy v1 endpoint error : metadataArray is not an array'); } @@ -215,7 +212,7 @@ export class DestinationPostTransformationService { authErrorCategory: errObj.authErrorCategory, }), } as DeliveryV1Response; - MiscService.logError(errObj.message, metaTo.errorDetails); + logger.error(errObj.message, metaTo.errorDetails); ErrorReportingService.reportError(error, metaTo.errorContext, resp); return resp; } @@ -233,7 +230,7 @@ export class DestinationPostTransformationService { authErrorCategory: errObj.authErrorCategory, }), } as UserDeletionResponse; - MiscService.logError(errObj.message, metaTo.errorDetails); + logger.error(errObj.message, metaTo.errorDetails); ErrorReportingService.reportError(error, metaTo.errorContext, resp); return resp; } diff --git a/src/services/misc.ts b/src/services/misc.ts index 3df1196c1d..09051edeec 100644 --- a/src/services/misc.ts +++ b/src/services/misc.ts @@ -1,11 +1,10 @@ /* eslint-disable global-require, import/no-dynamic-require */ -import { LoggableExtraData, structuredLogger as logger } from '@rudderstack/integrations-lib'; import fs from 'fs'; import { Context } from 'koa'; import path from 'path'; import { DestHandlerMap } from '../constants/destinationCanonicalNames'; import { getCPUProfile, getHeapProfile } from '../middleware'; -import { ErrorDetailer, Metadata } from '../types'; +import { Metadata } from '../types'; export class MiscService { public static getDestHandler(dest: string, version: string) { @@ -75,21 +74,4 @@ export class MiscService { public static async getHeapProfile() { return getHeapProfile(); } - - public static getLoggableData(errorDetailer: ErrorDetailer): Partial { - return { - ...(errorDetailer?.destinationId && { destinationId: errorDetailer.destinationId }), - ...(errorDetailer?.sourceId && { sourceId: errorDetailer.sourceId }), - ...(errorDetailer?.workspaceId && { workspaceId: errorDetailer.workspaceId }), - ...(errorDetailer?.destType && { destType: errorDetailer.destType }), - ...(errorDetailer?.module && { module: errorDetailer.module }), - ...(errorDetailer?.implementation && { implementation: errorDetailer.implementation }), - ...(errorDetailer?.feature && { feature: errorDetailer.feature }), - }; - } - - public static logError(message: string, errorDetailer: ErrorDetailer) { - const loggableExtraData: Partial = this.getLoggableData(errorDetailer); - logger.errorw(message || '', loggableExtraData); - } } diff --git a/src/services/source/__tests__/nativeIntegration.test.ts b/src/services/source/__tests__/nativeIntegration.test.ts index 77e355fd1a..a2a5af041e 100644 --- a/src/services/source/__tests__/nativeIntegration.test.ts +++ b/src/services/source/__tests__/nativeIntegration.test.ts @@ -1,4 +1,3 @@ -import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import { FetchHandler } from '../../../helpers/fetchHandlers'; import { RudderMessage, SourceTransformationResponse } from '../../../types/index'; import stats from '../../../util/stats'; @@ -44,13 +43,7 @@ describe('NativeIntegration Source Service', () => { }); const service = new NativeIntegrationSourceService(); - const resp = await service.sourceTransformRoutine( - events, - sourceType, - version, - requestMetadata, - logger, - ); + const resp = await service.sourceTransformRoutine(events, sourceType, version, requestMetadata); expect(resp).toEqual(tresponse); @@ -87,13 +80,7 @@ describe('NativeIntegration Source Service', () => { jest.spyOn(stats, 'increment').mockImplementation(() => {}); const service = new NativeIntegrationSourceService(); - const resp = await service.sourceTransformRoutine( - events, - sourceType, - version, - requestMetadata, - logger, - ); + const resp = await service.sourceTransformRoutine(events, sourceType, version, requestMetadata); expect(resp).toEqual(tresponse); diff --git a/src/services/source/nativeIntegration.ts b/src/services/source/nativeIntegration.ts index 2ecfc30066..a4f26d068a 100644 --- a/src/services/source/nativeIntegration.ts +++ b/src/services/source/nativeIntegration.ts @@ -9,8 +9,8 @@ import { import stats from '../../util/stats'; import { FixMe } from '../../util/types'; import tags from '../../v0/util/tags'; -import { MiscService } from '../misc'; import { SourcePostTransformationService } from './postTransformation'; +import logger from '../../logger'; export class NativeIntegrationSourceService implements SourceService { public getTags(): MetaTransferObject { @@ -32,23 +32,23 @@ export class NativeIntegrationSourceService implements SourceService { version: string, // eslint-disable-next-line @typescript-eslint/no-unused-vars _requestMetadata: NonNullable, - logger: FixMe, ): Promise { const sourceHandler = FetchHandler.getSourceHandler(sourceType, version); const metaTO = this.getTags(); - const loggerWithCtx = logger.child({ ...MiscService.getLoggableData(metaTO.errorDetails) }); const respList: SourceTransformationResponse[] = await Promise.all( sourceEvents.map(async (sourceEvent) => { try { const respEvents: RudderMessage | RudderMessage[] | SourceTransformationResponse = - await sourceHandler.process(sourceEvent, loggerWithCtx); + await sourceHandler.process(sourceEvent); return SourcePostTransformationService.handleSuccessEventsSource(respEvents); } catch (error: FixMe) { stats.increment('source_transform_errors', { source: sourceType, version, }); - logger.debug('Error during source Transform', error); + logger.debug(`Error during source Transform: ${error}`, { + ...logger.getLogMetadata(metaTO.errorDetails), + }); return SourcePostTransformationService.handleFailureEventsSource(error, metaTO); } }), diff --git a/src/util/errorNotifier/bugsnag.js b/src/util/errorNotifier/bugsnag.js index ef01c58730..a6a22655ad 100644 --- a/src/util/errorNotifier/bugsnag.js +++ b/src/util/errorNotifier/bugsnag.js @@ -22,7 +22,7 @@ const { NetworkInstrumentationError, } = require('@rudderstack/integrations-lib'); const { FilteredEventsError } = require('../../v0/util/errorTypes'); -const { logger } = require('../../logger'); +const logger = require('../../logger'); const pkg = require('../../../package.json'); const { diff --git a/src/util/errorNotifier/default.js b/src/util/errorNotifier/default.js index 28557a22f2..18f04f055d 100644 --- a/src/util/errorNotifier/default.js +++ b/src/util/errorNotifier/default.js @@ -3,7 +3,7 @@ const logger = require('../../logger'); function init() {} function notify(err, context, metadata) { - logger.error(err, context, metadata); + logger.error(err, { context, metadata }); } module.exports = { diff --git a/src/util/prometheus.js b/src/util/prometheus.js index bc4c6f2eb9..72f424d39a 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -567,6 +567,11 @@ class Prometheus { 'statusCode', 'requestMethod', 'module', + 'workspaceId', + 'destinationId', + 'module', + 'implementation', + 'sourceId', ], }, @@ -631,7 +636,18 @@ class Prometheus { name: 'outgoing_request_latency', help: 'Outgoing HTTP requests duration in seconds', type: 'histogram', - labelNames: ['feature', 'destType', 'endpointPath', 'requestMethod', 'module'], + labelNames: [ + 'feature', + 'destType', + 'endpointPath', + 'requestMethod', + 'module', + 'workspaceId', + 'destinationId', + 'module', + 'implementation', + 'sourceId', + ], }, { name: 'http_request_duration', diff --git a/src/util/redis/redisConnector.js b/src/util/redis/redisConnector.js index 84d578d3b3..7dc20a305d 100644 --- a/src/util/redis/redisConnector.js +++ b/src/util/redis/redisConnector.js @@ -1,7 +1,7 @@ const Redis = require('ioredis'); const { RedisError } = require('@rudderstack/integrations-lib'); -const log = require('../../logger'); const stats = require('../stats'); +const logger = require('../../logger'); const timeoutPromise = () => new Promise((_, reject) => { @@ -29,13 +29,13 @@ const RedisDB = { stats.increment('redis_error', { operation: 'redis_down', }); - log.error(`Redis is down at ${this.host}:${this.port}`); + logger.error(`Redis is down at ${this.host}:${this.port}`); return false; // stop retrying }, tls: {}, }); this.client.on('ready', () => { - log.info(`Connected to redis at ${this.host}:${this.port}`); + logger.info(`Connected to redis at ${this.host}:${this.port}`); }); } }, @@ -89,7 +89,7 @@ const RedisDB = { stats.increment('redis_error', { operation: 'get', }); - log.error(`Error getting value from Redis: ${e}`); + logger.error(`Error getting value from Redis: ${e}`); throw new RedisError(`Error getting value from Redis: ${e}`); } }, @@ -124,13 +124,13 @@ const RedisDB = { stats.increment('redis_error', { operation: 'set', }); - log.error(`Error setting value in Redis due ${e}`); + logger.error(`Error setting value in Redis due ${e}`); throw new RedisError(`Error setting value in Redis due ${e}`); } }, async disconnect() { if (process.env.USE_REDIS_DB && process.env.USE_REDIS_DB !== 'false') { - log.info(`Disconnecting from redis at ${this.host}:${this.port}`); + logger.info(`Disconnecting from redis at ${this.host}:${this.port}`); this.client.disconnect(); } }, diff --git a/src/util/redis/redisConnector.test.js b/src/util/redis/redisConnector.test.js index 7cf2ccbbcf..659b32925b 100644 --- a/src/util/redis/redisConnector.test.js +++ b/src/util/redis/redisConnector.test.js @@ -2,8 +2,8 @@ const fs = require('fs'); const path = require('path'); const version = 'v0'; const { RedisDB } = require('./redisConnector'); -const { structuredLogger: logger } = require('@rudderstack/integrations-lib'); jest.mock('ioredis', () => require('../../../test/__mocks__/redis')); + const sourcesList = ['shopify']; process.env.USE_REDIS_DB = 'true'; @@ -55,7 +55,7 @@ describe(`Source Tests`, () => { data.forEach((dataPoint, index) => { it(`${index}. ${source} - ${dataPoint.description}`, async () => { try { - const output = await transformer.process(dataPoint.input, logger); + const output = await transformer.process(dataPoint.input); expect(output).toEqual(dataPoint.output); } catch (error) { expect(error.message).toEqual(dataPoint.output.error); diff --git a/src/util/utils.js b/src/util/utils.js index d74603dd7a..1ac70b9541 100644 --- a/src/util/utils.js +++ b/src/util/utils.js @@ -170,7 +170,8 @@ function processInfo() { } function logProcessInfo() { - logger.error(`Process info: `, util.inspect(processInfo(), false, null, true)); + const inspectedInfo = util.inspect(processInfo(), false, Infinity, true); + logger.error(`Process info: ${inspectedInfo}`); } // stringLiterals expected to be an array of strings. A line in trace should contain diff --git a/src/v0/destinations/braze/util.js b/src/v0/destinations/braze/util.js index f131c40f5f..4253619d33 100644 --- a/src/v0/destinations/braze/util.js +++ b/src/v0/destinations/braze/util.js @@ -1,7 +1,7 @@ /* eslint-disable */ const _ = require('lodash'); const get = require('get-value'); -const { structuredLogger: logger } = require('@rudderstack/integrations-lib'); +const logger = require('../../../logger'); const stats = require('../../../util/stats'); const { handleHttpRequest } = require('../../../adapters/network'); const { diff --git a/src/v0/destinations/campaign_manager/transform.js b/src/v0/destinations/campaign_manager/transform.js index ef208df5d1..7f007ef633 100644 --- a/src/v0/destinations/campaign_manager/transform.js +++ b/src/v0/destinations/campaign_manager/transform.js @@ -256,8 +256,7 @@ const batchEvents = (eventChunksArray) => { return batchedResponseList; }; -const processRouterDest = async (inputs, reqMetadata, logger) => { - logger.debug(`Transformation router request received with size ${inputs.length}`); +const processRouterDest = async (inputs, reqMetadata) => { const batchErrorRespList = []; const eventChunksArray = []; const { destination } = inputs[0]; diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/config.js b/src/v0/destinations/google_adwords_enhanced_conversions/config.js index 8d194655f7..e8f486fb7a 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/config.js +++ b/src/v0/destinations/google_adwords_enhanced_conversions/config.js @@ -16,4 +16,5 @@ module.exports = { BASE_ENDPOINT, hashAttributes, CONVERSION_ACTION_ID_CACHE_TTL, + destType: 'google_adwords_enhanced_conversions', }; diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js index f7ac660f53..d82349c04d 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js +++ b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js @@ -4,8 +4,9 @@ const { NetworkError, NetworkInstrumentationError } = require('@rudderstack/inte const SqlString = require('sqlstring'); const { prepareProxyRequest, handleHttpRequest } = require('../../../adapters/network'); const { isHttpStatusSuccess, getAuthErrCategoryFromStCode } = require('../../util/index'); -const { CONVERSION_ACTION_ID_CACHE_TTL } = require('./config'); +const { CONVERSION_ACTION_ID_CACHE_TTL, destType } = require('./config'); const Cache = require('../../util/cache'); +const logger = require('../../../logger'); const conversionActionIdCache = new Cache(CONVERSION_ACTION_ID_CACHE_TTL); @@ -27,7 +28,7 @@ const ERROR_MSG_PATH = 'response[0].error.message'; * @returns */ -const getConversionActionId = async (method, headers, params) => { +const getConversionActionId = async ({ method, headers, params, metadata }) => { const conversionActionIdKey = sha256(params.event + params.customerId).toString(); return conversionActionIdCache.get(conversionActionIdKey, async () => { const queryString = SqlString.format( @@ -37,8 +38,13 @@ const getConversionActionId = async (method, headers, params) => { const data = { query: queryString, }; + const searchStreamEndpoint = `${BASE_ENDPOINT}/${params.customerId}/googleAds:searchStream`; + logger.requestLog(`[${destType.toUpperCase()}] get conversion action id request`, { + metadata, + requestDetails: { url: searchStreamEndpoint, body: data, method }, + }); const requestBody = { - url: `${BASE_ENDPOINT}/${params.customerId}/googleAds:searchStream`, + url: searchStreamEndpoint, data, headers, method, @@ -52,21 +58,31 @@ const getConversionActionId = async (method, headers, params) => { endpointPath: `/googleAds:searchStream`, requestMethod: 'POST', module: 'dataDelivery', + metadata, }, ); - if (!isHttpStatusSuccess(gaecConversionActionIdResponse.status)) { + const { status, response, headers: responseHeaders } = gaecConversionActionIdResponse; + logger.responseLog(`[${destType.toUpperCase()}] get conversion action id response`, { + metadata, + responseDetails: { + response, + status, + headers: responseHeaders, + }, + }); + if (!isHttpStatusSuccess(status)) { throw new NetworkError( `"${JSON.stringify( get(gaecConversionActionIdResponse, ERROR_MSG_PATH, '') ? get(gaecConversionActionIdResponse, ERROR_MSG_PATH, '') - : gaecConversionActionIdResponse.response, + : response, )} during Google_adwords_enhanced_conversions response transformation"`, - gaecConversionActionIdResponse.status, + status, { - [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(gaecConversionActionIdResponse.status), + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), }, - gaecConversionActionIdResponse.response, - getAuthErrCategoryFromStCode(gaecConversionActionIdResponse.status), + response, + getAuthErrCategoryFromStCode(status), ); } const conversionActionId = get( @@ -90,23 +106,41 @@ const getConversionActionId = async (method, headers, params) => { * @returns */ const ProxyRequest = async (request) => { - const { body, method, endpoint, params } = request; + const { body, method, endpoint, params, metadata } = request; const { headers } = request; - const conversionActionId = await getConversionActionId(method, headers, params); + const conversionActionId = await getConversionActionId({ method, headers, params, metadata }); set( body.JSON, 'conversionAdjustments[0].conversionAction', `customers/${params.customerId}/conversionActions/${conversionActionId}`, ); + logger.requestLog(`[${destType.toUpperCase()}] conversion enhancement request`, { + metadata, + requestDetails: { url: endpoint, body: body.JSON, method }, + }); const requestBody = { url: endpoint, data: body.JSON, headers, method }; - const { httpResponse: response } = await handleHttpRequest('constructor', requestBody, { - destType: 'google_adwords_enhanced_conversions', - feature: 'proxy', - endpointPath: `/googleAds:uploadOfflineUserData`, - requestMethod: 'POST', - module: 'dataDelivery', + const { httpResponse: response, processedResponse } = await handleHttpRequest( + 'constructor', + requestBody, + { + destType: 'google_adwords_enhanced_conversions', + feature: 'proxy', + endpointPath: `/googleAds:uploadOfflineUserData`, + requestMethod: 'POST', + module: 'dataDelivery', + metadata, + }, + ); + const { response: processedResp, status, headers: responseHeaders } = processedResponse; + logger.responseLog(`[${destType.toUpperCase()}] conversion enhancement response`, { + metadata, + responseDetails: { + response: processedResp, + status, + headers: responseHeaders, + }, }); return response; }; diff --git a/src/v0/destinations/google_adwords_offline_conversions/config.js b/src/v0/destinations/google_adwords_offline_conversions/config.js index f065be946c..6eec1068a6 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/config.js +++ b/src/v0/destinations/google_adwords_offline_conversions/config.js @@ -48,6 +48,7 @@ const consentConfigMap = { }; module.exports = { + destType: 'google_adwords_offline_conversions', trackClickConversionsMapping: MAPPING_CONFIG[CONFIG_CATEGORIES.TRACK_CLICK_CONVERSIONS_CONFIG.name], trackCallConversionsMapping: MAPPING_CONFIG[CONFIG_CATEGORIES.TRACK_CALL_CONVERSIONS_CONFIG.name], diff --git a/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js b/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js index 5541fd6e1e..51bc57d176 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js +++ b/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js @@ -6,7 +6,7 @@ const { NetworkInstrumentationError, NetworkError, } = require('@rudderstack/integrations-lib'); -const { prepareProxyRequest, httpSend, httpPOST } = require('../../../adapters/network'); +const { prepareProxyRequest, httpPOST, handleHttpRequest } = require('../../../adapters/network'); const { isHttpStatusSuccess, getHashFromArray, @@ -15,31 +15,49 @@ const { } = require('../../util'); const { getConversionActionId } = require('./utils'); const Cache = require('../../util/cache'); -const { CONVERSION_CUSTOM_VARIABLE_CACHE_TTL, SEARCH_STREAM } = require('./config'); +const { CONVERSION_CUSTOM_VARIABLE_CACHE_TTL, SEARCH_STREAM, destType } = require('./config'); const { processAxiosResponse, getDynamicErrorType, } = require('../../../adapters/utils/networkUtils'); const tags = require('../../util/tags'); +const logger = require('../../../logger'); const conversionCustomVariableCache = new Cache(CONVERSION_CUSTOM_VARIABLE_CACHE_TTL); -const createJob = async (endpoint, headers, payload) => { +const createJob = async ({ endpoint, headers, payload, metadata }) => { const endPoint = `${endpoint}:create`; + logger.requestLog(`[${destType.toUpperCase()}] job creation request`, { + metadata, + requestDetails: { + url: endpoint, + body: payload, + method: 'post', + }, + }); let createJobResponse = await httpPOST( endPoint, payload, { headers }, { - destType: 'google_adwords_offline_conversions', + destType, feature: 'proxy', endpointPath: `/create`, requestMethod: 'POST', module: 'dataDelivery', + metadata, }, ); createJobResponse = processAxiosResponse(createJobResponse); - const { response, status } = createJobResponse; + const { response, status, headers: responseHeaders } = createJobResponse; + logger.responseLog(`[${destType.toUpperCase()}] create job`, { + metadata, + responseDetails: { + headers: responseHeaders, + status, + response, + }, + }); if (!isHttpStatusSuccess(status)) { throw new AbortedError( `[Google Ads Offline Conversions]:: ${response?.error?.message} during google_ads_offline_store_conversions Job Creation`, @@ -51,8 +69,16 @@ const createJob = async (endpoint, headers, payload) => { return response.resourceName.split('/')[3]; }; -const addConversionToJob = async (endpoint, headers, jobId, payload) => { +const addConversionToJob = async ({ endpoint, headers, jobId, payload, metadata }) => { const endPoint = `${endpoint}/${jobId}:addOperations`; + logger.requestLog(`[${destType.toUpperCase()}] add conversion to job request`, { + metadata, + requestDetails: { + url: endpoint, + body: payload, + method: 'post', + }, + }); let addConversionToJobResponse = await httpPOST( endPoint, payload, @@ -63,23 +89,42 @@ const addConversionToJob = async (endpoint, headers, jobId, payload) => { endpointPath: `/addOperations`, requestMethod: 'POST', module: 'dataDelivery', + metadata, }, ); addConversionToJobResponse = processAxiosResponse(addConversionToJobResponse); - if (!isHttpStatusSuccess(addConversionToJobResponse.status)) { + const { response, status, headers: responseHeaders } = addConversionToJobResponse; + logger.responseLog(`[${destType.toUpperCase()}] add conversion to job`, { + metadata, + responseDetails: { + response, + status, + headers: responseHeaders, + }, + }); + if (!isHttpStatusSuccess(status)) { throw new AbortedError( - `[Google Ads Offline Conversions]:: ${addConversionToJobResponse.response?.error?.message} during google_ads_offline_store_conversions Add Conversion`, - addConversionToJobResponse.status, - addConversionToJobResponse.response, + `[Google Ads Offline Conversions]:: ${response?.error?.message} during google_ads_offline_store_conversions Add Conversion`, + status, + response, getAuthErrCategoryFromStCode(get(addConversionToJobResponse, 'status')), ); } return true; }; -const runTheJob = async (endpoint, headers, payload, jobId) => { +const runTheJob = async ({ endpoint, headers, payload, jobId, metadata }) => { const endPoint = `${endpoint}/${jobId}:run`; - const executeJobResponse = await httpPOST( + logger.requestLog(`[${destType.toUpperCase()}] run job request`, { + metadata, + requestDetails: { + body: payload, + method: 'POST', + url: endPoint, + }, + }); + const { httpResponse: executeJobResponse, processedResponse } = await handleHttpRequest( + 'post', endPoint, payload, { headers }, @@ -89,8 +134,18 @@ const runTheJob = async (endpoint, headers, payload, jobId) => { endpointPath: `/run`, requestMethod: 'POST', module: 'dataDelivery', + metadata, }, ); + const { headers: responseHeaders, response, status } = processedResponse; + logger.responseLog(`[${destType.toUpperCase()}] run job`, { + metadata, + responseDetails: { + response, + status, + responseHeaders, + }, + }); return executeJobResponse; }; @@ -102,7 +157,7 @@ const runTheJob = async (endpoint, headers, payload, jobId) => { * @param {*} headers * @returns */ -const getConversionCustomVariable = async (headers, params) => { +const getConversionCustomVariable = async ({ headers, params, metadata }) => { const conversionCustomVariableKey = sha256(params.customerId).toString(); return conversionCustomVariableCache.get(conversionCustomVariableKey, async () => { const data = { @@ -112,23 +167,41 @@ const getConversionCustomVariable = async (headers, params) => { const requestOptions = { headers, }; + logger.requestLog(`[${destType.toUpperCase()}] get conversion custom variable request`, { + metadata, + requestDetails: { + url: endpoint, + body: data, + method: 'post', + }, + }); let searchStreamResponse = await httpPOST(endpoint, data, requestOptions, { destType: 'google_adwords_offline_conversions', feature: 'proxy', endpointPath: `/searchStream`, requestMethod: 'POST', module: 'dataDelivery', + metadata, }); searchStreamResponse = processAxiosResponse(searchStreamResponse); - if (!isHttpStatusSuccess(searchStreamResponse.status)) { + const { response, status, headers: responseHeaders } = searchStreamResponse; + logger.responseLog(`[${destType.toUpperCase()}] get conversion custom variable`, { + metadata, + responseDetails: { + response, + status, + headers: responseHeaders, + }, + }); + if (!isHttpStatusSuccess(status)) { throw new NetworkError( - `[Google Ads Offline Conversions]:: ${searchStreamResponse?.response?.[0]?.error?.message} during google_ads_offline_conversions response transformation`, - searchStreamResponse.status, + `[Google Ads Offline Conversions]:: ${response?.[0]?.error?.message} during google_ads_offline_conversions response transformation`, + status, { - [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(searchStreamResponse.status), + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), }, - searchStreamResponse?.response || searchStreamResponse, - getAuthErrCategoryFromStCode(searchStreamResponse.status), + response || searchStreamResponse, + getAuthErrCategoryFromStCode(status), ); } const conversionCustomVariable = get(searchStreamResponse, 'response.0.results'); @@ -195,37 +268,53 @@ const isValidCustomVariables = (customVariables) => { * @returns */ const ProxyRequest = async (request) => { - const { method, endpoint, headers, params, body } = request; + const { method, endpoint, headers, params, body, metadata } = request; if (body.JSON?.isStoreConversion) { - const firstResponse = await createJob(endpoint, headers, body.JSON.createJobPayload); + const firstResponse = await createJob({ + endpoint, + headers, + payload: body.JSON.createJobPayload, + metadata, + }); const addPayload = body.JSON.addConversionPayload; // Mapping Conversion Action - const conversionId = await getConversionActionId(headers, params); + const conversionId = await getConversionActionId({ headers, params, metadata }); if (Array.isArray(addPayload.operations)) { addPayload.operations.forEach((operation) => { set(operation, 'create.transaction_attribute.conversion_action', conversionId); }); } - await addConversionToJob(endpoint, headers, firstResponse, addPayload); - const thirdResponse = await runTheJob( + await addConversionToJob({ endpoint, headers, - body.JSON.executeJobPayload, - firstResponse, - ); + jobId: firstResponse, + payload: addPayload, + metadata, + }); + const thirdResponse = await runTheJob({ + endpoint, + headers, + payload: body.JSON.executeJobPayload, + jobId: firstResponse, + metadata, + }); return thirdResponse; } // fetch conversionAction // httpPOST -> myAxios.post() if (params?.event) { - const conversionActionId = await getConversionActionId(headers, params); + const conversionActionId = await getConversionActionId({ headers, params, metadata }); set(body.JSON, 'conversions.0.conversionAction', conversionActionId); } // customVariables would be undefined in case of Store Conversions if (isValidCustomVariables(params.customVariables)) { // fetch all conversion custom variable in google ads - let conversionCustomVariable = await getConversionCustomVariable(headers, params); + let conversionCustomVariable = await getConversionCustomVariable({ + headers, + params, + metadata, + }); // convert it into hashMap conversionCustomVariable = getConversionCustomVariableHashMap(conversionCustomVariable); @@ -249,16 +338,29 @@ const ProxyRequest = async (request) => { set(body.JSON, 'conversions.0.customVariables', resultantCustomVariables); } } - const requestBody = { url: endpoint, data: body.JSON, headers, method }; - const response = await httpSend(requestBody, { + logger.requestLog(`[${destType.toUpperCase()}] offline conversion creation request`, { + metadata, + requestDetails: { url: requestBody.url, body: requestBody.data, method }, + }); + const { httpResponse, processedResponse } = await handleHttpRequest('constructor', requestBody, { feature: 'proxy', destType: 'gogole_adwords_offline_conversions', endpointPath: `/proxy`, requestMethod: 'POST', module: 'dataDelivery', + metadata, + }); + const { headers: responseHeaders, status, response } = processedResponse; + logger.responseLog(`[${destType.toUpperCase()}] deliver event to destination`, { + metadata, + responseDetails: { + response, + headers: responseHeaders, + status, + }, }); - return response; + return httpResponse; }; const responseHandler = (responseParams) => { diff --git a/src/v0/destinations/google_adwords_offline_conversions/utils.js b/src/v0/destinations/google_adwords_offline_conversions/utils.js index dfa892a769..bf1773d450 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/utils.js +++ b/src/v0/destinations/google_adwords_offline_conversions/utils.js @@ -30,11 +30,13 @@ const { CLICK_CONVERSION, trackCallConversionsMapping, consentConfigMap, + destType, } = require('./config'); const { processAxiosResponse } = require('../../../adapters/utils/networkUtils'); const Cache = require('../../util/cache'); const helper = require('./helper'); const { finaliseConsent } = require('../../util/googleUtils'); +const logger = require('../../../logger'); const conversionActionIdCache = new Cache(CONVERSION_ACTION_ID_CACHE_TTL); @@ -55,7 +57,7 @@ const validateDestinationConfig = ({ Config }) => { * @param {*} headers * @returns */ -const getConversionActionId = async (headers, params) => { +const getConversionActionId = async ({ headers, params, metadata }) => { const conversionActionIdKey = sha256(params.event + params.customerId).toString(); return conversionActionIdCache.get(conversionActionIdKey, async () => { const queryString = SqlString.format( @@ -69,21 +71,39 @@ const getConversionActionId = async (headers, params) => { const requestOptions = { headers, }; + logger.requestLog(`[${destType.toUpperCase()}] get conversion action id request`, { + metadata, + requestDetails: { + url: endpoint, + body: data, + method: 'post', + }, + }); let searchStreamResponse = await httpPOST(endpoint, data, requestOptions, { destType: 'google_adwords_offline_conversions', feature: 'transformation', endpointPath: `/googleAds:searchStream`, requestMethod: 'POST', module: 'dataDelivery', + metadata, }); searchStreamResponse = processAxiosResponse(searchStreamResponse); - if (!isHttpStatusSuccess(searchStreamResponse.status)) { + const { response, status, headers: responseHeaders } = searchStreamResponse; + logger.responseLog(`[${destType.toUpperCase()}] get conversion action id response`, { + metadata, + responseDetails: { + response, + status, + headers: responseHeaders, + }, + }); + if (!isHttpStatusSuccess(status)) { throw new AbortedError( `[Google Ads Offline Conversions]:: ${JSON.stringify( - searchStreamResponse.response, + response, )} during google_ads_offline_conversions response transformation`, - searchStreamResponse.status, - searchStreamResponse.response, + status, + response, getAuthErrCategoryFromStCode(get(searchStreamResponse, 'status')), ); } diff --git a/src/v0/destinations/google_adwords_remarketing_lists/config.js b/src/v0/destinations/google_adwords_remarketing_lists/config.js index 0f08b3866d..f8983699c6 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/config.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/config.js @@ -29,4 +29,5 @@ module.exports = { offlineDataJobsMapping: MAPPING_CONFIG[CONFIG_CATEGORIES.AUDIENCE_LIST.name], addressInfoMapping: MAPPING_CONFIG[CONFIG_CATEGORIES.ADDRESSINFO.name], consentConfigMap, + destType: 'google_adwords_remarketing_lists', }; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js b/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js index 3045c1713f..82fb62b74e 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js @@ -1,12 +1,14 @@ const { NetworkError } = require('@rudderstack/integrations-lib'); -const { httpSend, prepareProxyRequest } = require('../../../adapters/network'); +const { prepareProxyRequest, handleHttpRequest } = require('../../../adapters/network'); const { isHttpStatusSuccess, getAuthErrCategoryFromStCode } = require('../../util/index'); +const logger = require('../../../logger'); const { processAxiosResponse, getDynamicErrorType, } = require('../../../adapters/utils/networkUtils'); const tags = require('../../util/tags'); +const { destType } = require('./config'); /** * This function helps to create a offlineUserDataJobs * @param endpoint @@ -18,7 +20,7 @@ const tags = require('../../util/tags'); * ref: https://developers.google.com/google-ads/api/rest/reference/rest/v15/CustomerMatchUserListMetadata */ -const createJob = async (endpoint, headers, method, params) => { +const createJob = async ({ endpoint, headers, method, params, metadata }) => { const jobCreatingUrl = `${endpoint}:create`; const customerMatchUserListMetadata = { userList: `customers/${params.customerId}/userLists/${params.listId}`, @@ -37,14 +39,31 @@ const createJob = async (endpoint, headers, method, params) => { headers, method, }; - const response = await httpSend(jobCreatingRequest, { - destType: 'google_adwords_remarketing_lists', - feature: 'proxy', - endpointPath: '/customers/create', - requestMethod: 'POST', - module: 'dataDelivery', + logger.requestLog(`[${destType.toUpperCase()}] job creation request`, { + metadata, + requestDetails: { + url: jobCreatingRequest.url, + body: jobCreatingRequest.data, + method: jobCreatingRequest.method, + }, }); - return response; + const { httpResponse, processedResponse } = await handleHttpRequest( + 'constructor', + jobCreatingRequest, + { + destType: 'google_adwords_remarketing_lists', + feature: 'proxy', + endpointPath: '/customers/create', + requestMethod: 'POST', + module: 'dataDelivery', + metadata, + }, + ); + logger.responseLog(`[${destType.toUpperCase()}] job creation response`, { + metadata, + responseDetails: processedResponse, + }); + return httpResponse; }; /** * This function helps to put user details in a offlineUserDataJobs @@ -55,7 +74,7 @@ const createJob = async (endpoint, headers, method, params) => { * @param body */ -const addUserToJob = async (endpoint, headers, method, jobId, body) => { +const addUserToJob = async ({ endpoint, headers, method, jobId, body, metadata }) => { const jobAddingUrl = `${endpoint}/${jobId}:addOperations`; const secondRequest = { url: jobAddingUrl, @@ -63,12 +82,29 @@ const addUserToJob = async (endpoint, headers, method, jobId, body) => { headers, method, }; - const response = await httpSend(secondRequest, { - destType: 'google_adwords_remarketing_lists', - feature: 'proxy', - endpointPath: '/addOperations', - requestMethod: 'POST', - module: 'dataDelivery', + logger.requestLog(`[${destType.toUpperCase()}] add user to job request`, { + metadata, + requestDetails: { + url: secondRequest.url, + body: secondRequest.data, + method: secondRequest.method, + }, + }); + const { httpResponse: response, processedResponse } = await handleHttpRequest( + 'constructor', + secondRequest, + { + destType: 'google_adwords_remarketing_lists', + feature: 'proxy', + endpointPath: '/addOperations', + requestMethod: 'POST', + module: 'dataDelivery', + metadata, + }, + ); + logger.responseLog(`[${destType.toUpperCase()}] add user to job response`, { + metadata, + responseDetails: processedResponse, }); return response; }; @@ -80,19 +116,35 @@ const addUserToJob = async (endpoint, headers, method, jobId, body) => { * @param method * @param jobId */ -const runTheJob = async (endpoint, headers, method, jobId) => { +const runTheJob = async ({ endpoint, headers, method, jobId, metadata }) => { const jobRunningUrl = `${endpoint}/${jobId}:run`; const thirdRequest = { url: jobRunningUrl, headers, method, }; - const response = await httpSend(thirdRequest, { - destType: 'google_adwords_remarketing_lists', - feature: 'proxy', - endpointPath: '/run', - requestMethod: 'POST', - module: 'dataDelivery', + logger.requestLog(`[${destType.toUpperCase()}] run job request`, { + metadata, + requestDetails: { + url: thirdRequest.url, + body: thirdRequest.data, + method: thirdRequest.method, + }, + }); + const { httpResponse: response, processedResponse } = await handleHttpRequest( + 'constructor', + thirdRequest, + { + destType: 'google_adwords_remarketing_lists', + feature: 'proxy', + endpointPath: '/run', + requestMethod: 'POST', + module: 'dataDelivery', + }, + ); + logger.responseLog(`[${destType.toUpperCase()}] run job response`, { + metadata, + responseDetails: processedResponse, }); return response; }; @@ -104,12 +156,12 @@ const runTheJob = async (endpoint, headers, method, jobId) => { * @returns */ const gaAudienceProxyRequest = async (request) => { - const { body, method, params, endpoint } = request; + const { body, method, params, endpoint, metadata } = request; const { headers } = request; // step1: offlineUserDataJobs creation - const firstResponse = await createJob(endpoint, headers, method, params); + const firstResponse = await createJob({ endpoint, headers, method, params, metadata }); if (!firstResponse.success && !isHttpStatusSuccess(firstResponse?.response?.status)) { return firstResponse; } @@ -126,7 +178,7 @@ const gaAudienceProxyRequest = async (request) => { if (firstResponse?.response?.data?.resourceName) // eslint-disable-next-line prefer-destructuring jobId = firstResponse.response.data.resourceName.split('/')[3]; - const secondResponse = await addUserToJob(endpoint, headers, method, jobId, body); + const secondResponse = await addUserToJob({ endpoint, headers, method, jobId, body, metadata }); if (!secondResponse.success && !isHttpStatusSuccess(secondResponse?.response?.status)) { return secondResponse; } @@ -139,7 +191,7 @@ const gaAudienceProxyRequest = async (request) => { } // step3: running the job - const thirdResponse = await runTheJob(endpoint, headers, method, jobId); + const thirdResponse = await runTheJob({ endpoint, headers, method, jobId, metadata }); return thirdResponse; }; diff --git a/src/v0/destinations/klaviyo/config.js b/src/v0/destinations/klaviyo/config.js index 5c15804e14..d8583ab9cb 100644 --- a/src/v0/destinations/klaviyo/config.js +++ b/src/v0/destinations/klaviyo/config.js @@ -57,6 +57,7 @@ const LIST_CONF = { }; const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); +const destType = 'klaviyo'; module.exports = { BASE_ENDPOINT, @@ -68,4 +69,5 @@ module.exports = { ecomEvents, eventNameMapping, jsonNameMapping, + destType, }; diff --git a/src/v0/destinations/klaviyo/transform.js b/src/v0/destinations/klaviyo/transform.js index a0fe3e81a7..09e75919f9 100644 --- a/src/v0/destinations/klaviyo/transform.js +++ b/src/v0/destinations/klaviyo/transform.js @@ -51,7 +51,10 @@ const { JSON_MIME_TYPE, HTTP_STATUS_CODES } = require('../../util/constant'); * @param {*} reqMetadata * @returns */ -const identifyRequestHandler = async (message, category, destination, reqMetadata) => { +const identifyRequestHandler = async ( + { message, category, destination, metadata }, + reqMetadata, +) => { // If listId property is present try to subscribe/member user in list const { privateApiKey, enforceEmailAsPrimary, listId, flattenProperties } = destination.Config; const mappedToDestination = get(message, MappedToDestinationKey); @@ -109,11 +112,12 @@ const identifyRequestHandler = async (message, category, destination, reqMetadat }, }; - const { profileId, response, statusCode } = await getIdFromNewOrExistingProfile( + const { profileId, response, statusCode } = await getIdFromNewOrExistingProfile({ endpoint, payload, requestOptions, - ); + metadata, + }); const responseMap = { profileUpdateResponse: profileUpdateResponseBuilder( @@ -271,7 +275,8 @@ const groupRequestHandler = (message, category, destination) => { }; // Main event processor using specific handler funcs -const processEvent = async (message, destination, reqMetadata) => { +const processEvent = async (event, reqMetadata) => { + const { message, destination, metadata } = event; if (!message.type) { throw new InstrumentationError('Event type is required'); } @@ -285,7 +290,10 @@ const processEvent = async (message, destination, reqMetadata) => { switch (messageType) { case EventType.IDENTIFY: category = CONFIG_CATEGORIES.IDENTIFY; - response = await identifyRequestHandler(message, category, destination, reqMetadata); + response = await identifyRequestHandler( + { message, category, destination, metadata }, + reqMetadata, + ); break; case EventType.SCREEN: case EventType.TRACK: @@ -303,7 +311,7 @@ const processEvent = async (message, destination, reqMetadata) => { }; const process = async (event, reqMetadata) => { - const result = await processEvent(event.message, event.destination, reqMetadata); + const result = await processEvent(event, reqMetadata); return result; }; diff --git a/src/v0/destinations/klaviyo/util.js b/src/v0/destinations/klaviyo/util.js index df2dbb4712..4db59cfb05 100644 --- a/src/v0/destinations/klaviyo/util.js +++ b/src/v0/destinations/klaviyo/util.js @@ -2,6 +2,7 @@ const { defaultRequestConfig } = require('rudder-transformer-cdk/build/utils'); const lodash = require('lodash'); const { NetworkError, InstrumentationError } = require('@rudderstack/integrations-lib'); const { WhiteListedTraits } = require('../../../constants'); +const logger = require('../../../logger'); const { constructPayload, @@ -17,7 +18,13 @@ const tags = require('../../util/tags'); const { handleHttpRequest } = require('../../../adapters/network'); const { JSON_MIME_TYPE, HTTP_STATUS_CODES } = require('../../util/constant'); const { getDynamicErrorType } = require('../../../adapters/utils/networkUtils'); -const { BASE_ENDPOINT, MAPPING_CONFIG, CONFIG_CATEGORIES, MAX_BATCH_SIZE } = require('./config'); +const { + BASE_ENDPOINT, + MAPPING_CONFIG, + CONFIG_CATEGORIES, + MAX_BATCH_SIZE, + destType, +} = require('./config'); const REVISION_CONSTANT = '2023-02-22'; @@ -32,10 +39,18 @@ const REVISION_CONSTANT = '2023-02-22'; * @param {*} requestOptions * @returns */ -const getIdFromNewOrExistingProfile = async (endpoint, payload, requestOptions) => { +const getIdFromNewOrExistingProfile = async ({ endpoint, payload, requestOptions, metadata }) => { let response; let profileId; const endpointPath = '/api/profiles'; + logger.requestLog(`[${destType.toUpperCase()}] get id from profile request`, { + metadata, + requestDetails: { + url: endpoint, + body: payload, + method: 'post', + }, + }); const { processedResponse: resp } = await handleHttpRequest( 'post', endpoint, @@ -49,6 +64,10 @@ const getIdFromNewOrExistingProfile = async (endpoint, payload, requestOptions) module: 'router', }, ); + logger.responseLog(`[${destType.toUpperCase()}] get id from profile response`, { + metadata, + responseDetails: resp, + }); /** * 201 - profile is created with updated payload no need to update it again (suppress event with 299 status code) diff --git a/src/v0/destinations/mailchimp/utils.js b/src/v0/destinations/mailchimp/utils.js index a726f23a39..f678742f2d 100644 --- a/src/v0/destinations/mailchimp/utils.js +++ b/src/v0/destinations/mailchimp/utils.js @@ -1,10 +1,6 @@ const get = require('get-value'); const md5 = require('md5'); -const { - InstrumentationError, - NetworkError, - structuredLogger: logger, -} = require('@rudderstack/integrations-lib'); +const { InstrumentationError, NetworkError } = require('@rudderstack/integrations-lib'); const myAxios = require('../../../util/myAxios'); const { MappedToDestinationKey } = require('../../../constants'); const { @@ -19,6 +15,7 @@ const { defaultBatchRequestConfig, constructPayload, } = require('../../util'); +const logger = require('../../../logger'); const { MERGE_CONFIG, MERGE_ADDRESS, SUBSCRIPTION_STATUS, VALID_STATUSES } = require('./config'); const { getDynamicErrorType } = require('../../../adapters/utils/networkUtils'); const tags = require('../../util/tags'); diff --git a/src/v0/sources/adjust/transform.js b/src/v0/sources/adjust/transform.js index 8568622aeb..9da90751b7 100644 --- a/src/v0/sources/adjust/transform.js +++ b/src/v0/sources/adjust/transform.js @@ -1,7 +1,8 @@ const lodash = require('lodash'); const path = require('path'); const fs = require('fs'); -const { TransformationError, structuredLogger: logger } = require('@rudderstack/integrations-lib'); +const { TransformationError } = require('@rudderstack/integrations-lib'); +const logger = require('../../../logger'); const Message = require('../message'); const { CommonUtils } = require('../../../util/common'); const { excludedFieldList } = require('./config'); diff --git a/src/v0/sources/canny/transform.js b/src/v0/sources/canny/transform.js index 38ed5e137e..aad5a881c1 100644 --- a/src/v0/sources/canny/transform.js +++ b/src/v0/sources/canny/transform.js @@ -2,6 +2,7 @@ const sha256 = require('sha256'); const { TransformationError } = require('@rudderstack/integrations-lib'); const Message = require('../message'); const { voterMapping, authorMapping, checkForRequiredFields } = require('./util'); +const logger = require('../../../logger'); const CannyOperation = { VOTE_CREATED: 'vote.created', @@ -14,7 +15,7 @@ const CannyOperation = { * @param {*} event * @param {*} typeOfUser */ -function settingIds(message, event, typeOfUser, logger) { +function settingIds(message, event, typeOfUser) { const clonedMessage = { ...message }; try { // setting up userId @@ -47,7 +48,7 @@ function settingIds(message, event, typeOfUser, logger) { * @param {*} typeOfUser * @returns message */ -function createMessage(event, typeOfUser, logger) { +function createMessage(event, typeOfUser) { const message = new Message(`Canny`); message.setEventType('track'); @@ -60,7 +61,7 @@ function createMessage(event, typeOfUser, logger) { message.context.integration.version = '1.0.0'; - const finalMessage = settingIds(message, event, typeOfUser, logger); + const finalMessage = settingIds(message, event, typeOfUser); checkForRequiredFields(finalMessage); @@ -72,7 +73,7 @@ function createMessage(event, typeOfUser, logger) { return finalMessage; } -function process(event, logger) { +function process(event) { let typeOfUser; switch (event.type) { @@ -85,6 +86,6 @@ function process(event, logger) { typeOfUser = 'author'; } - return createMessage(event, typeOfUser, logger); + return createMessage(event, typeOfUser); } module.exports = { process }; diff --git a/src/v0/sources/shopify/transform.js b/src/v0/sources/shopify/transform.js index 4886fb3df1..bc2135d215 100644 --- a/src/v0/sources/shopify/transform.js +++ b/src/v0/sources/shopify/transform.js @@ -13,6 +13,7 @@ const { getHashLineItems, getDataFromRedis, } = require('./util'); +const logger = require('../../../logger'); const { RedisDB } = require('../../../util/redis/redisConnector'); const { removeUndefinedAndNullValues, isDefinedAndNotNull } = require('../../util'); const Message = require('../message'); @@ -205,7 +206,7 @@ const processEvent = async (inputEvent, metricMetadata) => { }; const isIdentifierEvent = (event) => ['rudderIdentifier', 'rudderSessionIdentifier'].includes(event?.event); -const processIdentifierEvent = async (event, metricMetadata, logger) => { +const processIdentifierEvent = async (event, metricMetadata) => { if (useRedisDatabase) { let value; let field; @@ -243,7 +244,11 @@ const processIdentifierEvent = async (event, metricMetadata, logger) => { }); await RedisDB.setVal(`${event.cartToken}`, value); } catch (e) { - logger.debug(`{{SHOPIFY::}} cartToken map set call Failed due redis error ${e}`); + logger.debug(`{{SHOPIFY::}} cartToken map set call Failed due redis error ${e}`, { + type: 'set', + source: metricMetadata.source, + writeKey: metricMetadata.writeKey, + }); stats.increment('shopify_redis_failures', { type: 'set', source: metricMetadata.source, @@ -255,13 +260,13 @@ const processIdentifierEvent = async (event, metricMetadata, logger) => { } return NO_OPERATION_SUCCESS; }; -const process = async (event, logger) => { +const process = async (event) => { const metricMetadata = { writeKey: event.query_parameters?.writeKey?.[0], source: 'SHOPIFY', }; if (isIdentifierEvent(event)) { - return processIdentifierEvent(event, metricMetadata, logger); + return processIdentifierEvent(event, metricMetadata); } const response = await processEvent(event, metricMetadata); return response; diff --git a/src/v0/sources/shopify/util.js b/src/v0/sources/shopify/util.js index 3dc54cc434..6aea0d19bd 100644 --- a/src/v0/sources/shopify/util.js +++ b/src/v0/sources/shopify/util.js @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ const { v5 } = require('uuid'); const sha256 = require('sha256'); -const { TransformationError, structuredLogger: logger } = require('@rudderstack/integrations-lib'); +const { TransformationError } = require('@rudderstack/integrations-lib'); const stats = require('../../../util/stats'); const { constructPayload, @@ -22,6 +22,7 @@ const { useRedisDatabase, maxTimeToIdentifyRSGeneratedCall, } = require('./config'); +const logger = require('../../../logger'); const getDataFromRedis = async (key, metricMetadata) => { try { diff --git a/src/v1/destinations/campaign_manager/networkHandler.js b/src/v1/destinations/campaign_manager/networkHandler.js index 300b5f9676..eee3869fb5 100644 --- a/src/v1/destinations/campaign_manager/networkHandler.js +++ b/src/v1/destinations/campaign_manager/networkHandler.js @@ -9,6 +9,7 @@ const { getDynamicErrorType, } = require('../../../adapters/utils/networkUtils'); const tags = require('../../../v0/util/tags'); +const logger = require('../../../logger'); function isEventAbortableAndExtractErrMsg(element, proxyOutputObj) { let isAbortable = false; @@ -38,7 +39,16 @@ const responseHandler = (responseParams) => { const { destinationResponse, rudderJobMetadata } = responseParams; const message = `[CAMPAIGN_MANAGER Response V1 Handler] - Request Processed Successfully`; const responseWithIndividualEvents = []; - const { response, status } = destinationResponse; + const { response, status, headers } = destinationResponse; + + logger.responseLog('[campaign_manager] response handling', { + metadata: rudderJobMetadata, + responseDetails: { + headers, + response, + status, + }, + }); if (isHttpStatusSuccess(status)) { // check for Partial Event failures and Successes diff --git a/src/v1/destinations/monday/networkHandler.js b/src/v1/destinations/monday/networkHandler.js index 28a7f1abc0..5a0313a27b 100644 --- a/src/v1/destinations/monday/networkHandler.js +++ b/src/v1/destinations/monday/networkHandler.js @@ -6,6 +6,7 @@ const { } = require('../../../adapters/utils/networkUtils'); const { isHttpStatusSuccess } = require('../../../v0/util/index'); const tags = require('../../../v0/util/tags'); +const logger = require('../../../logger'); const checkIfUpdationOfStatusRequired = (response) => { let errorMsg = ''; @@ -41,8 +42,16 @@ const responseHandler = (responseParams) => { const message = '[MONDAY Response V1 Handler] - Request Processed Successfully'; const responseWithIndividualEvents = []; - const { response, status } = destinationResponse; + const { response, status, headers } = destinationResponse; + logger.responseLog('[monday] proxy response', { + metadata: rudderJobMetadata, + responseDetails: { + headers, + response, + status, + }, + }); // batching not supported if (isHttpStatusSuccess(status)) { const proxyOutput = { diff --git a/test/__tests__/pinterestConversion-cdk.test.ts b/test/__tests__/pinterestConversion-cdk.test.ts index 6aaa710ed7..2afde331d5 100644 --- a/test/__tests__/pinterestConversion-cdk.test.ts +++ b/test/__tests__/pinterestConversion-cdk.test.ts @@ -1,8 +1,8 @@ -import { structuredLogger as logger } from '@rudderstack/integrations-lib'; import fs from 'fs'; import path from 'path'; import { executeWorkflow, getWorkflowEngine, processCdkV2Workflow } from '../../src/cdk/v2/handler'; import tags from '../../src/v0/util/tags'; +import logger from '../../src/logger'; const integration = 'pinterest_tag'; const name = 'Pinterest Conversion API'; diff --git a/test/integrations/destinations/am/dataDelivery/data.ts b/test/integrations/destinations/am/dataDelivery/data.ts index a4faa7e60c..19baca02c3 100644 --- a/test/integrations/destinations/am/dataDelivery/data.ts +++ b/test/integrations/destinations/am/dataDelivery/data.ts @@ -68,6 +68,15 @@ export const data = [ message: '[Generic Response Handler] Request for destination: am Processed Successfully', destinationResponse: { + headers: { + 'access-control-allow-methods': 'GET, POST', + 'access-control-allow-origin': '*', + connection: 'keep-alive', + 'content-length': '93', + 'content-type': 'application/json', + date: 'Sat, 11 Dec 2021 15:08:22 GMT', + 'strict-transport-security': 'max-age=15768000', + }, response: { code: 200, server_upload_time: 1639235302252, @@ -145,6 +154,15 @@ export const data = [ message: '[Generic Response Handler] Request failed for destination am with status: 400', destinationResponse: { + headers: { + 'access-control-allow-methods': 'GET, POST', + 'access-control-allow-origin': '*', + connection: 'keep-alive', + 'content-length': '93', + 'content-type': 'application/json', + date: 'Sat, 11 Dec 2021 15:08:22 GMT', + 'strict-transport-security': 'max-age=15768000', + }, response: { code: 400, server_upload_time: 1639235302252, From e6c50980a12960a419f5c03e120739eea642767a Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Tue, 18 Jun 2024 17:57:00 +0530 Subject: [PATCH 219/240] fix: credentials payload fix --- src/types/index.ts | 8 ++++---- src/util/customTransformer.js | 4 ++-- .../user_transformation_input_credentials.json | 16 ++++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/types/index.ts b/src/types/index.ts index 6297d49c42..150758363e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -140,10 +140,10 @@ type UserTransformationLibrary = { }; type Credential = { - ID: string; - Key: string; - Value: string; - IsSecret: boolean; + id: string; + key: string; + value: string; + isSecret: boolean; }; type ProcessorTransformationRequest = { diff --git a/src/util/customTransformer.js b/src/util/customTransformer.js index 5107d7e29a..a7b52b004c 100644 --- a/src/util/customTransformer.js +++ b/src/util/customTransformer.js @@ -290,11 +290,11 @@ async function userTransformHandler( const credentialsMap = {}; if (testMode === false) { (events[0]?.credentials || []).forEach((cred) => { - credentialsMap[cred.Key] = cred.Value; + credentialsMap[cred.key] = cred.value; }); } else { (credentials || []).forEach((cred) => { - credentialsMap[cred.Key] = cred.Value; + credentialsMap[cred.key] = cred.value; }); events.forEach((ev) => { if (isNil(ev.credentials)) { diff --git a/test/__tests__/data/user_transformation_input_credentials.json b/test/__tests__/data/user_transformation_input_credentials.json index bd2593f56d..d210a3715c 100644 --- a/test/__tests__/data/user_transformation_input_credentials.json +++ b/test/__tests__/data/user_transformation_input_credentials.json @@ -51,16 +51,16 @@ }, "credentials": [ { - "Id": "id1", - "Key": "key1", - "Value": "value1", - "IsSecret": false + "id": "id1", + "key": "key1", + "value": "value1", + "isSecret": false }, { - "Id": "id2", - "Key": "key2", - "Value": "value2", - "IsSecret": true + "id": "id2", + "key": "key2", + "value": "value2", + "isSecret": true } ] } From ca5568e5d06ab6dccef1b0f9c369d78a44ae7a8a Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Tue, 18 Jun 2024 18:41:16 +0530 Subject: [PATCH 220/240] chore: upgrade packages --- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 066dac2c88..c1ca7a6ca6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,8 +20,8 @@ "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.10", - "@rudderstack/json-template-engine": "^0.13.2", - "@rudderstack/workflow-engine": "^0.8.2", + "@rudderstack/json-template-engine": "^0.13.8", + "@rudderstack/workflow-engine": "^0.8.6", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", @@ -4526,17 +4526,17 @@ } }, "node_modules/@rudderstack/json-template-engine": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.13.2.tgz", - "integrity": "sha512-uEyMv/qjm/mP5V8EifJzolvFLtka/dacmvwo9Xk3+MnEbsNN0YLu7Z/qWeyXeDF5chvy8JfaqV8lNgO3SxVG7g==" + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.13.8.tgz", + "integrity": "sha512-2bl6a25SEm+LapdNqR5QhLv61dQiv3squmCr4Qy9U89BIp9yX9WOL8tvoaIS1isN7lwIOViD3cD8ft0ehlj8Sw==" }, "node_modules/@rudderstack/workflow-engine": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.8.2.tgz", - "integrity": "sha512-cjn3J8CUarAE3cbASRvkf7A2745Clzkw/ffqGLzD8+9KvTN6mC28Pm9c5169LPDmt+NMUMw0W5xHgNO3cV9eqQ==", + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.8.6.tgz", + "integrity": "sha512-O+Kj3y4DZNgV2fWAOkwYwqsnjot0YxiVrDFWUkLdFQHTSXJ5Y/eqDJglDXdcpwSPg9AlaUFVHsGzw6BkAQLcIA==", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", - "@rudderstack/json-template-engine": "^0.13.2", + "@rudderstack/json-template-engine": "^0.13.8", "jsonata": "^2.0.5", "lodash": "^4.17.21", "object-sizeof": "^2.6.4", diff --git a/package.json b/package.json index 4c558430d9..5b0d74c650 100644 --- a/package.json +++ b/package.json @@ -65,8 +65,8 @@ "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.10", - "@rudderstack/json-template-engine": "^0.13.2", - "@rudderstack/workflow-engine": "^0.8.2", + "@rudderstack/json-template-engine": "^0.13.8", + "@rudderstack/workflow-engine": "^0.8.6", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", From 60fee0e5a442c4f6c773234d88d86b447434de9b Mon Sep 17 00:00:00 2001 From: Vikas26021999 <79831638+Vikas26021999@users.noreply.github.com> Date: Tue, 18 Jun 2024 21:24:08 +0530 Subject: [PATCH 221/240] feat: garl record event support (#3403) --- .../fb_custom_audience/recordTransform.js | 53 +- .../config.js | 2 +- .../recordTransform.js | 144 ++++ .../transform.js | 165 ++--- .../google_adwords_remarketing_lists/util.js | 122 ++++ .../util.test.js | 203 ++++++ src/v0/util/recordUtils.js | 55 ++ .../processor/data.ts | 140 ++-- .../router/audience.ts | 129 ++++ .../router/data.ts | 631 +++++++++++------- .../router/record.ts | 158 +++++ 11 files changed, 1329 insertions(+), 473 deletions(-) create mode 100644 src/v0/destinations/google_adwords_remarketing_lists/recordTransform.js create mode 100644 src/v0/destinations/google_adwords_remarketing_lists/util.js create mode 100644 src/v0/destinations/google_adwords_remarketing_lists/util.test.js create mode 100644 src/v0/util/recordUtils.js create mode 100644 test/integrations/destinations/google_adwords_remarketing_lists/router/audience.ts create mode 100644 test/integrations/destinations/google_adwords_remarketing_lists/router/record.ts diff --git a/src/v0/destinations/fb_custom_audience/recordTransform.js b/src/v0/destinations/fb_custom_audience/recordTransform.js index 0f7b65c0bf..62d4bd568b 100644 --- a/src/v0/destinations/fb_custom_audience/recordTransform.js +++ b/src/v0/destinations/fb_custom_audience/recordTransform.js @@ -1,11 +1,7 @@ /* eslint-disable no-const-assign */ const lodash = require('lodash'); const get = require('get-value'); -const { - InstrumentationError, - ConfigurationError, - getErrorRespEvents, -} = require('@rudderstack/integrations-lib'); +const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); const { schemaFields } = require('./config'); const { MappedToDestinationKey } = require('../../../constants'); const stats = require('../../../util/stats'); @@ -15,8 +11,8 @@ const { checkSubsetOfArray, returnArrayOfSubarrays, getSuccessRespEvents, - generateErrorObject, } = require('../../util'); +const { getErrorResponse, createFinalResponse } = require('../../util/recordUtils'); const { ensureApplicableFormat, getUpdatedDataElement, @@ -26,19 +22,6 @@ const { getDataSource, } = require('./util'); -function getErrorMetaData(inputs, acceptedOperations) { - const metadata = []; - // eslint-disable-next-line no-restricted-syntax - for (const key in inputs) { - if (!acceptedOperations.includes(key)) { - inputs[key].forEach((input) => { - metadata.push(input.metadata); - }); - } - } - return metadata; -} - const processRecordEventArray = ( recordChunksArray, userSchema, @@ -177,8 +160,6 @@ async function processRecordInputs(groupedRecordInputs) { record.message.action?.toLowerCase(), ); - const finalResponse = []; - let insertResponse; let deleteResponse; let updateResponse; @@ -238,32 +219,14 @@ async function processRecordInputs(groupedRecordInputs) { ); } - const eventTypes = ['update', 'insert', 'delete']; - const errorMetaData = []; - const errorMetaDataObject = getErrorMetaData(groupedRecordsByAction, eventTypes); - if (errorMetaDataObject.length > 0) { - errorMetaData.push(errorMetaDataObject); - } + const errorResponse = getErrorResponse(groupedRecordsByAction); - const error = new InstrumentationError('Invalid action type in record event'); - const errorObj = generateErrorObject(error); - const errorResponseList = errorMetaData.map((metadata) => - getErrorRespEvents(metadata, errorObj.status, errorObj.message, errorObj.statTags), + const finalResponse = createFinalResponse( + deleteResponse, + insertResponse, + updateResponse, + errorResponse, ); - - if (deleteResponse && deleteResponse.batchedRequest.length > 0) { - finalResponse.push(deleteResponse); - } - if (insertResponse && insertResponse.batchedRequest.length > 0) { - finalResponse.push(insertResponse); - } - if (updateResponse && updateResponse.batchedRequest.length > 0) { - finalResponse.push(updateResponse); - } - if (errorResponseList.length > 0) { - finalResponse.push(...errorResponseList); - } - if (finalResponse.length === 0) { throw new InstrumentationError( 'Missing valid parameters, unable to generate transformed payload', diff --git a/src/v0/destinations/google_adwords_remarketing_lists/config.js b/src/v0/destinations/google_adwords_remarketing_lists/config.js index f8983699c6..c7b97e0e6c 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/config.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/config.js @@ -1,6 +1,6 @@ const { getMappingConfig } = require('../../util'); -const BASE_ENDPOINT = 'https://googleads.googleapis.com/v15/customers'; +const BASE_ENDPOINT = 'https://googleads.googleapis.com/v16/customers'; const CONFIG_CATEGORIES = { AUDIENCE_LIST: { type: 'audienceList', name: 'offlineDataJobs' }, ADDRESSINFO: { type: 'addressInfo', name: 'addressInfo' }, diff --git a/src/v0/destinations/google_adwords_remarketing_lists/recordTransform.js b/src/v0/destinations/google_adwords_remarketing_lists/recordTransform.js new file mode 100644 index 0000000000..18e7f96821 --- /dev/null +++ b/src/v0/destinations/google_adwords_remarketing_lists/recordTransform.js @@ -0,0 +1,144 @@ +/* eslint-disable no-const-assign */ +const lodash = require('lodash'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { + getValueFromMessage, + getAccessToken, + constructPayload, + returnArrayOfSubarrays, + getSuccessRespEvents, +} = require('../../util'); +const { populateConsentFromConfig } = require('../../util/googleUtils'); +const { populateIdentifiers, responseBuilder } = require('./util'); +const { getErrorResponse, createFinalResponse } = require('../../util/recordUtils'); +const { offlineDataJobsMapping, consentConfigMap } = require('./config'); + +const processRecordEventArray = ( + records, + message, + destination, + accessToken, + developerToken, + operationType, +) => { + let outputPayloads = {}; + // ** only send it if identifier > 0 + + const fieldsArray = []; + const metadata = []; + records.forEach((record) => { + fieldsArray.push(record.message.fields); + metadata.push(record.metadata); + }); + + const userIdentifiersList = populateIdentifiers(fieldsArray, destination); + + const outputPayload = constructPayload(message, offlineDataJobsMapping); + outputPayload.operations = []; + // breaking the userIdentiFier array in chunks of 20 + const userIdentifierChunks = returnArrayOfSubarrays(userIdentifiersList, 20); + // putting each chunk in different create/remove operations + switch (operationType) { + case 'add': + // for add operation + userIdentifierChunks.forEach((element) => { + const operations = { + create: {}, + }; + operations.create.userIdentifiers = element; + outputPayload.operations.push(operations); + }); + outputPayloads = { ...outputPayloads, create: outputPayload }; + break; + case 'remove': + // for remove operation + userIdentifierChunks.forEach((element) => { + const operations = { + remove: {}, + }; + operations.remove.userIdentifiers = element; + outputPayload.operations.push(operations); + }); + outputPayloads = { ...outputPayloads, remove: outputPayload }; + break; + default: + } + + const toSendEvents = []; + Object.values(outputPayloads).forEach((data) => { + const consentObj = populateConsentFromConfig(destination.Config, consentConfigMap); + toSendEvents.push( + responseBuilder(accessToken, developerToken, data, destination, message, consentObj), + ); + }); + + const successResponse = getSuccessRespEvents(toSendEvents, metadata, destination, true); + + return successResponse; +}; + +async function processRecordInputs(groupedRecordInputs) { + const { destination, message, metadata } = groupedRecordInputs[0]; + const accessToken = getAccessToken(metadata, 'accessToken'); + const developerToken = getValueFromMessage(metadata, 'secret.developer_token'); + + const groupedRecordsByAction = lodash.groupBy(groupedRecordInputs, (record) => + record.message.action?.toLowerCase(), + ); + + let insertResponse; + let deleteResponse; + let updateResponse; + + if (groupedRecordsByAction.delete) { + deleteResponse = processRecordEventArray( + groupedRecordsByAction.delete, + message, + destination, + accessToken, + developerToken, + 'remove', + ); + } + + if (groupedRecordsByAction.insert) { + insertResponse = processRecordEventArray( + groupedRecordsByAction.insert, + message, + destination, + accessToken, + developerToken, + 'add', + ); + } + + if (groupedRecordsByAction.update) { + updateResponse = processRecordEventArray( + groupedRecordsByAction.update, + message, + destination, + accessToken, + developerToken, + 'add', + ); + } + + const errorResponse = getErrorResponse(groupedRecordsByAction); + const finalResponse = createFinalResponse( + deleteResponse, + insertResponse, + updateResponse, + errorResponse, + ); + if (finalResponse.length === 0) { + throw new InstrumentationError( + 'Missing valid parameters, unable to generate transformed payload', + ); + } + + return finalResponse; +} + +module.exports = { + processRecordInputs, +}; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/transform.js b/src/v0/destinations/google_adwords_remarketing_lists/transform.js index b0dfaa0c35..d879a39c63 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/transform.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/transform.js @@ -1,138 +1,29 @@ -const sha256 = require('sha256'); -const get = require('get-value'); +const lodash = require('lodash'); const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); const logger = require('../../../logger'); const { - isDefinedAndNotNullAndNotEmpty, returnArrayOfSubarrays, constructPayload, - defaultRequestConfig, getValueFromMessage, - removeUndefinedAndNullValues, - removeHyphens, simpleProcessRouterDest, - getDestinationExternalIDInfoForRetl, getAccessToken, } = require('../../util'); const { populateConsentFromConfig } = require('../../util/googleUtils'); - -const { - offlineDataJobsMapping, - addressInfoMapping, - BASE_ENDPOINT, - attributeMapping, - hashAttributes, - TYPEOFLIST, - consentConfigMap, -} = require('./config'); -const { JSON_MIME_TYPE } = require('../../util/constant'); -const { MappedToDestinationKey } = require('../../../constants'); - -const hashEncrypt = (object) => { - Object.keys(object).forEach((key) => { - if (hashAttributes.includes(key) && object[key]) { - // eslint-disable-next-line no-param-reassign - object[key] = sha256(object[key]); +const { offlineDataJobsMapping, consentConfigMap } = require('./config'); +const { processRecordInputs } = require('./recordTransform'); +const { populateIdentifiers, responseBuilder } = require('./util'); + +function extraKeysPresent(dictionary, keyList) { + // eslint-disable-next-line no-restricted-syntax + for (const key in dictionary) { + if (!keyList.includes(key)) { + return true; } - }); -}; - -/** - * This function is used for building the response. It create a default rudder response - * and populate headers, params and body.JSON - * @param {*} metadata - * @param {*} body - * @param {*} param2 - * @returns - */ -const responseBuilder = (metadata, body, { Config }, message, consentBlock) => { - const payload = body; - const response = defaultRequestConfig(); - const filteredCustomerId = removeHyphens(Config.customerId); - response.endpoint = `${BASE_ENDPOINT}/${filteredCustomerId}/offlineUserDataJobs`; - response.body.JSON = removeUndefinedAndNullValues(payload); - const accessToken = getAccessToken(metadata, 'access_token'); - let operationAudienceId = Config.audienceId || Config.listId; - const mappedToDestination = get(message, MappedToDestinationKey); - if (!operationAudienceId && mappedToDestination) { - const { objectType } = getDestinationExternalIDInfoForRetl( - message, - 'GOOGLE_ADWORDS_REMARKETING_LISTS', - ); - operationAudienceId = objectType; - } - if (!isDefinedAndNotNullAndNotEmpty(operationAudienceId)) { - throw new ConfigurationError('List ID is a mandatory field'); } - response.params = { - listId: operationAudienceId, - customerId: filteredCustomerId, - consent: consentBlock, - }; - response.headers = { - Authorization: `Bearer ${accessToken}`, - 'Content-Type': JSON_MIME_TYPE, - 'developer-token': getValueFromMessage(metadata, 'secret.developer_token'), - }; - if (Config.subAccount) - if (Config.loginCustomerId) { - const filteredLoginCustomerId = removeHyphens(Config.loginCustomerId); - response.headers['login-customer-id'] = filteredLoginCustomerId; - } else throw new ConfigurationError(`loginCustomerId is required as subAccount is true.`); - return response; -}; -/** - * This function helps creates an array with proper mapping for userIdentiFier. - * Logics: Here we are creating an array with all the attributes provided in the add/remove array - * inside listData. - * @param {rudder event message properties listData add} attributeArray - * @param {rudder event destination} Config - * @returns - */ + return false; +} -const populateIdentifiers = (attributeArray, { Config }) => { - const userIdentifier = []; - const { typeOfList } = Config; - const { isHashRequired, userSchema } = Config; - let attribute; - if (TYPEOFLIST[typeOfList]) { - attribute = TYPEOFLIST[typeOfList]; - } else { - attribute = userSchema; - } - if (isDefinedAndNotNullAndNotEmpty(attributeArray)) { - // traversing through every element in the add array - attributeArray.forEach((element, index) => { - if (isHashRequired) { - hashEncrypt(element); - } - // checking if the attribute is an array or not for generic type list - if (!Array.isArray(attribute)) { - if (element[attribute]) { - userIdentifier.push({ [attribute]: element[attribute] }); - } else { - logger.info(` ${attribute} is not present in index:`, index); - } - } else { - attribute.forEach((attributeElement, index2) => { - if (attributeElement === 'addressInfo') { - const addressInfo = constructPayload(element, addressInfoMapping); - // checking if addressInfo object is empty or not. - if (isDefinedAndNotNullAndNotEmpty(addressInfo)) userIdentifier.push({ addressInfo }); - } else if (element[`${attributeElement}`]) { - userIdentifier.push({ - [`${attributeMapping[attributeElement]}`]: element[`${attributeElement}`], - }); - } else { - logger.info(` ${attribute[index2]} is not present in index:`, index); - } - }); - } - }); - } - return userIdentifier; -}; /** * This function helps to create different operations by breaking the * userIdentiFier Array in chunks of 20. @@ -143,7 +34,6 @@ const populateIdentifiers = (attributeArray, { Config }) => { * @param {rudder event destination} destination * @returns */ - const createPayload = (message, destination) => { const { listData } = message.properties; const properties = ['add', 'remove']; @@ -218,9 +108,14 @@ const processEvent = async (metadata, message, destination) => { ); } + const accessToken = getAccessToken(metadata, 'accessToken'); + const developerToken = getValueFromMessage(metadata, 'secret.developer_token'); + Object.values(createdPayload).forEach((data) => { const consentObj = populateConsentFromConfig(destination.Config, consentConfigMap); - response.push(responseBuilder(metadata, data, destination, message, consentObj)); + response.push( + responseBuilder(accessToken, developerToken, data, destination, message, consentObj), + ); }); return response; } @@ -231,7 +126,29 @@ const processEvent = async (metadata, message, destination) => { const process = async (event) => processEvent(event.metadata, event.message, event.destination); const processRouterDest = async (inputs, reqMetadata) => { - const respList = await simpleProcessRouterDest(inputs, process, reqMetadata); + const respList = []; + const groupedInputs = lodash.groupBy(inputs, (input) => input.message.type?.toLowerCase()); + let transformedRecordEvent = []; + let transformedAudienceEvent = []; + + const eventTypes = ['record', 'audiencelist']; + if (extraKeysPresent(groupedInputs, eventTypes)) { + throw new ConfigurationError('unsupported events present in the event'); + } + + if (groupedInputs.record) { + transformedRecordEvent = await processRecordInputs(groupedInputs.record, reqMetadata); + } + + if (groupedInputs.audiencelist) { + transformedAudienceEvent = await simpleProcessRouterDest( + groupedInputs.audiencelist, + process, + reqMetadata, + ); + } + + respList.push(...transformedRecordEvent, ...transformedAudienceEvent); return respList; }; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/util.js b/src/v0/destinations/google_adwords_remarketing_lists/util.js new file mode 100644 index 0000000000..3e04dd8f6f --- /dev/null +++ b/src/v0/destinations/google_adwords_remarketing_lists/util.js @@ -0,0 +1,122 @@ +const get = require('get-value'); +const sha256 = require('sha256'); +const { ConfigurationError } = require('@rudderstack/integrations-lib'); +const { + isDefinedAndNotNullAndNotEmpty, + constructPayload, + defaultRequestConfig, + removeHyphens, + removeUndefinedAndNullValues, + getDestinationExternalIDInfoForRetl, +} = require('../../util'); +const logger = require('../../../logger'); +const { MappedToDestinationKey } = require('../../../constants'); +const { JSON_MIME_TYPE } = require('../../util/constant'); +const { + addressInfoMapping, + attributeMapping, + TYPEOFLIST, + BASE_ENDPOINT, + hashAttributes, +} = require('./config'); + +const hashEncrypt = (object) => { + Object.keys(object).forEach((key) => { + if (hashAttributes.includes(key) && object[key]) { + // eslint-disable-next-line no-param-reassign + object[key] = sha256(object[key]); + } + }); +}; + +const responseBuilder = (accessToken, developerToken, body, { Config }, message, consentBlock) => { + const payload = body; + const response = defaultRequestConfig(); + const filteredCustomerId = removeHyphens(Config.customerId); + response.endpoint = `${BASE_ENDPOINT}/${filteredCustomerId}/offlineUserDataJobs`; + response.body.JSON = removeUndefinedAndNullValues(payload); + let operationAudienceId = Config.audienceId || Config.listId; + const mappedToDestination = get(message, MappedToDestinationKey); + if (!operationAudienceId && mappedToDestination) { + const { objectType } = getDestinationExternalIDInfoForRetl( + message, + 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ); + operationAudienceId = objectType; + } + if (!isDefinedAndNotNullAndNotEmpty(operationAudienceId)) { + throw new ConfigurationError('List ID is a mandatory field'); + } + response.params = { + listId: operationAudienceId, + customerId: filteredCustomerId, + consent: consentBlock, + }; + response.headers = { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': JSON_MIME_TYPE, + 'developer-token': developerToken, + }; + if (Config.subAccount) + if (Config.loginCustomerId) { + const filteredLoginCustomerId = removeHyphens(Config.loginCustomerId); + response.headers['login-customer-id'] = filteredLoginCustomerId; + } else throw new ConfigurationError(`loginCustomerId is required as subAccount is true.`); + return response; +}; + +/** + * This function helps creates an array with proper mapping for userIdentiFier. + * Logics: Here we are creating an array with all the attributes provided in the add/remove array + * inside listData. + * @param {rudder event message properties listData add} attributeArray + * @param {rudder event destination} Config + * @returns + */ +const populateIdentifiers = (attributeArray, { Config }) => { + const userIdentifier = []; + const { typeOfList } = Config; + const { isHashRequired, userSchema } = Config; + let attribute; + if (TYPEOFLIST[typeOfList]) { + attribute = TYPEOFLIST[typeOfList]; + } else { + attribute = userSchema; + } + if (isDefinedAndNotNullAndNotEmpty(attributeArray)) { + // traversing through every element in the add array + attributeArray.forEach((element, index) => { + if (isHashRequired) { + hashEncrypt(element); + } + // checking if the attribute is an array or not for generic type list + if (!Array.isArray(attribute)) { + if (element[attribute]) { + userIdentifier.push({ [attribute]: element[attribute] }); + } else { + logger.info(` ${attribute} is not present in index:`, index); + } + } else { + attribute.forEach((attributeElement, index2) => { + if (attributeElement === 'addressInfo') { + const addressInfo = constructPayload(element, addressInfoMapping); + // checking if addressInfo object is empty or not. + if (isDefinedAndNotNullAndNotEmpty(addressInfo)) userIdentifier.push({ addressInfo }); + } else if (element[`${attributeElement}`]) { + userIdentifier.push({ + [`${attributeMapping[attributeElement]}`]: element[`${attributeElement}`], + }); + } else { + logger.info(` ${attribute[index2]} is not present in index:`, index); + } + }); + } + }); + } + return userIdentifier; +}; + +module.exports = { + populateIdentifiers, + responseBuilder, +}; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/util.test.js b/src/v0/destinations/google_adwords_remarketing_lists/util.test.js new file mode 100644 index 0000000000..e9fe90c317 --- /dev/null +++ b/src/v0/destinations/google_adwords_remarketing_lists/util.test.js @@ -0,0 +1,203 @@ +const { populateIdentifiers, responseBuilder } = require('./util'); + +const accessToken = 'abcd1234'; +const developerToken = 'ijkl9101'; +const body = { + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, + ], + }, + }, + ], +}; +const baseDestination = { + Config: { + rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', + listId: '7090784486', + customerId: '7693729833', + loginCustomerId: '', + subAccount: false, + userSchema: ['email', 'phone', 'addressInfo'], + isHashRequired: true, + typeOfList: 'General', + }, +}; +const consentBlock = { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', +}; +const message = { + action: 'insert', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + phone: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + firstName: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + lastName: 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + country: 'US', + postalCode: '1245', + }, + type: 'record', +}; +const expectedResponse = { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl9101', + }, + params: { + listId: '7090784486', + customerId: '7693729833', + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, + }, + body: { + JSON: { + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: + '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: + 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, + ], + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, +}; +const attributeArray = [ + { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, +]; + +const hashedArray = [ + { + hashedEmail: 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, +]; + +describe('GARL utils test', () => { + describe('responseBuilder function tests', () => { + it('Should return correct response for given payload', () => { + const response = responseBuilder( + accessToken, + developerToken, + body, + baseDestination, + message, + consentBlock, + ); + expect(response).toEqual(expectedResponse); + }); + + it('Should throw error if subaccount is true and loginCustomerId is not defined', () => { + try { + const destination2 = Object.create(baseDestination); + destination2.Config.subAccount = true; + destination2.Config.loginCustomerId = ''; + const response = responseBuilder( + accessToken, + developerToken, + body, + destination2, + message, + consentBlock, + ); + expect(response).toEqual(); + } catch (error) { + expect(error.message).toEqual(`loginCustomerId is required as subAccount is true.`); + } + }); + + it('Should throw error if operationAudienceId is not defined', () => { + try { + const destination1 = Object.create(baseDestination); + destination1.Config.listId = ''; + const response = responseBuilder( + accessToken, + developerToken, + body, + destination1, + message, + consentBlock, + ); + expect(response).toEqual(); + } catch (error) { + expect(error.message).toEqual(`List ID is a mandatory field`); + } + }); + }); + + describe('populateIdentifiers function tests', () => { + it('Should hash and return identifiers for a given list of attributes', () => { + const identifier = populateIdentifiers(attributeArray, baseDestination); + expect(identifier).toEqual(hashedArray); + }); + }); +}); diff --git a/src/v0/util/recordUtils.js b/src/v0/util/recordUtils.js new file mode 100644 index 0000000000..a3dd65ef33 --- /dev/null +++ b/src/v0/util/recordUtils.js @@ -0,0 +1,55 @@ +const { InstrumentationError, getErrorRespEvents } = require('@rudderstack/integrations-lib'); +const { generateErrorObject } = require('./index'); + +const eventTypes = ['update', 'insert', 'delete']; + +function getErrorMetaData(inputs, acceptedOperations) { + const metadata = []; + // eslint-disable-next-line no-restricted-syntax + for (const key in inputs) { + if (!acceptedOperations.includes(key)) { + inputs[key].forEach((input) => { + metadata.push(input.metadata); + }); + } + } + return metadata; +} + +function getErrorResponse(groupedRecordsByAction) { + const errorMetaData = []; + const errorMetaDataObject = getErrorMetaData(groupedRecordsByAction, eventTypes); + if (errorMetaDataObject.length > 0) { + errorMetaData.push(errorMetaDataObject); + } + + const error = new InstrumentationError('Invalid action type in record event'); + const errorObj = generateErrorObject(error); + const errorResponseList = errorMetaData.map((data) => + getErrorRespEvents(data, errorObj.status, errorObj.message, errorObj.statTags), + ); + + return errorResponseList; +} + +function createFinalResponse(deleteResponse, insertResponse, updateResponse, errorResponseList) { + const finalResponse = []; + if (deleteResponse && deleteResponse.batchedRequest.length > 0) { + finalResponse.push(deleteResponse); + } + if (insertResponse && insertResponse.batchedRequest.length > 0) { + finalResponse.push(insertResponse); + } + if (updateResponse && updateResponse.batchedRequest.length > 0) { + finalResponse.push(updateResponse); + } + if (errorResponseList.length > 0) { + finalResponse.push(...errorResponseList); + } + return finalResponse; +} + +module.exports = { + getErrorResponse, + createFinalResponse, +}; diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/processor/data.ts b/test/integrations/destinations/google_adwords_remarketing_lists/processor/data.ts index a846e0370d..639e28403c 100644 --- a/test/integrations/destinations/google_adwords_remarketing_lists/processor/data.ts +++ b/test/integrations/destinations/google_adwords_remarketing_lists/processor/data.ts @@ -11,7 +11,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -70,7 +70,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -120,7 +120,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -143,7 +143,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -203,7 +203,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -239,7 +239,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -262,7 +262,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -323,7 +323,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -358,7 +358,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -381,7 +381,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -456,7 +456,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -489,7 +489,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -546,7 +546,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -579,7 +579,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -635,7 +635,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -667,7 +667,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -711,7 +711,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -743,7 +743,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -807,7 +807,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -839,7 +839,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -1425,7 +1425,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -2712,7 +2712,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -2735,7 +2735,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -2820,7 +2820,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -2887,7 +2887,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -2900,7 +2900,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -2967,7 +2967,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -2990,7 +2990,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -4113,7 +4113,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -5400,7 +5400,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -5413,7 +5413,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -6700,7 +6700,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -6723,7 +6723,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -6790,7 +6790,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -6857,7 +6857,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -6880,7 +6880,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -6944,7 +6944,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -6977,7 +6977,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -8100,7 +8100,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -9387,7 +9387,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -9400,7 +9400,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -10687,7 +10687,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -10710,7 +10710,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -10795,7 +10795,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -10862,7 +10862,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -10875,7 +10875,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -10942,7 +10942,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -10965,7 +10965,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11050,7 +11050,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11115,7 +11115,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11128,7 +11128,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11190,7 +11190,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11213,7 +11213,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11272,7 +11272,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11316,7 +11316,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11419,7 +11419,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11479,7 +11479,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11523,7 +11523,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11546,7 +11546,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11618,7 +11618,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11661,7 +11661,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11684,7 +11684,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11756,7 +11756,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11799,7 +11799,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11822,7 +11822,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11896,7 +11896,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11942,7 +11942,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11965,7 +11965,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -12039,7 +12039,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -12082,7 +12082,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/router/audience.ts b/test/integrations/destinations/google_adwords_remarketing_lists/router/audience.ts new file mode 100644 index 0000000000..233f160ad3 --- /dev/null +++ b/test/integrations/destinations/google_adwords_remarketing_lists/router/audience.ts @@ -0,0 +1,129 @@ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + Config: { + rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', + listId: '7090784486', + customerId: '7693729833', + loginCustomerId: '', + subAccount: false, + userSchema: ['email', 'phone', 'addressInfo'], + isHashRequired: true, + typeOfList: 'General', + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, +}; + +export const rETLAudienceRouterRequest: RouterTransformationRequest = { + input: [ + { + metadata: generateMetadata(1), + destination: destination, + message: { + userId: 'user 1', + anonymousId: 'anon-id-new', + event: 'event1', + type: 'audiencelist', + properties: { + listData: { + add: [ + { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + ], + }, + enablePartialFailure: true, + }, + context: { ip: '14.5.67.21', library: { name: 'http' } }, + timestamp: '2020-02-02T00:23:09.544Z', + }, + }, + { + metadata: generateMetadata(3), + destination: destination, + message: { + userId: 'user 1', + anonymousId: 'anon-id-new', + event: 'event1', + type: 'audiencelist', + properties: { + listData: { + remove: [ + { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + ], + }, + enablePartialFailure: true, + }, + context: { ip: '14.5.67.21', library: { name: 'http' } }, + timestamp: '2020-02-02T00:23:09.544Z', + }, + }, + { + metadata: generateMetadata(4), + destination: destination, + message: { + userId: 'user 1', + anonymousId: 'anon-id-new', + event: 'event1', + type: 'audiencelist', + properties: { + listData: { + remove: [ + { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + ], + add: [ + { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + ], + }, + enablePartialFailure: true, + }, + context: { ip: '14.5.67.21', library: { name: 'http' } }, + timestamp: '2020-02-02T00:23:09.544Z', + }, + }, + ], + destType: 'google_adwords_remarketing_lists', +}; + +module.exports = { + rETLAudienceRouterRequest, +}; diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/router/data.ts b/test/integrations/destinations/google_adwords_remarketing_lists/router/data.ts index 31d5c72694..35da4daff5 100644 --- a/test/integrations/destinations/google_adwords_remarketing_lists/router/data.ts +++ b/test/integrations/destinations/google_adwords_remarketing_lists/router/data.ts @@ -1,3 +1,6 @@ +import { rETLAudienceRouterRequest } from './audience'; +import { rETLRecordRouterRequest } from './record'; + export const data = [ { name: 'google_adwords_remarketing_lists', @@ -7,157 +10,85 @@ export const data = [ version: 'v0', input: { request: { + body: rETLAudienceRouterRequest, + method: 'POST', + }, + }, + output: { + response: { + status: 200, body: { - input: [ + output: [ { - metadata: { - secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', - }, - jobId: 1, - userId: 'u1', - }, - destination: { - Config: { - rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', - customerId: '7693729833', - loginCustomerId: '', - subAccount: false, - userSchema: ['email', 'phone', 'addressInfo'], - isHashRequired: true, - typeOfList: 'General', - }, - }, - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - add: [ - { - email: 'test@abc.com', - phone: '@09876543210', - firstName: 'test', - lastName: 'rudderlabs', - country: 'US', - postalCode: '1245', - }, - ], + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', + headers: { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', }, - enablePartialFailure: true, - }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', - }, - }, - { - metadata: { - secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', - }, - jobId: 2, - userId: 'u1', - }, - destination: { - Config: { - rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', - customerId: '7693729833', - loginCustomerId: '', - subAccount: false, - userSchema: ['email', 'phone', 'addressInfo'], - isHashRequired: true, - typeOfList: 'userID', - }, - }, - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - add: [ - { - email: 'test@abc.com', - phone: '@09876543210', - firstName: 'test', - lastName: 'rudderlabs', - country: 'US', - postalCode: '1245', - thirdPartyUserId: 'useri1234', - }, - ], + params: { + listId: '7090784486', + customerId: '7693729833', + consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, }, - enablePartialFailure: true, - }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', - }, - }, - { - metadata: { - secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', - }, - jobId: 3, - userId: 'u1', - }, - destination: { - Config: { - rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', - customerId: '7693729833', - loginCustomerId: '', - subAccount: false, - userSchema: ['email', 'phone', 'addressInfo'], - isHashRequired: true, - typeOfList: 'General', - }, - }, - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - remove: [ - { - email: 'test@abc.com', - phone: '@09876543210', - firstName: 'test', - lastName: 'rudderlabs', - country: 'US', - postalCode: '1245', - }, - ], + body: { + JSON: { + enablePartialFailure: true, + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: + 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: + '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: + 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, + ], + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, - enablePartialFailure: true, + files: {}, }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', - }, - }, - { - metadata: { - secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', + ], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 1, }, - jobId: 4, - userId: 'u1', - }, + ], + batched: false, + statusCode: 200, destination: { Config: { rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', @@ -169,52 +100,21 @@ export const data = [ isHashRequired: true, typeOfList: 'General', }, - }, - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - remove: [ - { - email: 'test@abc.com', - phone: '@09876543210', - firstName: 'test', - lastName: 'rudderlabs', - country: 'US', - postalCode: '1245', - }, - ], - add: [ - { - email: 'test@abc.com', - phone: '@09876543210', - firstName: 'test', - lastName: 'rudderlabs', - country: 'US', - postalCode: '1245', - }, - ], - }, - enablePartialFailure: true, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', }, }, - ], - destType: 'google_adwords_remarketing_lists', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: [ { batchedRequest: [ { @@ -222,11 +122,10 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { - Authorization: 'Bearer abcd1234', + Authorization: 'Bearer default-accessToken', 'Content-Type': 'application/json', - 'developer-token': 'ijkl9101', }, params: { listId: '7090784486', @@ -238,7 +137,7 @@ export const data = [ enablePartialFailure: true, operations: [ { - create: { + remove: { userIdentifiers: [ { hashedEmail: @@ -272,13 +171,16 @@ export const data = [ ], metadata: [ { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', + accessToken: 'default-accessToken', }, - jobId: 1, - userId: 'u1', + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 3, }, ], batched: false, @@ -294,6 +196,19 @@ export const data = [ isHashRequired: true, typeOfList: 'General', }, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + }, + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', }, }, { @@ -303,11 +218,10 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { - Authorization: 'Bearer abcd1234', + Authorization: 'Bearer default-accessToken', 'Content-Type': 'application/json', - 'developer-token': 'ijkl9101', }, params: { listId: '7090784486', @@ -318,7 +232,81 @@ export const data = [ JSON: { enablePartialFailure: true, operations: [ - { create: { userIdentifiers: [{ thirdPartyUserId: 'useri1234' }] } }, + { + remove: { + userIdentifiers: [ + { + hashedEmail: + 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: + '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: + 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, + ], + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', + headers: { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', + }, + params: { + listId: '7090784486', + customerId: '7693729833', + consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, + }, + body: { + JSON: { + enablePartialFailure: true, + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: + 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: + '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: + 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, + ], + }, + }, ], }, JSON_ARRAY: {}, @@ -330,13 +318,16 @@ export const data = [ ], metadata: [ { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', + accessToken: 'default-accessToken', }, - jobId: 2, - userId: 'u1', + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 4, }, ], batched: false, @@ -350,10 +341,45 @@ export const data = [ subAccount: false, userSchema: ['email', 'phone', 'addressInfo'], isHashRequired: true, - typeOfList: 'userID', + typeOfList: 'General', + }, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', }, + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', }, }, + ], + }, + }, + }, + }, + { + name: 'google_adwords_remarketing_lists record event tests', + description: 'Test 1', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: rETLRecordRouterRequest, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ { batchedRequest: [ { @@ -361,20 +387,21 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { - Authorization: 'Bearer abcd1234', + Authorization: 'Bearer default-accessToken', 'Content-Type': 'application/json', - 'developer-token': 'ijkl9101', }, params: { listId: '7090784486', customerId: '7693729833', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, }, body: { JSON: { - enablePartialFailure: true, operations: [ { remove: { @@ -411,16 +438,19 @@ export const data = [ ], metadata: [ { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', + accessToken: 'default-accessToken', }, - jobId: 3, - userId: 'u1', + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 1, }, ], - batched: false, + batched: true, statusCode: 200, destination: { Config: { @@ -433,6 +463,19 @@ export const data = [ isHashRequired: true, typeOfList: 'General', }, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + }, + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', }, }, { @@ -442,23 +485,24 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { - Authorization: 'Bearer abcd1234', + Authorization: 'Bearer default-accessToken', 'Content-Type': 'application/json', - 'developer-token': 'ijkl9101', }, params: { listId: '7090784486', customerId: '7693729833', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, }, body: { JSON: { - enablePartialFailure: true, operations: [ { - remove: { + create: { userIdentifiers: [ { hashedEmail: @@ -478,6 +522,24 @@ export const data = [ postalCode: '1245', }, }, + { + hashedEmail: + 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: + '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: + 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, ], }, }, @@ -489,25 +551,83 @@ export const data = [ }, files: {}, }, + ], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 2, + }, + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 3, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], + batched: true, + statusCode: 200, + destination: { + Config: { + rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', + listId: '7090784486', + customerId: '7693729833', + loginCustomerId: '', + subAccount: false, + userSchema: ['email', 'phone', 'addressInfo'], + isHashRequired: true, + typeOfList: 'General', + }, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + }, + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + }, + }, + { + batchedRequest: [ { version: '1', type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { - Authorization: 'Bearer abcd1234', + Authorization: 'Bearer default-accessToken', 'Content-Type': 'application/json', - 'developer-token': 'ijkl9101', }, params: { listId: '7090784486', customerId: '7693729833', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, }, body: { JSON: { - enablePartialFailure: true, operations: [ { create: { @@ -544,16 +664,19 @@ export const data = [ ], metadata: [ { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', + accessToken: 'default-accessToken', }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', jobId: 4, - userId: 'u1', }, ], - batched: false, + batched: true, statusCode: 200, destination: { Config: { @@ -566,6 +689,48 @@ export const data = [ isHashRequired: true, typeOfList: 'General', }, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + }, + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + }, + }, + { + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 5, + }, + ], + batched: false, + statusCode: 400, + error: 'Invalid action type in record event', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destinationId: 'default-destinationId', + destType: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + module: 'destination', + implementation: 'native', + feature: 'router', + workspaceId: 'default-workspaceId', }, }, ], diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/router/record.ts b/test/integrations/destinations/google_adwords_remarketing_lists/router/record.ts new file mode 100644 index 0000000000..743213bcc1 --- /dev/null +++ b/test/integrations/destinations/google_adwords_remarketing_lists/router/record.ts @@ -0,0 +1,158 @@ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + Config: { + rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', + listId: '7090784486', + customerId: '7693729833', + loginCustomerId: '', + subAccount: false, + userSchema: ['email', 'phone', 'addressInfo'], + isHashRequired: true, + typeOfList: 'General', + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, +}; + +export const rETLRecordRouterRequest: RouterTransformationRequest = { + input: [ + { + destination: destination, + message: { + action: 'insert', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + type: 'record', + }, + metadata: generateMetadata(2), + }, + { + destination: destination, + message: { + action: 'update', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + type: 'record', + }, + metadata: generateMetadata(4), + }, + { + destination: destination, + message: { + action: 'delete', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + type: 'record', + }, + metadata: generateMetadata(1), + }, + { + destination: destination, + message: { + action: 'lol', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + type: 'record', + }, + metadata: generateMetadata(5), + }, + { + destination: destination, + message: { + action: 'insert', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + type: 'record', + }, + metadata: generateMetadata(3), + }, + ], + destType: 'google_adwords_remarketing_lists', +}; + +module.exports = { + rETLRecordRouterRequest, +}; From 1a013627d13e0dc286f55de8925bfbb12d9a462b Mon Sep 17 00:00:00 2001 From: Utsab Chowdhury Date: Wed, 19 Jun 2024 10:46:54 +0530 Subject: [PATCH 222/240] fix: onboard custom alias support for braze (#3335) --- src/util/prometheus.js | 6 + src/v0/destinations/braze/braze.util.test.js | 119 ++++++++- src/v0/destinations/braze/transform.js | 26 +- src/v0/destinations/braze/util.js | 30 ++- .../destinations/braze/processor/data.ts | 248 ++++++++++++++++++ 5 files changed, 407 insertions(+), 22 deletions(-) diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 72f424d39a..5b7fe692b4 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -612,6 +612,12 @@ class Prometheus { type: 'counter', labelNames: ['destination_id'], }, + { + name: 'braze_alias_missconfigured_count', + help: 'braze_alias_missconfigured_count', + type: 'counter', + labelNames: ['destination_id'], + }, { name: 'mixpanel_batch_engage_pack_size', help: 'mixpanel_batch_engage_pack_size', diff --git a/src/v0/destinations/braze/braze.util.test.js b/src/v0/destinations/braze/braze.util.test.js index cc50ae921e..460f1db565 100644 --- a/src/v0/destinations/braze/braze.util.test.js +++ b/src/v0/destinations/braze/braze.util.test.js @@ -1,6 +1,6 @@ const _ = require('lodash'); const { handleHttpRequest } = require('../../../adapters/network'); -const { BrazeDedupUtility, addAppId, getPurchaseObjs } = require('./util'); +const { BrazeDedupUtility, addAppId, getPurchaseObjs, setAliasObject } = require('./util'); const { processBatch } = require('./util'); const { removeUndefinedAndNullValues, @@ -1421,3 +1421,120 @@ describe('getPurchaseObjs', () => { ]); }); }); + +describe('setAliasObject function', () => { + // Test when integrationsObj has both alias_name and alias_label + test('should set user_alias from integrationsObj if alias_name and alias_label are defined', () => { + const payload = {}; + const result = setAliasObject(payload, { + anonymousId: '12345', + integrations: { + BRAZE: { + alias: { + alias_name: 'user123', + alias_label: 'customer_id', + }, + }, + }, + }); + + expect(result).toEqual({ + user_alias: { + alias_name: 'user123', + alias_label: 'customer_id', + }, + }); + }); + + // Test when integrationsObj is missing alias_name or alias_label + test('should set user_alias with anonymousId as alias_name and "rudder_id" as alias_label if integrationsObj does not have alias_name or alias_label', () => { + const message = { + anonymousId: '12345', + }; + const payload = {}; + const result = setAliasObject(payload, message); + + expect(result).toEqual({ + user_alias: { + alias_name: '12345', + alias_label: 'rudder_id', + }, + }); + }); + + // Test when message has no anonymousId and integrationsObj is missing + test('should return payload unchanged if message has no anonymousId and integrationsObj is missing', () => { + const message = {}; + const payload = {}; + const result = setAliasObject(payload, message); + + expect(result).toEqual(payload); + }); + + test('should set user_alias from integrationsObj if alias_name and alias_label are defined', () => { + const payload = {}; + const result = setAliasObject(payload, { + anonymousId: '12345', + integrations: { + BRAZE: { + alias: { + alias_name: 'user123', + alias_label: 'customer_id', + }, + }, + }, + }); + + expect(result).toEqual({ + user_alias: { + alias_name: 'user123', + alias_label: 'customer_id', + }, + }); + }); + + test('should set user_alias from integrationsObj if alias_name and alias_label either is not defined', () => { + const payload = {}; + const result = setAliasObject(payload, { + anonymousId: '12345', + integrations: { + BRAZE: { + alias: { + alias_name: null, + alias_label: 'customer_id', + }, + }, + }, + }); + + expect(result).toEqual({ + user_alias: { + alias_name: '12345', + alias_label: 'rudder_id', + }, + }); + }); + + test('should set user_alias from integrationsObj if alias_name and alias_label either is not defined', () => { + const payload = {}; + const result = setAliasObject(payload, { + anonymousId: '12345', + userID: 'user123', + integrations: { + BRAZE: { + alias: { + alias_name: 'rudder_id-123', + alias_label: 'customer_id', + }, + }, + }, + }); + + expect(result).toEqual({ + user_alias: { + alias_name: 'rudder_id-123', + alias_label: 'customer_id', + }, + }); + }); +}); diff --git a/src/v0/destinations/braze/transform.js b/src/v0/destinations/braze/transform.js index 11b2bb0636..3d6a99d424 100644 --- a/src/v0/destinations/braze/transform.js +++ b/src/v0/destinations/braze/transform.js @@ -12,8 +12,9 @@ const { setExternalIdOrAliasObject, getPurchaseObjs, setExternalId, - setAliasObjectWithAnonId, + setAliasObject, collectStatsForAliasFailure, + collectStatsForAliasMissConfigurations, } = require('./util'); const tags = require('../../util/tags'); const { EventType, MappedToDestinationKey } = require('../../../constants'); @@ -27,6 +28,7 @@ const { simpleProcessRouterDestSync, simpleProcessRouterDest, isNewStatusCodesAccepted, + getDestinationExternalID, } = require('../../util'); const { ConfigCategory, @@ -82,7 +84,7 @@ function buildResponse(message, destination, properties, endpoint) { function getIdentifyPayload(message) { let payload = {}; - payload = setAliasObjectWithAnonId(payload, message); + payload = setAliasObject(payload, message); payload = setExternalId(payload, message); return { aliases_to_identify: [payload], merge_behavior: 'merge' }; } @@ -202,12 +204,6 @@ function getUserAttributesObject(message, mappingJson, destination) { * @param {*} destination */ async function processIdentify(message, destination) { - // override userId with externalId in context(if present) and event is mapped to destination - const mappedToDestination = get(message, MappedToDestinationKey); - if (mappedToDestination) { - adduserIdFromExternalId(message); - } - const identifyPayload = getIdentifyPayload(message); const identifyEndpoint = getIdentifyEndpoint(getEndpointFromConfig(destination)); const { processedResponse: brazeIdentifyResp } = await handleHttpRequest( @@ -511,10 +507,19 @@ async function process(event, processParams = { userStore: new Map() }, reqMetad processParams, ); break; - case EventType.IDENTIFY: + case EventType.IDENTIFY: { category = ConfigCategory.IDENTIFY; - if (message.anonymousId) { + // override userId with externalId in context(if present) and event is mapped to destination + const mappedToDestination = get(message, MappedToDestinationKey); + if (mappedToDestination) { + adduserIdFromExternalId(message); + } + const brazeExternalID = + getDestinationExternalID(message, 'brazeExternalId') || message.userId; + if (message.anonymousId && brazeExternalID) { await processIdentify(message, destination); + } else { + collectStatsForAliasMissConfigurations(destination.ID); } response = processTrackWithUserAttributes( message, @@ -524,6 +529,7 @@ async function process(event, processParams = { userStore: new Map() }, reqMetad reqMetadata, ); break; + } case EventType.GROUP: response = processGroup(message, destination); break; diff --git a/src/v0/destinations/braze/util.js b/src/v0/destinations/braze/util.js index 4253619d33..b3b29cdf96 100644 --- a/src/v0/destinations/braze/util.js +++ b/src/v0/destinations/braze/util.js @@ -519,8 +519,18 @@ function setExternalId(payload, message) { return payload; } -function setAliasObjectWithAnonId(payload, message) { - if (message.anonymousId) { +function setAliasObject(payload, message) { + const integrationsObj = getIntegrationsObj(message, 'BRAZE'); + if ( + isDefinedAndNotNull(integrationsObj?.alias?.alias_name) && + isDefinedAndNotNull(integrationsObj?.alias?.alias_label) + ) { + const { alias_name, alias_label } = integrationsObj.alias; + payload.user_alias = { + alias_name, + alias_label, + }; + } else if (message.anonymousId) { payload.user_alias = { alias_name: message.anonymousId, alias_label: 'rudder_id', @@ -537,7 +547,7 @@ function setExternalIdOrAliasObject(payload, message) { // eslint-disable-next-line no-underscore-dangle payload._update_existing_only = false; - return setAliasObjectWithAnonId(payload, message); + return setAliasObject(payload, message); } function addMandatoryPurchaseProperties(productId, price, currencyCode, quantity, timestamp) { @@ -687,16 +697,13 @@ const collectStatsForAliasFailure = (brazeResponse, destinationId) => { const { aliases_processed: aliasesProcessed, errors } = brazeResponse; if (aliasesProcessed === 0) { stats.increment('braze_alias_failure_count', { destination_id: destinationId }); - - if (Array.isArray(errors)) { - logger.info('Braze Alias Failure Errors:', { - destinationId, - errors, - }); - } } }; +const collectStatsForAliasMissConfigurations = (destinationId) => { + stats.increment('braze_alias_missconfigured_count', { destination_id: destinationId }); +}; + module.exports = { BrazeDedupUtility, CustomAttributeOperationUtil, @@ -707,7 +714,8 @@ module.exports = { getPurchaseObjs, setExternalIdOrAliasObject, setExternalId, - setAliasObjectWithAnonId, + setAliasObject, addMandatoryPurchaseProperties, collectStatsForAliasFailure, + collectStatsForAliasMissConfigurations, }; diff --git a/test/integrations/destinations/braze/processor/data.ts b/test/integrations/destinations/braze/processor/data.ts index 58c54bb232..dc5a84470f 100644 --- a/test/integrations/destinations/braze/processor/data.ts +++ b/test/integrations/destinations/braze/processor/data.ts @@ -3863,4 +3863,252 @@ export const data = [ }, }, }, + { + name: 'braze', + description: '[Test 28] Test for alias_id overriding feature for track event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + restApiKey: 'dummyApiKey', + prefixProperties: true, + useNativeSDK: false, + }, + DestinationDefinition: { + DisplayName: 'Braze', + ID: '1WhbSZ6uA3H5ChVifHpfL2H6sie', + Name: 'BRAZE', + }, + Enabled: true, + ID: '1WhcOCGgj9asZu850HvugU2C3Aq', + Name: 'Braze', + Transformations: [], + }, + message: { + anonymousId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', + channel: 'web', + context: { + traits: { + city: 'Disney', + country: 'USA', + email: 'micky@example.com', + firstname: 'Mickey', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + }, + event: 'Test Event', + integrations: { + All: true, + Braze: { + alias: { + alias_label: 'email', + alias_name: 'micky@example.com', + }, + }, + }, + messageId: 'aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a', + originalTimestamp: '2020-01-24T06:29:02.367Z', + properties: { + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', + coupon: 'hasbros', + currency: 'USD', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + revenue: 25, + shipping: 3, + subtotal: 22.5, + tax: 2, + total: 27.5, + }, + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53712', + sentAt: '2020-01-24T06:29:02.368Z', + timestamp: '2020-01-24T11:59:02.402+05:30', + type: 'track', + userId: '', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://rest.fra-01.braze.eu/users/track', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + Authorization: 'Bearer dummyApiKey', + }, + params: {}, + body: { + JSON: { + partner: 'RudderStack', + attributes: [ + { + email: 'micky@example.com', + city: 'Disney', + country: 'USA', + firstname: 'Mickey', + _update_existing_only: false, + user_alias: { + alias_name: 'micky@example.com', + alias_label: 'email', + }, + }, + ], + events: [ + { + name: 'Test Event', + time: '2020-01-24T11:59:02.402+05:30', + properties: { + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', + coupon: 'hasbros', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + revenue: 25, + shipping: 3, + subtotal: 22.5, + tax: 2, + total: 27.5, + }, + _update_existing_only: false, + user_alias: { + alias_name: 'micky@example.com', + alias_label: 'email', + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'braze', + description: + '[Test 29] Test for alias_id overriding feature for identify event (with intermediate alias call)', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + restApiKey: 'dummyApiKey', + prefixProperties: true, + useNativeSDK: false, + dataCenter: 'us-01', + }, + DestinationDefinition: { + DisplayName: 'Braze', + ID: '1WhbSZ6uA3H5ChVifHpfL2H6sie', + Name: 'BRAZE', + }, + Enabled: true, + ID: '1WhcOCGgj9asZu850HvugU2C3Aq', + Name: 'Braze', + Transformations: [], + }, + message: { + anonymousId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', + channel: 'web', + context: { + traits: { + city: 'Disney', + country: 'USA', + email: 'micky@example.com', + firstname: 'Mickey', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + }, + integrations: { + All: true, + Braze: { + alias: { + alias_label: 'email', + alias_name: 'micky@example.com', + }, + }, + }, + messageId: 'aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a', + originalTimestamp: '2020-01-24T06:29:02.367Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53712', + sentAt: '2020-01-24T06:29:02.368Z', + timestamp: '2020-01-24T11:59:02.402+05:30', + type: 'identify', + userId: 'mickeyUser', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://rest.iad-01.braze.com/users/track', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + Authorization: 'Bearer dummyApiKey', + }, + params: {}, + body: { + JSON: { + attributes: [ + { + email: 'micky@example.com', + city: 'Disney', + country: 'USA', + firstname: 'Mickey', + external_id: 'mickeyUser', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: 'mickeyUser', + }, + statusCode: 200, + }, + ], + }, + }, + }, ]; From 20020aff1743db14527842bf62e6f09f10e9ed34 Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Wed, 19 Jun 2024 11:30:49 +0530 Subject: [PATCH 223/240] chore: clean unnecessary metrics (#3475) --- src/controllers/destination.ts | 25 ------------------------- src/controllers/regulation.ts | 6 ------ src/util/prometheus.js | 13 ------------- 3 files changed, 44 deletions(-) diff --git a/src/controllers/destination.ts b/src/controllers/destination.ts index 998cab67bb..1b9d19c5af 100644 --- a/src/controllers/destination.ts +++ b/src/controllers/destination.ts @@ -19,7 +19,6 @@ import logger from '../logger'; export class DestinationController { public static async destinationTransformAtProcessor(ctx: Context) { - const startTime = new Date(); logger.debug('Native(Process-Transform):: Requst to transformer::', ctx.request.body); let resplist: ProcessorTransformationResponse[]; const requestMetadata = MiscService.getRequestMetadata(ctx); @@ -71,22 +70,10 @@ export class DestinationController { version, ...metaTags, }); - stats.timing('dest_transform_request_latency', startTime, { - destination, - feature: tags.FEATURES.PROCESSOR, - version, - ...metaTags, - }); - stats.increment('dest_transform_requests', { - destination, - version, - ...metaTags, - }); return ctx; } public static async destinationTransformAtRouter(ctx: Context) { - const startTime = new Date(); logger.debug('Native(Router-Transform):: Requst to transformer::', ctx.request.body); const requestMetadata = MiscService.getRequestMetadata(ctx); const routerRequest = ctx.request.body as RouterTransformationRequest; @@ -145,18 +132,11 @@ export class DestinationController { version: 'v0', ...metaTags, }); - stats.timing('dest_transform_request_latency', startTime, { - destination, - version: 'v0', - feature: tags.FEATURES.ROUTER, - ...metaTags, - }); return ctx; } public static batchProcess(ctx: Context) { logger.debug('Native(Process-Transform-Batch):: Requst to transformer::', ctx.request.body); - const startTime = new Date(); const requestMetadata = MiscService.getRequestMetadata(ctx); const routerRequest = ctx.request.body as RouterTransformationRequest; const destination = routerRequest.destType; @@ -187,11 +167,6 @@ export class DestinationController { ctx.body = [errResp]; } ControllerUtility.postProcess(ctx); - stats.timing('dest_transform_request_latency', startTime, { - destination, - feature: tags.FEATURES.BATCH, - version: 'v0', - }); return ctx; } } diff --git a/src/controllers/regulation.ts b/src/controllers/regulation.ts index 2d40c518f4..ecb55ff7a3 100644 --- a/src/controllers/regulation.ts +++ b/src/controllers/regulation.ts @@ -2,7 +2,6 @@ import { Context } from 'koa'; import { ServiceSelector } from '../helpers/serviceSelector'; import { DestinationPostTransformationService } from '../services/destination/postTransformation'; import { UserDeletionRequest, UserDeletionResponse } from '../types'; -import stats from '../util/stats'; import tags from '../v0/util/tags'; // eslint-disable-next-line @typescript-eslint/no-unused-vars import { CatchErr } from '../util/types'; @@ -11,7 +10,6 @@ import logger from '../logger'; export class RegulationController { public static async deleteUsers(ctx: Context) { logger.debug('Native(Process-Transform):: Requst to transformer::', ctx.request.body); - const startTime = new Date(); let rudderDestInfo: any; try { const rudderDestInfoHeader = ctx.get('x-rudder-dest-info'); @@ -47,10 +45,6 @@ export class RegulationController { ctx.body = [{ error, statusCode: 500 }] as UserDeletionResponse[]; // TODO: responses array length is always 1. Is that okay? ctx.status = 500; } - stats.timing('dest_transform_request_latency', startTime, { - feature: tags.FEATURES.USER_DELETION, - version: 'v0', - }); return ctx; } } diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 5b7fe692b4..fe9a2af107 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -721,19 +721,6 @@ class Prometheus { type: 'histogram', labelNames: ['feature', 'implementation', 'destType'], }, - { - name: 'dest_transform_request_latency', - help: 'dest_transform_request_latency', - type: 'histogram', - labelNames: [ - 'destination', - 'version', - 'sourceType', - 'destinationType', - 'k8_namespace', - 'feature', - ], - }, { name: 'user_transform_request_latency', help: 'user_transform_request_latency', From 3db810c857017318c1979965ce653eca3b3fdadf Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Wed, 19 Jun 2024 14:29:37 +0530 Subject: [PATCH 224/240] fix: tests names, credentialsMap refactor --- src/legacy/router.js | 1 - src/services/userTransform.ts | 14 +- src/util/customTransformer-v1.js | 7 +- src/util/customTransformer.js | 18 --- src/util/customTransformerFactory.js | 10 +- src/util/ivmFactory.js | 4 +- src/util/prometheus.js | 2 +- .../user_transformation.integration.test.js | 2 +- test/__tests__/user_transformation.test.js | 132 +++++++++--------- .../user_transformation_errors.test.js | 8 -- .../user_transformation_fetch.test.js | 24 ++-- 11 files changed, 100 insertions(+), 122 deletions(-) diff --git a/src/legacy/router.js b/src/legacy/router.js index b2c50660f4..afc8c1a797 100644 --- a/src/legacy/router.js +++ b/src/legacy/router.js @@ -678,7 +678,6 @@ if (transformerTestModeEnabled) { trRevCode.versionId, libraryVersionIDs, trRevCode, - null, true, ); logger.debug(`[CT] Test Output Events: ${JSON.stringify(res.transformedEvents)}`); diff --git a/src/services/userTransform.ts b/src/services/userTransform.ts index e03aebc290..83e0c807d6 100644 --- a/src/services/userTransform.ts +++ b/src/services/userTransform.ts @@ -1,5 +1,6 @@ import groupBy from 'lodash/groupBy'; import isEmpty from 'lodash/isEmpty'; +import { isNil } from 'lodash'; import { userTransformHandler } from '../routerUtils'; import { UserTransformationLibrary, @@ -213,15 +214,24 @@ export class UserTransformService { throw new Error('Invalid request. Missing events'); } + const updatedEvents = events.map((ev) => { + if (isNil(ev.credentials)) { + return { + ...ev, + credentials, + }; + } + return ev; + }); + logger.debug(`[CT] Test Input Events: ${JSON.stringify(events)}`); // eslint-disable-next-line no-param-reassign trRevCode.versionId = 'testVersionId'; response.body = await userTransformHandler()( - events, + updatedEvents, trRevCode.versionId, libraryVersionIDs, trRevCode, - credentials, true, ); logger.debug(`[CT] Test Output Events: ${JSON.stringify(response.body.transformedEvents)}`); diff --git a/src/util/customTransformer-v1.js b/src/util/customTransformer-v1.js index e0212831ca..b1865dee6b 100644 --- a/src/util/customTransformer-v1.js +++ b/src/util/customTransformer-v1.js @@ -51,7 +51,6 @@ function calculateMsFromIvmTime(value) { async function userTransformHandlerV1( events, userTransformation, - credentials, libraryVersionIds, testMode = false, ) { @@ -59,13 +58,17 @@ async function userTransformHandlerV1( return { transformedEvents: events }; } + const credentialsMap = {}; + (events[0]?.credentials || []).forEach((cred) => { + credentialsMap[cred.key] = cred.value; + }); const isolatevmFactory = await getFactory( userTransformation.code, libraryVersionIds, userTransformation.versionId, userTransformation.id, userTransformation.workspaceId, - credentials, + credentialsMap, userTransformation.secrets || {}, testMode, ); diff --git a/src/util/customTransformer.js b/src/util/customTransformer.js index a7b52b004c..787ce04d63 100644 --- a/src/util/customTransformer.js +++ b/src/util/customTransformer.js @@ -8,7 +8,6 @@ const { parserForImport } = require('./parser'); const stats = require('./stats'); const { fetchWithDnsWrapper } = require('./utils'); const { getMetadata, getTransformationMetadata } = require('../v0/util'); -const { isNil } = require('lodash'); const ISOLATE_VM_MEMORY = parseInt(process.env.ISOLATE_VM_MEMORY || '128', 10); const GEOLOCATION_TIMEOUT_IN_MS = parseInt(process.env.GEOLOCATION_TIMEOUT_IN_MS || '1000', 10); @@ -274,7 +273,6 @@ async function userTransformHandler( versionId, libraryVersionIDs, trRevCode = {}, - credentials = [], testMode = false, ) { if (versionId) { @@ -287,27 +285,11 @@ async function userTransformHandler( events.forEach((ev) => { eventsMetadata[ev.message.messageId] = ev.metadata; }); - const credentialsMap = {}; - if (testMode === false) { - (events[0]?.credentials || []).forEach((cred) => { - credentialsMap[cred.key] = cred.value; - }); - } else { - (credentials || []).forEach((cred) => { - credentialsMap[cred.key] = cred.value; - }); - events.forEach((ev) => { - if (isNil(ev.credentials)) { - ev.credentials = credentials; - } - }); - } let userTransformedEvents = []; let result; if (res.codeVersion && res.codeVersion === '1') { result = await UserTransformHandlerFactory(res).runUserTransfrom( events, - credentialsMap, testMode, libraryVersionIDs, ); diff --git a/src/util/customTransformerFactory.js b/src/util/customTransformerFactory.js index f6bbbc492c..ee53531946 100644 --- a/src/util/customTransformerFactory.js +++ b/src/util/customTransformerFactory.js @@ -13,20 +13,14 @@ const UserTransformHandlerFactory = (userTransformation) => { } }, - runUserTransfrom: async (events, credentials, testMode, libraryVersionIds) => { + runUserTransfrom: async (events, testMode, libraryVersionIds) => { switch (userTransformation.language) { case 'pythonfaas': case 'python': return runOpenFaasUserTransform(events, userTransformation, libraryVersionIds, testMode); default: - return userTransformHandlerV1( - events, - userTransformation, - credentials, - libraryVersionIds, - testMode, - ); + return userTransformHandlerV1(events, userTransformation, libraryVersionIds, testMode); } }, }; diff --git a/src/util/ivmFactory.js b/src/util/ivmFactory.js index b31c87c049..84eb52f0c8 100644 --- a/src/util/ivmFactory.js +++ b/src/util/ivmFactory.js @@ -255,7 +255,7 @@ async function createIvm( await jail.set('_credential', function (key) { if (isNil(credentials) || !isObject(credentials)) { logger.error('Error fetching credentials map', versionId); - stats.increment('credential_errors', { transformationId, workspaceId }); + stats.increment('credential_error_total', { transformationId, workspaceId }); return undefined; } return credentials[key]; @@ -344,7 +344,7 @@ async function createIvm( global.credential = function(...args) { const key = args[0]; if (key === null || key === undefined) { - throw new Error('Key should be valid and defined'+ JSON.stringify(args)); + throw new TypeError('Key should be valid and defined'); } return credential(new ivm.ExternalCopy(key).copyInto()); }; diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 0f8a1f4e0c..d8a2a0cb04 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -570,7 +570,7 @@ class Prometheus { ], }, { - name: 'credential_errors', + name: 'credential_error_total', help: 'Error in fetching credentials count', type: 'counter', labelNames: ['transformationId', 'workspaceId'], diff --git a/test/__tests__/user_transformation.integration.test.js b/test/__tests__/user_transformation.integration.test.js index 712e151a45..6598b9fa0d 100644 --- a/test/__tests__/user_transformation.integration.test.js +++ b/test/__tests__/user_transformation.integration.test.js @@ -166,7 +166,7 @@ describe("Function invocation & creation tests", () => { // Test with language python; should return same output trRevCode = contructTrRevCode(versionId, 'python'); - response = await userTransformHandler(inputEvents, versionId, [], trRevCode, [], true); + response = await userTransformHandler(inputEvents, versionId, [], trRevCode, true); expect(response).toEqual(outputEvents); }); diff --git a/test/__tests__/user_transformation.test.js b/test/__tests__/user_transformation.test.js index 574a5417ca..24ed1ae1ff 100644 --- a/test/__tests__/user_transformation.test.js +++ b/test/__tests__/user_transformation.test.js @@ -1073,7 +1073,6 @@ describe("User transformation", () => { trRevCode.versionId, [libraryVersionId], trRevCode, - [], true ); @@ -1110,14 +1109,13 @@ describe("User transformation", () => { trRevCode.versionId, [], trRevCode, - [], true ); expect(output).toEqual(expectedData); }); - describe("Credentials", () => { - it(`Simple ${name} Test with credentials for codeVersion 1`, async () => { + describe("UserTransformation With Credentials for code version 1", () => { + it(`successfully executes transformation with credential lookup with valid key`, async () => { const versionId = randomID(); const inputData = require(`./data/${integration}_input_credentials.json`); @@ -1145,7 +1143,7 @@ describe("User transformation", () => { expect(output[0].transformedEvent.credentialValue).toEqual("value1"); }); - it(`Simple ${name} Test with credentials without key for codeVersion 1`, async () => { + it(`throws TypeError if the key provided for credential lookup is null or undefined`, async () => { const versionId = randomID(); const inputData = require(`./data/${integration}_input_credentials.json`); @@ -1174,7 +1172,7 @@ describe("User transformation", () => { expect(output[0].error).toMatch(/Key should be valid and defined/); }); - it(`Simple ${name} Test with credentials with multiple arguments for codeVersion 1`, async () => { + it(`allows user to pass multiple arguments to functions and performs lookup with first key passed`, async () => { const versionId = randomID(); const inputData = require(`./data/${integration}_input_credentials.json`); @@ -1203,7 +1201,7 @@ describe("User transformation", () => { expect(output[0].transformedEvent.credentialValue).toEqual("value1"); }); - it(`Simple ${name} Test with credentials with non string key for codeVersion 1`, async () => { + it(`allows user to pass valid key of any type and performs lookup accordingly`, async () => { const versionId = randomID(); const inputData = require(`./data/${integration}_input_credentials.json`); @@ -1238,7 +1236,7 @@ describe("User transformation", () => { expect(output[0].transformedEvent.credentialValueForObjkey).toBeUndefined(); }); - it(`Simple ${name} Test with credentials without value for codeVersion 1`, async () => { + it(`returns undefined when the key doesn't match any credential lookup`, async () => { const versionId = randomID(); const inputData = require(`./data/${integration}_input_credentials.json`); @@ -1267,7 +1265,7 @@ describe("User transformation", () => { expect(output[0].transformedEvent.credentialValue).toBeUndefined(); }); - it(`Simple ${name} Test without credentials for codeVersion 1`, async () => { + it(`returns undefined when the credentials are not passed in the request`, async () => { const versionId = randomID(); const inputData = require(`./data/${integration}_input.json`); @@ -1296,69 +1294,72 @@ describe("User transformation", () => { expect(output[0].transformedEvent.credentialValue).toBeUndefined(); }); - it(`Simple ${name} Batch Test with credentials for codeVersion 1`, async () => { - const versionId = randomID(); - - const inputData = require(`./data/${integration}_input_credentials.json`); + describe('Batch UserTransformation with Credentials', () => { + it(`successfully executes transformation with credential lookup with valid key`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformBatch(events, metadata) { + events.forEach((event) => { + event.credentialValue1 = credential("key1"); + event.credentialValue2 = credential("key3"); + }); + return events; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); - const respBody = { - versionId: versionId, - codeVersion: "1", - name, - code: ` - export function transformBatch(events, metadata) { - events.forEach((event) => { - event.credentialValue1 = credential("key1"); - event.credentialValue2 = credential("key3"); - }); - return events; - } - ` - }; - fetch.mockResolvedValue({ - status: 200, - json: jest.fn().mockResolvedValue(respBody) + const output = await userTransformHandler(inputData, versionId, []); + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].transformedEvent.credentialValue1).toEqual("value1"); + expect(output[0].transformedEvent.credentialValue2).toBeUndefined(); }); - const output = await userTransformHandler(inputData, versionId, []); - expect(fetch).toHaveBeenCalledWith( - `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` - ); - expect(output[0].transformedEvent.credentialValue1).toEqual("value1"); - expect(output[0].transformedEvent.credentialValue2).toBeUndefined(); - }); - - it(`Simple ${name} Batch Test with credentials without key for codeVersion 1`, async () => { - const versionId = randomID(); - - const inputData = require(`./data/${integration}_input_credentials.json`); + it(`throws TypeError if the key provided for credential lookup is null or undefined`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformBatch(events, metadata) { + events.forEach((event) => { + event.credentialValue = credential(); + }); + return events; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); - const respBody = { - versionId: versionId, - codeVersion: "1", - name, - code: ` - export function transformBatch(events, metadata) { - events.forEach((event) => { - event.credentialValue = credential(); - }); - return events; - } - ` - }; - fetch.mockResolvedValue({ - status: 200, - json: jest.fn().mockResolvedValue(respBody) + const output = await userTransformHandler(inputData, versionId, []); + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].error).toMatch(/Key should be valid and defined/); }); - const output = await userTransformHandler(inputData, versionId, []); - expect(fetch).toHaveBeenCalledWith( - `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` - ); - expect(output[0].error).toMatch(/Key should be valid and defined/); }); - it(`Simple ${name} Test with credentials for codeVersion 0`, async () => { + it(`throws error when credentials function is used with code version 0`, async () => { const versionId = randomID(); const inputData = require(`./data/${integration}_input_credentials.json`); @@ -1804,7 +1805,6 @@ describe("Python transformations", () => { trRevCode.versionId, [], trRevCode, - [], true ); expect(output).toEqual(outputData); @@ -1850,7 +1850,6 @@ describe("Python transformations", () => { trRevCode.versionId, Object.values(importNameLibraryVersionIdsMap), trRevCode, - [], true ); expect(output).toEqual(outputData); @@ -1892,7 +1891,6 @@ describe("Python transformations", () => { trRevCode.versionId, Object.values(importNameLibraryVersionIdsMap), trRevCode, - [], true ); expect(output).toEqual(outputData); diff --git a/test/__tests__/user_transformation_errors.test.js b/test/__tests__/user_transformation_errors.test.js index eb171a1e4d..c2a99ce09d 100644 --- a/test/__tests__/user_transformation_errors.test.js +++ b/test/__tests__/user_transformation_errors.test.js @@ -47,7 +47,6 @@ describe("JS Transformation Error Tests", () => { versionId, [], trRevCode, - null, true, ); @@ -66,7 +65,6 @@ describe("JS Transformation Error Tests", () => { versionId, [], trRevCode, - null, true, ); @@ -104,7 +102,6 @@ describe("JS Transformation Error Tests", () => { versionId, [libVid], trRevCode, - null, true, ); @@ -143,7 +140,6 @@ describe("JS Transformation Error Tests", () => { versionId, [libVid], trRevCode, - null, true, ); @@ -164,7 +160,6 @@ describe("JS Transformation Error Tests", () => { versionId, [], trRevCode, - null, true, ); @@ -186,7 +181,6 @@ describe("JS Transformation Error Tests", () => { versionId, [], trRevCode, - null, true, ); @@ -224,7 +218,6 @@ describe("JS Transformation Error Tests", () => { versionId, [libVid], trRevCode, - null, true, ); @@ -263,7 +256,6 @@ describe("JS Transformation Error Tests", () => { versionId, [libVid], trRevCode, - null, true, ); diff --git a/test/__tests__/user_transformation_fetch.test.js b/test/__tests__/user_transformation_fetch.test.js index 5bf9d54935..e37b42ea1e 100644 --- a/test/__tests__/user_transformation_fetch.test.js +++ b/test/__tests__/user_transformation_fetch.test.js @@ -43,7 +43,7 @@ describe("User transformation fetch tests", () => { ` }; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); expect(output).toEqual({ logs: [], transformedEvents: expectedData.map(ev => (ev.transformedEvent)) @@ -67,7 +67,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "ERROR"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); expect(mockResolver).toHaveBeenCalledTimes(0); output.transformedEvents.forEach(ev => { @@ -92,7 +92,7 @@ describe("User transformation fetch tests", () => { const errMsg = "ERROR"; mockResolver.mockResolvedValue([ '127.0.0.1' ]); - const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); expect(mockResolver).toHaveBeenCalledTimes(inputData.length); expect(mockResolver).toHaveBeenCalledWith('abc.xyz.com'); @@ -122,7 +122,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "invalid url, localhost requests are not allowed"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); expect(mockResolver).toHaveBeenCalledTimes(0); output.transformedEvents.forEach(ev => { @@ -152,7 +152,7 @@ describe("User transformation fetch tests", () => { const errMsg = "request to https://abc.xyz.com/dummyUrl failed, reason: Invalid IP address: unable to resolve IP address for abc.xyz.com"; mockResolver.mockRejectedValue('invalid host'); - const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); expect(mockResolver).toHaveBeenCalledTimes(inputData.length); expect(mockResolver).toHaveBeenCalledWith('abc.xyz.com'); @@ -182,7 +182,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "invalid protocol, only http and https are supported"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); output.transformedEvents.forEach(ev => { expect(ev.errMsg).toEqual(errMsg); @@ -204,7 +204,7 @@ describe("User transformation fetch tests", () => { versionId, }; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); expect(output).toEqual({ logs: [], transformedEvents: expectedData.map(ev => (ev.transformedEvent)) @@ -226,7 +226,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "ERROR"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); expect(mockResolver).toHaveBeenCalledTimes(0); output.transformedEvents.forEach(ev => { @@ -250,7 +250,7 @@ describe("User transformation fetch tests", () => { const errMsg = "ERROR"; mockResolver.mockRejectedValue('invalid host'); - const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); expect(mockResolver).toHaveBeenCalledTimes(inputData.length); expect(mockResolver).toHaveBeenCalledWith('abc.xyz.com'); @@ -279,7 +279,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "invalid url, localhost requests are not allowed"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); expect(mockResolver).toHaveBeenCalledTimes(0); output.transformedEvents.forEach(ev => { @@ -308,7 +308,7 @@ describe("User transformation fetch tests", () => { const errMsg = "request to https://abc.xyz.com/dummyUrl failed, reason: Invalid IP address: cannot use 127.0.0.1 as IP address"; mockResolver.mockResolvedValue(['3.122.122.122', '127.0.0.1']); - const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); expect(mockResolver).toHaveBeenCalledTimes(inputData.length); expect(mockResolver).toHaveBeenCalledWith('abc.xyz.com'); @@ -337,7 +337,7 @@ describe("User transformation fetch tests", () => { }; const errMsg = "fetch url is required"; - const output = await userTransformHandler(inputData, versionId, [], trRevCode, [], true); + const output = await userTransformHandler(inputData, versionId, [], trRevCode, true); output.transformedEvents.forEach(ev => { expect(ev.errMsg).toEqual(errMsg); From 417a83ce8a17d3a9d64943c896d9db3af01ee2d6 Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Wed, 19 Jun 2024 14:38:26 +0530 Subject: [PATCH 225/240] chore: upgrade packages --- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- src/v0/util/index.js | 8 +------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index c1ca7a6ca6..d2bc9b452b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,8 +20,8 @@ "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.10", - "@rudderstack/json-template-engine": "^0.13.8", - "@rudderstack/workflow-engine": "^0.8.6", + "@rudderstack/json-template-engine": "^0.14.1", + "@rudderstack/workflow-engine": "^0.8.8", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", @@ -4526,17 +4526,17 @@ } }, "node_modules/@rudderstack/json-template-engine": { - "version": "0.13.8", - "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.13.8.tgz", - "integrity": "sha512-2bl6a25SEm+LapdNqR5QhLv61dQiv3squmCr4Qy9U89BIp9yX9WOL8tvoaIS1isN7lwIOViD3cD8ft0ehlj8Sw==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.14.1.tgz", + "integrity": "sha512-frQ0UyN/hslA9K4cFyHLvLLTxHFhcWZRnF1ELbAHvj8AZJCblM8LZXh4A62aMmF0FbNqgsO5CeOStPqvYm3ugg==" }, "node_modules/@rudderstack/workflow-engine": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.8.6.tgz", - "integrity": "sha512-O+Kj3y4DZNgV2fWAOkwYwqsnjot0YxiVrDFWUkLdFQHTSXJ5Y/eqDJglDXdcpwSPg9AlaUFVHsGzw6BkAQLcIA==", + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.8.8.tgz", + "integrity": "sha512-6vX13wWlfQxFktq8MnAHTBpKulrjwYyZ8ZXcyugEU/BuVedtBTfjGGFsBrlKldLPuRsTZt8XuYbW8Ua7g2C5Kw==", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", - "@rudderstack/json-template-engine": "^0.13.8", + "@rudderstack/json-template-engine": "^0.14.1", "jsonata": "^2.0.5", "lodash": "^4.17.21", "object-sizeof": "^2.6.4", diff --git a/package.json b/package.json index 5b0d74c650..adeee0612b 100644 --- a/package.json +++ b/package.json @@ -65,8 +65,8 @@ "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.10", - "@rudderstack/json-template-engine": "^0.13.8", - "@rudderstack/workflow-engine": "^0.8.6", + "@rudderstack/json-template-engine": "^0.14.1", + "@rudderstack/workflow-engine": "^0.8.8", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", diff --git a/src/v0/util/index.js b/src/v0/util/index.js index 389b93a7af..b5ae74d480 100644 --- a/src/v0/util/index.js +++ b/src/v0/util/index.js @@ -2248,15 +2248,9 @@ const validateEventAndLowerCaseConversion = (event, isMandatory, convertToLowerC return convertToLowerCase ? event.toString().toLowerCase() : event.toString(); }; -const applyCustomMappings = (message, mappings) => { - const flatMappings = mappings.map((mapping) => ({ - input: mapping.from, - output: mapping.to, - })); - return JsonTemplateEngine.createAsSync(flatMappings, { defaultPathType: PathType.JSON }).evaluate( +const applyCustomMappings = (message, mappings) => JsonTemplateEngine.createAsSync(mappings, { defaultPathType: PathType.JSON }).evaluate( message, ); -}; // ======================================================================== // EXPORTS From 136ca64b3d8b1949950f98d4431a9670e22810cf Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Wed, 19 Jun 2024 15:31:48 +0530 Subject: [PATCH 226/240] fix: lint issues --- src/v0/util/index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/v0/util/index.js b/src/v0/util/index.js index b5ae74d480..366c58ce93 100644 --- a/src/v0/util/index.js +++ b/src/v0/util/index.js @@ -2248,9 +2248,8 @@ const validateEventAndLowerCaseConversion = (event, isMandatory, convertToLowerC return convertToLowerCase ? event.toString().toLowerCase() : event.toString(); }; -const applyCustomMappings = (message, mappings) => JsonTemplateEngine.createAsSync(mappings, { defaultPathType: PathType.JSON }).evaluate( - message, - ); +const applyCustomMappings = (message, mappings) => + JsonTemplateEngine.createAsSync(mappings, { defaultPathType: PathType.JSON }).evaluate(message); // ======================================================================== // EXPORTS From 7b0ea8040213b4bf7b73306743bd1414dc8e5b46 Mon Sep 17 00:00:00 2001 From: kanishkkatara Date: Wed, 19 Jun 2024 16:14:02 +0530 Subject: [PATCH 227/240] refactor: change in credentials function validation code --- src/util/ivmFactory.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/util/ivmFactory.js b/src/util/ivmFactory.js index 84eb52f0c8..44beff01dc 100644 --- a/src/util/ivmFactory.js +++ b/src/util/ivmFactory.js @@ -254,10 +254,15 @@ async function createIvm( await jail.set('_credential', function (key) { if (isNil(credentials) || !isObject(credentials)) { - logger.error('Error fetching credentials map', versionId); + logger.error( + `Error fetching credentials map for transformationID: ${transformationId} and workspaceId: ${workspaceId}`, + ); stats.increment('credential_error_total', { transformationId, workspaceId }); return undefined; } + if (key === null || key === undefined) { + throw new TypeError('Key should be valid and defined'); + } return credentials[key]; }); @@ -343,9 +348,6 @@ async function createIvm( delete _credential; global.credential = function(...args) { const key = args[0]; - if (key === null || key === undefined) { - throw new TypeError('Key should be valid and defined'); - } return credential(new ivm.ExternalCopy(key).copyInto()); }; From dc8eae2e8d87df8bb02cdf2662a6003289599525 Mon Sep 17 00:00:00 2001 From: Manish Kumar <144022547+manish339k@users.noreply.github.com> Date: Wed, 19 Jun 2024 17:49:44 +0530 Subject: [PATCH 228/240] feat: add tags to a company in intercom (#3434) * feat: add tags to a company * feat: modified group call to attach user and add tags to company * feat: added some tests * feat: added error handler * fix: addressing comments * chore: refactoring tests * fix: some minor changes in tests --- src/cdk/v2/destinations/intercom/config.js | 2 + .../destinations/intercom/procWorkflow.yaml | 25 + .../v2/destinations/intercom/rtWorkflow.yaml | 2 +- src/cdk/v2/destinations/intercom/utils.js | 138 +- .../v2/destinations/intercom/utils.test.js | 262 +++ .../destinations/intercom/network.ts | 362 ++++ .../destinations/intercom/router/data.ts | 1450 ++++++++--------- 7 files changed, 1494 insertions(+), 747 deletions(-) diff --git a/src/cdk/v2/destinations/intercom/config.js b/src/cdk/v2/destinations/intercom/config.js index 518d805d41..c3b806f9f9 100644 --- a/src/cdk/v2/destinations/intercom/config.js +++ b/src/cdk/v2/destinations/intercom/config.js @@ -4,6 +4,7 @@ const BASE_AU_ENDPOINT = 'https://api.au.intercom.io'; const SEARCH_CONTACT_ENDPOINT = 'contacts/search'; const CREATE_OR_UPDATE_COMPANY_ENDPOINT = 'companies'; +const TAGS_ENDPOINT = 'tags'; const ReservedAttributes = { v1UserAttributes: [ @@ -78,4 +79,5 @@ module.exports = { SEARCH_CONTACT_ENDPOINT, ReservedCompanyProperties, CREATE_OR_UPDATE_COMPANY_ENDPOINT, + TAGS_ENDPOINT, }; diff --git a/src/cdk/v2/destinations/intercom/procWorkflow.yaml b/src/cdk/v2/destinations/intercom/procWorkflow.yaml index 0a8842d5e7..008b22ace5 100644 --- a/src/cdk/v2/destinations/intercom/procWorkflow.yaml +++ b/src/cdk/v2/destinations/intercom/procWorkflow.yaml @@ -16,6 +16,8 @@ bindings: - name: addExternalIdToTraits path: ../../../../v0/util - path: ../../bindings/jsontemplate + - name: HTTP_STATUS_CODES + path: ../../../../v0/util/constant steps: - name: checkIfProcessed @@ -182,10 +184,17 @@ steps: id: companyId, }; $.context.endpoint = $.getBaseEndpoint(.destination) + "/" + "contacts" + "/" + contactId + "/" + "companies"; + const payload = $.context.payload; + const endpoint = $.context.endpoint; + await $.attachContactToCompany(payload, endpoint, .destination); + await $.addOrUpdateTagsToCompany(.message, .destination, companyId); else: name: whenSearchContactNotFound template: | + const companyId = await $.createOrUpdateCompany($.context.payload, .destination); + $.assert(companyId, "Unable to create or update company"); $.context.endpoint = $.getBaseEndpoint(.destination) + "/" + "companies"; + await $.addOrUpdateTagsToCompany(.message, .destination, companyId); - name: prepareFinalPayload template: | $.context.requestMethod = 'POST'; @@ -208,9 +217,25 @@ steps: response.method = "POST"; response.userId = .message.anonymousId; $.context.response.push(response); + payload = response.body.JSON; + const companyId = await $.createOrUpdateCompany(payload, .destination); + $.assert(companyId, "Unable to create or update company"); const attachUserAndCompanyResponse = $.attachUserAndCompany(.message, .destination.Config); attachUserAndCompanyResponse ? attachUserAndCompanyResponse.userId = .message.anonymousId; attachUserAndCompanyResponse ? $.context.response.push(attachUserAndCompanyResponse); + payload = attachUserAndCompanyResponse.body.JSON; + let endpoint = attachUserAndCompanyResponse.endpoint; + attachUserAndCompanyResponse ? await $.attachContactToCompany(payload, endpoint, .destination); + await $.addOrUpdateTagsToCompany(.message, .destination, companyId); + + - name: statusCode + condition: $.outputs.messageType === {{$.EventType.GROUP}} + template: | + $.HTTP_STATUS_CODES.SUPPRESS_EVENTS + else: + name: successStatusCode + template: | + $.HTTP_STATUS_CODES.OK - name: buildResponseForProcessTransformation description: Build response for multiple transformed event diff --git a/src/cdk/v2/destinations/intercom/rtWorkflow.yaml b/src/cdk/v2/destinations/intercom/rtWorkflow.yaml index edb7267b84..65c3a4976d 100644 --- a/src/cdk/v2/destinations/intercom/rtWorkflow.yaml +++ b/src/cdk/v2/destinations/intercom/rtWorkflow.yaml @@ -21,7 +21,7 @@ steps: "batched": false, "destination": ^[idx].destination, "metadata": ^[idx].metadata[], - "statusCode": 200 + "statusCode": .outputs.statusCode })[] - name: failedEvents template: | diff --git a/src/cdk/v2/destinations/intercom/utils.js b/src/cdk/v2/destinations/intercom/utils.js index ba3063c9f9..7e609d5c96 100644 --- a/src/cdk/v2/destinations/intercom/utils.js +++ b/src/cdk/v2/destinations/intercom/utils.js @@ -1,6 +1,10 @@ const md5 = require('md5'); const get = require('get-value'); -const { NetworkError } = require('@rudderstack/integrations-lib'); +const { + NetworkError, + ConfigurationError, + InstrumentationError, +} = require('@rudderstack/integrations-lib'); const tags = require('../../../../v0/util/tags'); const { httpPOST } = require('../../../../adapters/network'); const { @@ -27,8 +31,42 @@ const { SEARCH_CONTACT_ENDPOINT, ReservedCompanyProperties, CREATE_OR_UPDATE_COMPANY_ENDPOINT, + TAGS_ENDPOINT, } = require('./config'); +/** + * method to handle error during api call + * ref docs: https://developers.intercom.com/docs/references/rest-api/errors/error-codes/ + * https://developers.intercom.com/docs/references/rest-api/errors/error-objects/ + * https://developers.intercom.com/docs/references/rest-api/errors/http-responses/ + * e.g. + * 400 - code: parameter_not_found (or parameter_invalid), message: company not specified + * 401 - code: unauthorized, message: Access Token Invalid + * 404 - code: company_not_found, message: Company Not Found + * @param {*} message + * @param {*} processedResponse + */ +const intercomErrorHandler = (message, processedResponse) => { + const errorMessages = JSON.stringify(processedResponse.response); + if (processedResponse.status === 400) { + throw new InstrumentationError(`${message} : ${errorMessages}`); + } + if (processedResponse.status === 401) { + throw new ConfigurationError(`${message} : ${errorMessages}`); + } + if (processedResponse.status === 404) { + throw new InstrumentationError(`${message} : ${errorMessages}`); + } + throw new NetworkError( + `${message} : ${errorMessages}`, + processedResponse.status, + { + [tags]: getDynamicErrorType(processedResponse.status), + }, + processedResponse, + ); +}; + /** * Returns destination request headers * @param {*} destination @@ -285,7 +323,8 @@ const searchContact = async (message, destination) => { * @returns */ const createOrUpdateCompany = async (payload, destination) => { - const headers = getHeaders(destination); + const { apiVersion } = destination.Config; + const headers = getHeaders(destination, apiVersion); const finalPayload = JSON.stringify(removeUndefinedAndNullValues(payload)); const baseEndPoint = getBaseEndpoint(destination); const endpoint = `${baseEndPoint}/${CREATE_OR_UPDATE_COMPANY_ENDPOINT}`; @@ -369,6 +408,99 @@ const addMetadataToPayload = (payload) => { return finalPayload; }; +/** + * Api call to attach user to the company + * Ref doc v1: https://developers.intercom.com/docs/references/1.4/rest-api/users/companies-and-users/ + * Ref doc v2: https://developers.intercom.com/docs/references/2.10/rest-api/api.intercom.io/Contacts/attachContactToACompany/ + * @param {*} payload + * @param {*} endpoint + * @param {*} destination + */ +const attachContactToCompany = async (payload, endpoint, destination) => { + let { apiVersion } = destination.Config; + apiVersion = isDefinedAndNotNull(apiVersion) ? apiVersion : 'v2'; + let endpointPath = '/contact/{id}/companies'; + if (apiVersion === 'v1') { + endpointPath = '/users'; + } + const commonStatTags = { + destType: 'intercom', + feature: 'transformation', + requestMethod: 'POST', + module: 'router', + }; + const headers = getHeaders(destination, apiVersion); + const finalPayload = JSON.stringify(removeUndefinedAndNullValues(payload)); + const response = await httpPOST( + endpoint, + finalPayload, + { + headers, + }, + { + ...commonStatTags, + endpointPath, + }, + ); + + const processedResponse = processAxiosResponse(response); + if (!isHttpStatusSuccess(processedResponse.status)) { + intercomErrorHandler('Unable to attach Contact or User to Company due to', processedResponse); + } +}; + +/** + * Api calls to add or update tags to the company + * Ref doc v1: https://developers.intercom.com/docs/references/1.4/rest-api/tags/tag-or-untag-users-companies-leads-contacts/ + * Ref doc v2: https://developers.intercom.com/docs/references/2.10/rest-api/api.intercom.io/Tags/createTag/ + * @param message + * @param destination + * @param id + * @returns + */ +const addOrUpdateTagsToCompany = async (message, destination, id) => { + const companyTags = message?.context?.traits?.tags; + if (!companyTags) return; + const { apiVersion } = destination.Config; + const headers = getHeaders(destination, apiVersion); + const baseEndPoint = getBaseEndpoint(destination); + const endpoint = `${baseEndPoint}/${TAGS_ENDPOINT}`; + const statTags = { + destType: 'intercom', + feature: 'transformation', + endpointPath: '/tags', + requestMethod: 'POST', + module: 'router', + }; + await Promise.all( + companyTags.map(async (tag) => { + const finalPayload = { + name: tag, + companies: [ + { + id, + }, + ], + }; + const response = await httpPOST( + endpoint, + finalPayload, + { + headers, + }, + statTags, + ); + const processedResponse = processAxiosResponse(response); + if (!isHttpStatusSuccess(processedResponse.status)) { + intercomErrorHandler( + 'Unable to Add or Update the Tag to Company due to', + processedResponse, + ); + } + }), + ); +}; + module.exports = { getName, getHeaders, @@ -382,4 +514,6 @@ module.exports = { filterCustomAttributes, checkIfEmailOrUserIdPresent, separateReservedAndRestMetadata, + attachContactToCompany, + addOrUpdateTagsToCompany, }; diff --git a/src/cdk/v2/destinations/intercom/utils.test.js b/src/cdk/v2/destinations/intercom/utils.test.js index e651b4ea5d..2f7dac2583 100644 --- a/src/cdk/v2/destinations/intercom/utils.test.js +++ b/src/cdk/v2/destinations/intercom/utils.test.js @@ -13,6 +13,8 @@ const { filterCustomAttributes, checkIfEmailOrUserIdPresent, separateReservedAndRestMetadata, + attachContactToCompany, + addOrUpdateTagsToCompany, } = require('./utils'); const { BASE_ENDPOINT, BASE_EU_ENDPOINT, BASE_AU_ENDPOINT } = require('./config'); @@ -763,3 +765,263 @@ describe('attachUserAndCompany utility test', () => { expect(response).toEqual(expectedResponse); }); }); + +describe('attachContactToCompany utility test', () => { + it('Should successfully attach contact to company for apiVersion v2', async () => { + const payload = { + id: 'company123', + }; + const endpoint = 'https://api.intercom.io/contacts/contact123/companies'; + const destination = { Config: { apiKey: 'testApiKey', apiServer: 'us', apiVersion: 'v2' } }; + + axios.post.mockResolvedValue({ + status: 200, + data: { + type: 'company', + id: 'contact123', + company_id: 'company123', + }, + }); + + await attachContactToCompany(payload, endpoint, destination); + + expect(axios.post).toHaveBeenCalledWith( + endpoint, + JSON.stringify(payload), + expect.objectContaining({ + headers: getHeaders(destination, 'v2'), + }), + ); + }); + + it('Should successfully attach contact to company for apiVersion v1', async () => { + const payload = { + user_id: 'user123', + companies: [ + { + company_id: 'company123', + name: 'Company', + }, + ], + }; + const endpoint = 'https://api.intercom.io/users'; + const destination = { Config: { apiKey: 'testApiKey', apiVersion: 'v1' } }; + + axios.post.mockResolvedValue({ + status: 200, + data: { + id: 'contact123', + user_id: 'user123', + companies: { + type: 'companies.list', + companies: [ + { + type: 'company', + company_id: 'company123', + id: '123', + name: 'Company', + }, + ], + }, + }, + }); + + await attachContactToCompany(payload, endpoint, destination); + + expect(axios.post).toHaveBeenCalledWith( + endpoint, + JSON.stringify(payload), + expect.objectContaining({ + headers: getHeaders(destination, 'v1'), + }), + ); + }); + + it('Should throw error for invalid company during attachment', async () => { + const payload = { + id: 'company123', + }; + const endpoint = 'https://api.intercom.io/contacts/contact123/companies'; + const destination = { Config: { apiKey: 'testApiKey', apiServer: 'us', apiVersion: 'v2' } }; + + axios.post.mockRejectedValue({ + response: { + status: 404, + data: { + type: 'error.list', + request_id: '123', + errors: [ + { + code: 'company_not_found', + message: 'Company Not Found', + }, + ], + }, + }, + }); + + try { + await attachContactToCompany(payload, endpoint, destination); + } catch (error) { + expect(error.message).toEqual( + 'Unable to attach Contact or User to Company due to : {"type":"error.list","request_id":"123","errors":[{"code":"company_not_found","message":"Company Not Found"}]}', + ); + } + }); + + it('Should throw error for faulty payload during attachment', async () => { + const payload = {}; + const endpoint = 'https://api.intercom.io/contacts/contact123/companies'; + const destination = { Config: { apiKey: 'testApiKey', apiServer: 'us', apiVersion: 'v2' } }; + + axios.post.mockRejectedValue({ + response: { + status: 400, + data: { + type: 'error.list', + request_id: '123', + errors: [ + { + code: 'parameter_not_found', + message: 'company not specified', + }, + ], + }, + }, + }); + + try { + await attachContactToCompany(payload, endpoint, destination); + } catch (error) { + expect(error.message).toEqual( + 'Unable to attach Contact or User to Company due to : {"type":"error.list","request_id":"123","errors":[{"code":"parameter_not_found","message":"company not specified"}]}', + ); + } + }); +}); + +describe('addOrUpdateTagsToCompany utility test', () => { + it('Should successfully add tags to company', async () => { + const message = { + context: { + traits: { + tags: ['tag1', 'tag2'], + }, + }, + }; + const destination = { Config: { apiKey: 'testApiKey', apiServer: 'us' } }; + const id = 'companyId'; + + axios.post + .mockResolvedValueOnce({ + status: 200, + data: { type: 'tag', id: '123', name: 'tag1' }, + }) + .mockResolvedValueOnce({ + status: 200, + data: { type: 'tag', id: '124', name: 'tag2' }, + }); + + axios.post.mockClear(); + await addOrUpdateTagsToCompany(message, destination, id); + + expect(axios.post).toHaveBeenCalledTimes(2); + + expect(axios.post).toHaveBeenCalledWith( + `${getBaseEndpoint(destination)}/tags`, + { name: 'tag1', companies: [{ id: 'companyId' }] }, + expect.objectContaining({ + headers: getHeaders(destination), + }), + ); + + expect(axios.post).toHaveBeenCalledWith( + `${getBaseEndpoint(destination)}/tags`, + { name: 'tag2', companies: [{ id: 'companyId' }] }, + expect.objectContaining({ + headers: getHeaders(destination), + }), + ); + }); + + it('Should throw an error in case if axios calls returns an error', async () => { + const message = { + context: { + traits: { + tags: ['tag1'], + }, + }, + }; + const destination = { Config: { apiKey: 'testApiKey', apiServer: 'us' } }; + const id = 'companyId'; + + axios.post.mockRejectedValue({ + status: 401, + data: { + type: 'error.list', + request_id: 'request_401', + errors: [ + { + code: 'unauthorized', + message: 'Access Token Invalid', + }, + ], + }, + }); + + try { + axios.post.mockClear(); + await addOrUpdateTagsToCompany(message, destination, id); + } catch (error) { + expect(error.message).toEqual( + `Unable to Add or Update the Tag to Company due to : {"type":"error.list","request_id":"request_401","errors":[{"code":"unauthorized","message":"Access Token Invalid"}]}`, + ); + } + }); + + it('Should throw a network error in case if axios calls returns an error', async () => { + const message = { + context: { + traits: { + tags: ['tag1'], + }, + }, + }; + const destination = { Config: { apiKey: 'testApiKey', apiServer: 'us' } }; + const id = 'companyId'; + + axios.post.mockRejectedValue({ + status: 429, + data: { + type: 'error.list', + request_id: 'request_429', + errors: [ + { + code: 'rate_limit_exceeded', + message: 'You have exceeded the rate limit. Please try again later.', + }, + ], + }, + }); + + try { + axios.post.mockClear(); + await addOrUpdateTagsToCompany(message, destination, id); + } catch (error) { + expect(error.message).toEqual( + `Unable to Add or Update the Tag to Company due to : {"type":"error.list","request_id":"request_429","errors":[{"code":"rate_limit_exceeded","message":"You have exceeded the rate limit. Please try again later."}]}`, + ); + } + }); + + it('Should do nothing when no tags are provided', async () => { + const message = { traits: {} }; + const destination = { Config: { apiKey: 'testApiKey', apiServer: 'us' } }; + const id = 'companyId'; + + axios.post.mockClear(); + await addOrUpdateTagsToCompany(message, destination, id); + + expect(axios.post).not.toHaveBeenCalled(); + }); +}); diff --git a/test/integrations/destinations/intercom/network.ts b/test/integrations/destinations/intercom/network.ts index 5fa8ac6e96..69f5ea1247 100644 --- a/test/integrations/destinations/intercom/network.ts +++ b/test/integrations/destinations/intercom/network.ts @@ -43,6 +43,141 @@ const companyPayload = { industry: 'CDP', }; +const v1Headers = { + 'Content-Type': 'application/json', + Authorization: 'Bearer abcd=', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const companyData1 = { + company_id: 'test_company_id_wdasda', + name: 'rudderUpdate', + plan: 'basic', + size: 50, + industry: 'IT', + monthly_spend: 2131231, + remote_created_at: 1683017572, + custom_attributes: { + key1: 'val1', + employees: 450, + email: 'test@test.com', + }, +}; + +const companyData2 = { + company_id: 'test_company_id_wdasda', + name: 'rudderUpdate', + website: 'url', + plan: 'basic', + size: 50, + industry: 'IT', + monthly_spend: 2131231, + remote_created_at: 1683017572, + custom_attributes: { + key1: 'val1', + employees: 450, + email: 'test@test.com', + 'key2.a': 'a', + 'key2.b': 'b', + 'key3[0]': 1, + 'key3[1]': 2, + 'key3[2]': 3, + key4: null, + }, +}; + +const companyData3 = { + company_id: 'test_company_id', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', + monthly_spend: 2131231, + custom_attributes: { + email: 'comanyemail@abc.com', + }, +}; + +const userData1 = { + user_id: 'sdfrsdfsdfsf', + companies: [ + { + company_id: 'test_company_id_wdasda', + name: 'rudderUpdate', + }, + ], +}; + +const userData2 = { + email: 'testUser@test.com', + companies: [ + { + company_id: 'test_company_id_wdasda', + name: 'rudderUpdate', + }, + ], +}; + +const userData3 = { + user_id: 'sdfrsdfsdfsf', + email: 'testUser@test.com', + companies: [ + { + company_id: 'test_company_id_wdasda', + name: 'rudderUpdate', + }, + ], +}; + +const createCompanyDummyResp = { + type: 'company', + company_id: 'test_company_id_wdasda', + id: '657264e9018c0a647s45', + name: 'rudderUpdate', + website: 'url', + plan: 'basic', + size: 50, + industry: 'IT', + monthly_spend: 2131231, + remote_created_at: 1683017572, + created_at: 1701930212, + updated_at: 1701930212, + custom_attributes: { + key1: 'val1', + employees: 450, + email: 'test@test.com', + }, +}; + +const attachUserDummyResp = { + type: 'user', + id: '6662e5abd27951dd35e024e9', + user_id: 'user123', + anonymous: false, + email: 'test+5@rudderlabs.com', + app_id: '1234', + companies: { + type: 'company.list', + companies: [ + { + type: 'company', + company_id: 'company_id', + id: '6664ec390b9416d083be97fc', + name: 'Company', + }, + ], + }, + created_at: 1717757355, + updated_at: 1717890105, + tags: { + type: 'tag.list', + tags: [], + }, + custom_attributes: {}, +}; + const deleteNwData = [ { httpReq: { @@ -597,5 +732,232 @@ const deliveryCallsData = [ status: 504, }, }, + { + httpReq: { + method: 'post', + url: 'https://api.eu.intercom.io/contacts/70701240741e45d040/companies', + data: { + id: '657264e9018c0a647s45', + }, + headers: commonHeaders, + }, + httpRes: { + status: 200, + data: { + type: 'company', + company_id: 'rudderlabs', + id: '657264e9018c0a647s45', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', + remote_created_at: 1374138000, + created_at: 1701930212, + updated_at: 1701930212, + }, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/companies', + data: companyPayload, + headers: commonHeaders, + }, + httpRes: { + status: 200, + data: { + type: 'company', + company_id: 'rudderlabs', + id: '657264e9018c0a647s45', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', + remote_created_at: 1374138000, + created_at: 1701930212, + updated_at: 1701930212, + }, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/users', + data: { + user_id: 'user@5', + email: 'test+5@rudderlabs.com', + companies: [ + { + company_id: 'rudderlabs', + name: 'RudderStack', + }, + ], + }, + headers: commonHeaders, + }, + httpRes: { + status: 200, + data: { + type: 'company', + company_id: 'rudderlabs', + id: '657264e9018c0a647s45', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', + remote_created_at: 1374138000, + created_at: 1701930212, + updated_at: 1701930212, + companies: { + type: 'company.list', + companies: [ + { + type: 'company', + company_id: 'rudderlabs', + id: '657264e9018c0a647s45', + name: 'RudderStack', + }, + ], + }, + }, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/companies', + data: companyData1, + headers: v1Headers, + }, + httpRes: { + status: 200, + data: createCompanyDummyResp, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/companies', + data: { + ...companyData1, + website: 'url', + }, + headers: v1Headers, + }, + httpRes: { + status: 200, + data: createCompanyDummyResp, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/companies', + data: companyData2, + headers: v1Headers, + }, + httpRes: { + status: 200, + data: createCompanyDummyResp, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/companies', + data: companyData3, + headers: v1Headers, + }, + httpRes: { + status: 200, + data: createCompanyDummyResp, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/users', + data: userData1, + headers: v1Headers, + }, + httpRes: { + status: 200, + data: attachUserDummyResp, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/users', + data: userData2, + headers: v1Headers, + }, + httpRes: { + status: 200, + data: attachUserDummyResp, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/users', + data: userData3, + headers: v1Headers, + }, + httpRes: { + status: 200, + data: attachUserDummyResp, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.eu.intercom.io/tags', + data: { + name: 'tag1', + companies: [ + { + id: '657264e9018c0a647s45', + }, + ], + }, + headers: commonHeaders, + }, + httpRes: { + status: 200, + data: { + type: 'tag', + name: 'tag1', + id: '123', + }, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.eu.intercom.io/tags', + data: { + name: 'tag2', + companies: [ + { + id: '657264e9018c0a647s45', + }, + ], + }, + headers: commonHeaders, + }, + httpRes: { + status: 200, + data: { + type: 'tag', + name: 'tag2', + id: '123', + }, + }, + }, ]; export const networkCallsData = [...deleteNwData, ...deliveryCallsData]; diff --git a/test/integrations/destinations/intercom/router/data.ts b/test/integrations/destinations/intercom/router/data.ts index 2ce8621ca1..f0e6b46f4a 100644 --- a/test/integrations/destinations/intercom/router/data.ts +++ b/test/integrations/destinations/intercom/router/data.ts @@ -1,187 +1,655 @@ -export const data = [ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { RouterTestData } from '../../../testTypes'; +import { generateMetadata } from '../../../testUtils'; + +const destination1: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + apiKey: 'testApiKey', + apiServer: 'standard', + apiVersion: 'v2', + sendAnonymousId: false, + updateLastRequestAt: true, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const destination2: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + apiKey: 'testApiKey', + apiServer: 'standard', + apiVersion: 'v2', + sendAnonymousId: false, + updateLastRequestAt: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const destination3: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + apiKey: 'testApiKey', + apiVersion: 'v2', + apiServer: 'eu', + sendAnonymousId: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const destination4: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + apiKey: 'testApiKey', + apiVersion: 'v1', + sendAnonymousId: false, + updateLastRequestAt: false, + collectContext: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const destination5: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + apiKey: 'testApiKey', + apiVersion: 'v1', + sendAnonymousId: false, + collectContext: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const destination6: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + apiVersion: 'v1', + sendAnonymousId: false, + updateLastRequestAt: false, + collectContext: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const destination7: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + apiVersion: 'v1', + sendAnonymousId: false, + collectContext: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const routerRequest1: RouterTransformationRequest = { + input: [ + { + destination: destination1, + message: { + userId: 'user@1', + channel: 'web', + context: { + traits: { + age: 23, + email: 'test@rudderlabs.com', + phone: '+91 9999999999', + firstName: 'Test', + lastName: 'Rudderlabs', + address: 'california usa', + ownerId: '13', + }, + }, + type: 'identify', + integrations: { All: true }, + originalTimestamp: '2023-11-10T14:42:44.724Z', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(1), + }, + { + destination: destination2, + message: { + userId: 'user@3', + channel: 'web', + context: { + traits: { + age: 32, + email: 'test+3@rudderlabs.com', + phone: '+91 9399999999', + firstName: 'Test', + lastName: 'RudderStack', + ownerId: '15', + }, + }, + properties: { + revenue: { + amount: 1232, + currency: 'inr', + test: 123, + }, + price: { + amount: 3000, + currency: 'USD', + }, + }, + event: 'Product Viewed', + type: 'track', + originalTimestamp: '2023-11-10T14:42:44.724Z', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(2), + }, + { + destination: destination3, + message: { + userId: 'user@5', + groupId: 'rudderlabs', + channel: 'web', + context: { + traits: { + email: 'test+5@rudderlabs.com', + phone: '+91 9599999999', + firstName: 'John', + lastName: 'Snow', + ownerId: '17', + }, + }, + traits: { + name: 'RudderStack', + size: 500, + website: 'www.rudderstack.com', + industry: 'CDP', + plan: 'enterprise', + }, + type: 'group', + originalTimestamp: '2023-11-10T14:42:44.724Z', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(3), + }, + { + destination: destination3, + message: { + userId: 'user@5', + groupId: 'rudderlabs', + channel: 'web', + context: { + traits: { + email: 'test+5@rudderlabs.com', + phone: '+91 9599999999', + firstName: 'John', + lastName: 'Snow', + ownerId: '17', + tags: ['tag1', 'tag2'], + }, + }, + traits: { + name: 'RudderStack', + size: 500, + website: 'www.rudderstack.com', + industry: 'CDP', + plan: 'enterprise', + }, + type: 'group', + originalTimestamp: '2023-11-10T14:42:44.724Z', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(5), + }, + { + destination: destination3, + message: { + userId: 'user@6', + groupId: 'rudderlabs', + channel: 'web', + context: { + traits: { + email: 'test+5@rudderlabs.com', + phone: '+91 9599999999', + firstName: 'John', + lastName: 'Snow', + ownerId: '17', + }, + }, + traits: { + name: 'RudderStack', + size: 500, + website: 'www.rudderstack.com', + industry: 'CDP', + plan: 'enterprise', + isOpenSource: true, + }, + type: 'group', + originalTimestamp: '2023-11-10T14:42:44.724Z', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(4), + }, + ], + destType: 'intercom', +}; + +const routerRequest2: RouterTransformationRequest = { + input: [ + { + destination: destination4, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + channel: 'mobile', + context: { + app: { + build: '1.0', + name: 'Test_Example', + namespace: 'com.example.testapp', + version: '1.0', + }, + device: { + id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + manufacturer: 'Apple', + model: 'iPhone', + name: 'iPod touch (7th generation)', + type: 'iOS', + }, + library: { + name: 'test-ios-library', + version: '1.0.7', + }, + locale: 'en-US', + network: { + bluetooth: false, + carrier: 'unavailable', + cellular: false, + wifi: true, + }, + os: { + name: 'iOS', + version: '14.0', + }, + screen: { + density: 2, + height: 320, + width: 568, + }, + timezone: 'Asia/Kolkata', + traits: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + name: 'Test Name', + firstName: 'Test', + lastName: 'Name', + createdAt: '2020-09-30T19:11:00.337Z', + userId: 'test_user_id_1', + email: 'test_1@test.com', + phone: '9876543210', + key1: 'value1', + }, + userAgent: 'unknown', + }, + event: 'Test Event 2', + integrations: { + All: true, + }, + messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', + originalTimestamp: '2020-09-30T19:11:00.337Z', + receivedAt: '2020-10-01T00:41:11.369+05:30', + request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', + sentAt: '2020-09-30T19:11:10.382Z', + timestamp: '2020-10-01T00:41:01.324+05:30', + type: 'identify', + }, + metadata: generateMetadata(1), + }, + { + destination: destination4, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + channel: 'mobile', + context: { + app: { + build: '1.0', + name: 'Test_Example', + namespace: 'com.example.testapp', + version: '1.0', + }, + device: { + id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + manufacturer: 'Apple', + model: 'iPhone', + name: 'iPod touch (7th generation)', + type: 'iOS', + }, + library: { + name: 'test-ios-library', + version: '1.0.7', + }, + locale: 'en-US', + network: { + bluetooth: false, + carrier: 'unavailable', + cellular: false, + wifi: true, + }, + os: { + name: 'iOS', + version: '14.0', + }, + screen: { + density: 2, + height: 320, + width: 568, + }, + timezone: 'Asia/Kolkata', + traits: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + firstName: 'Test', + lastName: 'Name', + createdAt: '2020-09-30T19:11:00.337Z', + email: 'test_1@test.com', + phone: '9876543210', + key1: 'value1', + }, + userAgent: 'unknown', + }, + event: 'Test Event 2', + integrations: { + All: true, + }, + messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', + originalTimestamp: '2020-09-30T19:11:00.337Z', + receivedAt: '2020-10-01T00:41:11.369+05:30', + request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', + sentAt: '2020-09-30T19:11:10.382Z', + timestamp: '2020-10-01T00:41:01.324+05:30', + type: 'identify', + }, + metadata: generateMetadata(2), + }, + { + destination: destination5, + message: { + userId: 'user@5', + groupId: 'rudderlabs', + channel: 'web', + context: { + traits: { + email: 'test+5@rudderlabs.com', + phone: '+91 9599999999', + firstName: 'John', + lastName: 'Snow', + ownerId: '17', + }, + }, + traits: { + name: 'RudderStack', + size: 500, + website: 'www.rudderstack.com', + industry: 'CDP', + plan: 'enterprise', + }, + type: 'group', + originalTimestamp: '2023-11-10T14:42:44.724Z', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(3), + }, + ], + destType: 'intercom', +}; + +const routerRequest3: RouterTransformationRequest = { + input: [ + { + destination: destination6, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + channel: 'mobile', + context: { + app: { + build: '1.0', + name: 'Test_Example', + namespace: 'com.example.testapp', + version: '1.0', + }, + device: { + id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + manufacturer: 'Apple', + model: 'iPhone', + name: 'iPod touch (7th generation)', + type: 'iOS', + }, + library: { + name: 'test-ios-library', + version: '1.0.7', + }, + locale: 'en-US', + network: { + bluetooth: false, + carrier: 'unavailable', + cellular: false, + wifi: true, + }, + os: { + name: 'iOS', + version: '14.0', + }, + screen: { + density: 2, + height: 320, + width: 568, + }, + timezone: 'Asia/Kolkata', + traits: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + name: 'Test Name', + firstName: 'Test', + lastName: 'Name', + createdAt: '2020-09-30T19:11:00.337Z', + userId: 'test_user_id_1', + email: 'test_1@test.com', + phone: '9876543210', + key1: 'value1', + }, + userAgent: 'unknown', + }, + event: 'Test Event 2', + integrations: { + All: true, + }, + messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', + originalTimestamp: '2020-09-30T19:11:00.337Z', + receivedAt: '2020-10-01T00:41:11.369+05:30', + request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', + sentAt: '2020-09-30T19:11:10.382Z', + timestamp: '2020-10-01T00:41:01.324+05:30', + type: 'identify', + }, + metadata: generateMetadata(1), + }, + { + destination: destination6, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + channel: 'mobile', + context: { + app: { + build: '1.0', + name: 'Test_Example', + namespace: 'com.example.testapp', + version: '1.0', + }, + device: { + id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + manufacturer: 'Apple', + model: 'iPhone', + name: 'iPod touch (7th generation)', + type: 'iOS', + }, + library: { + name: 'test-ios-library', + version: '1.0.7', + }, + locale: 'en-US', + network: { + bluetooth: false, + carrier: 'unavailable', + cellular: false, + wifi: true, + }, + os: { + name: 'iOS', + version: '14.0', + }, + screen: { + density: 2, + height: 320, + width: 568, + }, + timezone: 'Asia/Kolkata', + traits: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + firstName: 'Test', + lastName: 'Name', + createdAt: '2020-09-30T19:11:00.337Z', + email: 'test_1@test.com', + phone: '9876543210', + key1: 'value1', + }, + userAgent: 'unknown', + }, + event: 'Test Event 2', + integrations: { + All: true, + }, + messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', + originalTimestamp: '2020-09-30T19:11:00.337Z', + receivedAt: '2020-10-01T00:41:11.369+05:30', + request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', + sentAt: '2020-09-30T19:11:10.382Z', + timestamp: '2020-10-01T00:41:01.324+05:30', + type: 'identify', + }, + metadata: generateMetadata(2), + }, + { + destination: destination7, + message: { + userId: 'user@5', + groupId: 'rudderlabs', + channel: 'web', + context: { + traits: { + email: 'test+5@rudderlabs.com', + phone: '+91 9599999999', + firstName: 'John', + lastName: 'Snow', + ownerId: '17', + }, + }, + traits: { + name: 'RudderStack', + size: 500, + website: 'www.rudderstack.com', + industry: 'CDP', + plan: 'enterprise', + }, + type: 'group', + originalTimestamp: '2023-11-10T14:42:44.724Z', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(3), + }, + ], + destType: 'intercom', +}; + +export const data: RouterTestData[] = [ { + id: 'Intercom-router-test-1', + scenario: 'Framework', + successCriteria: + 'Some events should be transformed successfully and some should fail for apiVersion v2', name: 'intercom', - description: 'Intercom router tests', + description: 'Intercom router tests for apiVersion v2', feature: 'router', module: 'destination', version: 'v0', input: { request: { - body: { - input: [ - { - message: { - userId: 'user@1', - channel: 'web', - context: { - traits: { - age: 23, - email: 'test@rudderlabs.com', - phone: '+91 9999999999', - firstName: 'Test', - lastName: 'Rudderlabs', - address: 'california usa', - ownerId: '13', - }, - }, - type: 'identify', - integrations: { All: true }, - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiServer: 'standard', - apiVersion: 'v2', - sendAnonymousId: false, - updateLastRequestAt: true, - }, - }, - metadata: { jobId: 1 }, - }, - { - message: { - userId: 'user@3', - channel: 'web', - context: { - traits: { - age: 32, - email: 'test+3@rudderlabs.com', - phone: '+91 9399999999', - firstName: 'Test', - lastName: 'RudderStack', - ownerId: '15', - }, - }, - properties: { - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - price: { - amount: 3000, - currency: 'USD', - }, - }, - event: 'Product Viewed', - type: 'track', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiServer: 'standard', - apiVersion: 'v2', - sendAnonymousId: false, - updateLastRequestAt: false, - }, - }, - metadata: { - jobId: 2, - }, - }, - { - message: { - userId: 'user@5', - groupId: 'rudderlabs', - channel: 'web', - context: { - traits: { - email: 'test+5@rudderlabs.com', - phone: '+91 9599999999', - firstName: 'John', - lastName: 'Snow', - ownerId: '17', - }, - }, - traits: { - name: 'RudderStack', - size: 500, - website: 'www.rudderstack.com', - industry: 'CDP', - plan: 'enterprise', - }, - type: 'group', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'eu', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 3, - }, - }, - { - message: { - userId: 'user@6', - groupId: 'rudderlabs', - channel: 'web', - context: { - traits: { - email: 'test+5@rudderlabs.com', - phone: '+91 9599999999', - firstName: 'John', - lastName: 'Snow', - ownerId: '17', - }, - }, - traits: { - name: 'RudderStack', - size: 500, - website: 'www.rudderstack.com', - industry: 'CDP', - plan: 'enterprise', - isOpenSource: true, - }, - type: 'group', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'eu', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 4, - }, - }, - ], - destType: 'intercom', - }, - method: 'POST', + body: routerRequest1, }, }, output: { @@ -221,21 +689,8 @@ export const data = [ type: 'REST', version: '1', }, - destination: { - Config: { - apiKey: 'testApiKey', - apiServer: 'standard', - apiVersion: 'v2', - sendAnonymousId: false, - updateLastRequestAt: true, - }, - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - }, - metadata: [{ jobId: 1 }], + destination: destination1, + metadata: [generateMetadata(1)], statusCode: 200, }, { @@ -276,21 +731,8 @@ export const data = [ type: 'REST', version: '1', }, - destination: { - Config: { - apiKey: 'testApiKey', - apiServer: 'standard', - apiVersion: 'v2', - sendAnonymousId: false, - updateLastRequestAt: false, - }, - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - }, - metadata: [{ jobId: 2 }], + destination: destination2, + metadata: [generateMetadata(2)], statusCode: 200, }, { @@ -317,25 +759,37 @@ export const data = [ type: 'REST', version: '1', }, - destination: { - Config: { - apiKey: 'testApiKey', - apiServer: 'eu', - apiVersion: 'v2', - sendAnonymousId: false, - }, - DestinationDefinition: { - Config: { - cdkV2Enabled: true, + destination: destination3, + metadata: [generateMetadata(3)], + statusCode: 299, + }, + { + batched: false, + batchedRequest: { + body: { + JSON: { + id: '657264e9018c0a647s45', }, + XML: {}, + FORM: {}, + JSON_ARRAY: {}, }, - }, - metadata: [ - { - jobId: 3, + endpoint: 'https://api.eu.intercom.io/contacts/70701240741e45d040/companies', + files: {}, + headers: { + Authorization: 'Bearer testApiKey', + 'Content-Type': 'application/json', + Accept: 'application/json', + 'Intercom-Version': '2.10', }, - ], - statusCode: 200, + method: 'POST', + params: {}, + type: 'REST', + version: '1', + }, + destination: destination3, + metadata: [generateMetadata(5)], + statusCode: 299, }, { batched: false, @@ -348,25 +802,11 @@ export const data = [ feature: 'router', implementation: 'cdkV2', module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', }, - destination: { - Config: { - apiKey: 'testApiKey', - apiServer: 'eu', - apiVersion: 'v2', - sendAnonymousId: false, - }, - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - }, - metadata: [ - { - jobId: 4, - }, - ], + destination: destination3, + metadata: [generateMetadata(4)], statusCode: 401, }, ], @@ -375,222 +815,18 @@ export const data = [ }, }, { + id: 'Intercom-router-test-2', + scenario: 'Framework', + successCriteria: + 'Events should be transformed successfully for apiVersion v1 and cdk v2 enabled', name: 'intercom', - description: 'Test 0', + description: 'Intercom router tests for apiVersion v1 and cdk v2 enabled', feature: 'router', module: 'destination', version: 'v0', input: { request: { - body: { - input: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - userId: 'test_user_id_1', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - metadata: { - jobId: 1, - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - sendAnonymousId: false, - updateLastRequestAt: false, - collectContext: false, - }, - }, - }, - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - metadata: { - jobId: 2, - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - sendAnonymousId: false, - updateLastRequestAt: false, - collectContext: false, - }, - }, - }, - { - message: { - userId: 'user@5', - groupId: 'rudderlabs', - channel: 'web', - context: { - traits: { - email: 'test+5@rudderlabs.com', - phone: '+91 9599999999', - firstName: 'John', - lastName: 'Snow', - ownerId: '17', - }, - }, - traits: { - name: 'RudderStack', - size: 500, - website: 'www.rudderstack.com', - industry: 'CDP', - plan: 'enterprise', - }, - type: 'group', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - sendAnonymousId: false, - collectContext: false, - }, - }, - metadata: { - jobId: 3, - }, - }, - ], - destType: 'intercom', - }, + body: routerRequest2, }, }, output: { @@ -632,27 +868,10 @@ export const data = [ files: {}, userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', }, - metadata: [ - { - jobId: 1, - }, - ], + metadata: [generateMetadata(1)], batched: false, statusCode: 200, - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - collectContext: false, - sendAnonymousId: false, - updateLastRequestAt: false, - }, - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - }, + destination: destination4, }, { batchedRequest: { @@ -687,27 +906,10 @@ export const data = [ files: {}, userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', }, - metadata: [ - { - jobId: 2, - }, - ], + metadata: [generateMetadata(2)], batched: false, statusCode: 200, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - collectContext: false, - sendAnonymousId: false, - updateLastRequestAt: false, - }, - }, + destination: destination4, }, { batched: false, @@ -769,25 +971,9 @@ export const data = [ version: '1', }, ], - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - collectContext: false, - sendAnonymousId: false, - }, - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - }, - metadata: [ - { - jobId: 3, - }, - ], - statusCode: 200, + destination: destination5, + metadata: [generateMetadata(3)], + statusCode: 299, }, ], }, @@ -795,207 +981,18 @@ export const data = [ }, }, { + id: 'Intercom-router-test-3', + scenario: 'Framework', + successCriteria: + 'Events should be transformed successfully for apiVersion v1 and cdk v2 not enabled', name: 'intercom', - description: 'Test 0', + description: 'Intercom router tests for apiVersion v1 and cdk v2 not enabled', feature: 'router', module: 'destination', version: 'v0', input: { request: { - body: { - input: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - userId: 'test_user_id_1', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - metadata: { - jobId: 1, - }, - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - sendAnonymousId: false, - updateLastRequestAt: false, - collectContext: false, - }, - }, - }, - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - metadata: { - jobId: 2, - }, - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - sendAnonymousId: false, - updateLastRequestAt: false, - collectContext: false, - }, - }, - }, - { - message: { - userId: 'user@5', - groupId: 'rudderlabs', - channel: 'web', - context: { - traits: { - email: 'test+5@rudderlabs.com', - phone: '+91 9599999999', - firstName: 'John', - lastName: 'Snow', - ownerId: '17', - }, - }, - traits: { - name: 'RudderStack', - size: 500, - website: 'www.rudderstack.com', - industry: 'CDP', - plan: 'enterprise', - }, - type: 'group', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - sendAnonymousId: false, - collectContext: false, - }, - }, - metadata: { - jobId: 3, - }, - }, - ], - destType: 'intercom', - }, + body: routerRequest3, }, }, output: { @@ -1037,22 +1034,10 @@ export const data = [ files: {}, userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', }, - metadata: [ - { - jobId: 1, - }, - ], + metadata: [generateMetadata(1)], batched: false, statusCode: 200, - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - collectContext: false, - sendAnonymousId: false, - updateLastRequestAt: false, - }, - }, + destination: destination6, }, { batchedRequest: { @@ -1087,22 +1072,10 @@ export const data = [ files: {}, userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', }, - metadata: [ - { - jobId: 2, - }, - ], + metadata: [generateMetadata(2)], batched: false, statusCode: 200, - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - collectContext: false, - sendAnonymousId: false, - updateLastRequestAt: false, - }, - }, + destination: destination6, }, { batched: false, @@ -1164,19 +1137,8 @@ export const data = [ version: '1', }, ], - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - collectContext: false, - sendAnonymousId: false, - }, - }, - metadata: [ - { - jobId: 3, - }, - ], + destination: destination7, + metadata: [generateMetadata(3)], statusCode: 200, }, ], From 769161a54de03fa3d4fe9f5db9ac51c11fa89dac Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Fri, 21 Jun 2024 11:28:18 +0530 Subject: [PATCH 229/240] feat: add support in fb pixel for advertiser_tracking_enabled (#3025) * feat: add support in fb pixel for advertiser_tracking_enabled * chore: add corresponding router test * chore: update router tests descriptions in fb pixel --- .../data/FBPIXELCommonConfig.json | 5 + .../facebook_pixel/router/data.ts | 113 +++++++++++++++++- 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/src/v0/destinations/facebook_pixel/data/FBPIXELCommonConfig.json b/src/v0/destinations/facebook_pixel/data/FBPIXELCommonConfig.json index 11a81a20ab..1ba6c4c82f 100644 --- a/src/v0/destinations/facebook_pixel/data/FBPIXELCommonConfig.json +++ b/src/v0/destinations/facebook_pixel/data/FBPIXELCommonConfig.json @@ -33,5 +33,10 @@ "context.traits.action_source", "properties.action_source" ] + }, + { + "destKey": "advertiser_tracking_enabled", + "sourceKeys": "context.device.adTrackingEnabled", + "required": false } ] diff --git a/test/integrations/destinations/facebook_pixel/router/data.ts b/test/integrations/destinations/facebook_pixel/router/data.ts index 4bd9914768..1d3b35b42a 100644 --- a/test/integrations/destinations/facebook_pixel/router/data.ts +++ b/test/integrations/destinations/facebook_pixel/router/data.ts @@ -8,7 +8,8 @@ export const mockFns = (_) => { export const data = [ { name: 'facebook_pixel', - description: 'Test 0', + description: + 'Test 0: General batch request with two events, one track and one identify event with advancedMapping set to false and true respectively', feature: 'router', module: 'destination', version: 'v0', @@ -201,4 +202,114 @@ export const data = [ }, }, }, + { + name: 'facebook_pixel', + description: + 'Test 1 : adTrackingEnabled is passed in context, advertiser_tracking_enabled set to true', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + destination_props: { Fb: { app_id: 'RudderFbApp' } }, + context: { + device: { + id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', + manufacturer: 'Xiaomi', + model: 'Redmi 6', + name: 'xiaomi', + adTrackingEnabled: true, + }, + network: { carrier: 'Banglalink' }, + os: { name: 'android', version: '8.1.0' }, + screen: { height: '100', density: 50 }, + traits: { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + }, + }, + event: 'spin_result', + integrations: { All: true }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { revenue: 400, additional_bet_index: 0 }, + timestamp: '2023-10-14T15:46:51.693229+05:30', + type: 'track', + }, + metadata: { jobId: 1, userId: 'u1' }, + destination: { + Config: { + limitedDataUsage: true, + blacklistPiiProperties: [{ blacklistPiiProperties: '', blacklistPiiHash: false }], + removeExternalId: true, + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [{ from: '', to: '' }], + eventCustomProperties: [{ eventCustomProperties: '' }], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [{ whitelistPiiProperties: '' }], + }, + Enabled: true, + }, + }, + ], + destType: 'facebook_pixel', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/dummyPixelId/events?access_token=09876', + headers: {}, + params: {}, + body: { + JSON: {}, + XML: {}, + JSON_ARRAY: {}, + FORM: { + data: [ + '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"spin_result","event_time":1697278611,"advertiser_tracking_enabled":true,"action_source":"other","custom_data":{"additional_bet_index":0,"value":400}}', + ], + }, + }, + files: {}, + }, + metadata: [{ jobId: 1, userId: 'u1' }], + batched: false, + statusCode: 200, + destination: { + Config: { + limitedDataUsage: true, + blacklistPiiProperties: [{ blacklistPiiProperties: '', blacklistPiiHash: false }], + removeExternalId: true, + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [{ from: '', to: '' }], + eventCustomProperties: [{ eventCustomProperties: '' }], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [{ whitelistPiiProperties: '' }], + }, + Enabled: true, + }, + }, + ], + }, + }, + }, + }, ].map((d) => ({ ...d, mockFns })); From dfa0cbd3608d22340c9acb5769af4173be4d4bfc Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:48:10 +0530 Subject: [PATCH 230/240] fix: adding actual error messgae for default code in facebook destinations (#3490) * fix: adding actual error messgae for default code in facebook destinations * fix: edit of test case description --- src/v0/util/facebookUtils/networkHandler.js | 5 +- .../facebook_pixel/dataDelivery/business.ts | 4 +- .../facebook_pixel/dataDelivery/data.ts | 2 +- .../dataDelivery/business.ts | 46 +++++++++++++++++++ .../fb_custom_audience/network.ts | 41 +++++++++++++++++ 5 files changed, 91 insertions(+), 7 deletions(-) diff --git a/src/v0/util/facebookUtils/networkHandler.js b/src/v0/util/facebookUtils/networkHandler.js index a84128e140..5338364fe2 100644 --- a/src/v0/util/facebookUtils/networkHandler.js +++ b/src/v0/util/facebookUtils/networkHandler.js @@ -85,10 +85,7 @@ const errorDetailsMap = { "Object with ID 'PIXEL_ID' / 'DATASET_ID' / 'AUDIENCE_ID' does not exist, cannot be loaded due to missing permissions, or does not support this operation", ) .build(), - default: new ErrorDetailsExtractorBuilder() - .setStatus(400) - .setMessage('Invalid Parameter') - .build(), + default: new ErrorDetailsExtractorBuilder().setStatus(400).setMessageField('message').build(), }, 1: { // An unknown error occurred. diff --git a/test/integrations/destinations/facebook_pixel/dataDelivery/business.ts b/test/integrations/destinations/facebook_pixel/dataDelivery/business.ts index 9ac709978d..1b425ac5fa 100644 --- a/test/integrations/destinations/facebook_pixel/dataDelivery/business.ts +++ b/test/integrations/destinations/facebook_pixel/dataDelivery/business.ts @@ -241,11 +241,11 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ body: { output: { status: 400, - message: 'Invalid Parameter', + message: 'Unsupported post request. some problem with sent parameters', statTags, response: [ { - error: 'Invalid Parameter', + error: 'Unsupported post request. some problem with sent parameters', statusCode: 400, metadata: generateMetadata(1), }, diff --git a/test/integrations/destinations/facebook_pixel/dataDelivery/data.ts b/test/integrations/destinations/facebook_pixel/dataDelivery/data.ts index dcc633e1a8..e66e98bbe3 100644 --- a/test/integrations/destinations/facebook_pixel/dataDelivery/data.ts +++ b/test/integrations/destinations/facebook_pixel/dataDelivery/data.ts @@ -362,7 +362,7 @@ export const v0TestData = [ body: { output: { status: 400, - message: 'Invalid Parameter', + message: 'Unsupported post request. some problem with sent parameters', destinationResponse: { error: { message: 'Unsupported post request. some problem with sent parameters', diff --git a/test/integrations/destinations/fb_custom_audience/dataDelivery/business.ts b/test/integrations/destinations/fb_custom_audience/dataDelivery/business.ts index c48ad227ab..6334094b3a 100644 --- a/test/integrations/destinations/fb_custom_audience/dataDelivery/business.ts +++ b/test/integrations/destinations/fb_custom_audience/dataDelivery/business.ts @@ -424,4 +424,50 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ }, }, }, + { + id: 'fbca_v1_scenario_9', + name: 'fb_custom_audience', + description: 'user deletion failed differently created custom audience', + successCriteria: 'Fail with status code 400 and sending the actual error message.', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + method: 'DELETE', + endpoint: getEndPoint('aud-value-based'), + headers: { + 'test-dest-response-key': 'validAccessToken', + }, + params: testParams2, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + message: '(#100) Value-Based Custom Audience requires LOOKALIKE_VALUE attribute.', + statTags: { + ...statTags, + errorCategory: 'network', + errorType: 'aborted', + }, + response: [ + { + error: '(#100) Value-Based Custom Audience requires LOOKALIKE_VALUE attribute.', + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + }, + }, ]; diff --git a/test/integrations/destinations/fb_custom_audience/network.ts b/test/integrations/destinations/fb_custom_audience/network.ts index 3331f874a3..369c27afa9 100644 --- a/test/integrations/destinations/fb_custom_audience/network.ts +++ b/test/integrations/destinations/fb_custom_audience/network.ts @@ -583,4 +583,45 @@ export const networkCallsData = [ status: 400, }, }, + { + httpReq: { + version: '1', + type: 'REST', + method: 'DELETE', + endpoint: getEndPoint('aud-value-based'), + headers: { + 'test-dest-response-key': 'validAccessToken', + }, + params: { + access_token: 'ABC', + payload: { + is_raw: true, + data_source: { + sub_type: 'ANYTHING', + }, + schema: ['DOBY', 'PHONE', 'GEN', 'FI', 'MADID', 'ZIP', 'ST', 'COUNTRY'], + data: [['2013', '@09432457768', 'f', 'Ms.', 'ABC', 'ZIP ', '123abc ', 'IN']], + }, + }, + userId: '', + body: { + JSON: {}, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + }, + httpRes: { + data: { + error: { + message: '(#100) Value-Based Custom Audience requires LOOKALIKE_VALUE attribute.', + type: 'OAuthException', + code: 100, + fbtrace_id: 'ADB2jAGDMC_CbfM9430kDdQ', + }, + }, + status: 400, + }, + }, ]; From a08de31e5ac5829bc5cca7da8982c9a4156b1ed5 Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Fri, 21 Jun 2024 14:48:12 +0530 Subject: [PATCH 231/240] fix: add missing userid and anonymousid for revenuecat source (#3485) * fix: add missing userid and anonymousid for revenuecat source * chore: add test and related mock, address comment --- src/v0/sources/revenuecat/transform.js | 9 +- test/integrations/sources/revenuecat/data.ts | 137 +++++++++++++++++++ 2 files changed, 145 insertions(+), 1 deletion(-) diff --git a/src/v0/sources/revenuecat/transform.js b/src/v0/sources/revenuecat/transform.js index 36944e10fa..c746f13b1a 100644 --- a/src/v0/sources/revenuecat/transform.js +++ b/src/v0/sources/revenuecat/transform.js @@ -1,6 +1,8 @@ const { camelCase } = require('lodash'); const moment = require('moment'); -const { removeUndefinedAndNullValues, isDefinedAndNotNull } = require('../../util'); +const { isDefinedAndNotNullAndNotEmpty } = require('@rudderstack/integrations-lib'); +const { removeUndefinedAndNullValues, isDefinedAndNotNull, generateUUID } = require('../../util'); + const Message = require('../message'); function process(event) { @@ -9,6 +11,11 @@ function process(event) { // we are setting event type as track always message.setEventType('track'); + message.userId = event?.event?.app_user_id || event?.event?.original_app_user_id || ''; + if (!isDefinedAndNotNullAndNotEmpty(message.userId)) { + message.anonymousId = generateUUID(); + } + const properties = {}; // dump all event properties to message.properties after converting them to camelCase if (event.event) { diff --git a/test/integrations/sources/revenuecat/data.ts b/test/integrations/sources/revenuecat/data.ts index 4963781763..2762bac5b2 100644 --- a/test/integrations/sources/revenuecat/data.ts +++ b/test/integrations/sources/revenuecat/data.ts @@ -1,3 +1,9 @@ +import utils from '../../../../src/v0/util'; + +const defaultMockFns = () => { + jest.spyOn(utils, 'generateUUID').mockReturnValue('97fcd7b2-cc24-47d7-b776-057b7b199513'); +}; + export const data = [ { name: 'revenuecat', @@ -144,6 +150,7 @@ export const data = [ type: 'TEST', }, event: 'TEST', + userId: 'f8e14f51-0c76-49ba-8d67-c229f1875dd9', messageId: '8CF0CD6C-CAF3-41FB-968A-661938235AF0', originalTimestamp: '2023-10-29T22:06:57.232Z', sentAt: '2023-10-29T22:06:57.232Z', @@ -154,6 +161,9 @@ export const data = [ ], }, }, + mockFns: () => { + defaultMockFns(); + }, }, { name: 'revenuecat', @@ -272,6 +282,130 @@ export const data = [ type: 'INITIAL_PURCHASE', }, event: 'INITIAL_PURCHASE', + userId: 'yourCustomerAppUserID', + messageId: 'UniqueIdentifierOfEvent', + originalTimestamp: '2020-06-02T18:17:35.319Z', + sentAt: '2020-06-02T18:17:35.319Z', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'revenuecat', + description: 'Purchase event with anonymous user', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + api_version: '1.0', + event: { + aliases: ['yourCustomerAliasedID', 'yourCustomerAliasedID'], + app_id: 'yourAppID', + commission_percentage: 0.3, + country_code: 'US', + currency: 'USD', + entitlement_id: 'pro_cat', + entitlement_ids: ['pro_cat'], + environment: 'PRODUCTION', + event_timestamp_ms: 1591121855319, + expiration_at_ms: 1591726653000, + id: 'UniqueIdentifierOfEvent', + is_family_share: false, + offer_code: 'free_month', + original_transaction_id: '1530648507000', + period_type: 'NORMAL', + presented_offering_id: 'OfferingID', + price: 2.49, + price_in_purchased_currency: 2.49, + product_id: 'onemonth_no_trial', + purchased_at_ms: 1591121853000, + store: 'APP_STORE', + subscriber_attributes: { + '$Favorite Cat': { + updated_at_ms: 1581121853000, + value: 'Garfield', + }, + }, + takehome_percentage: 0.7, + tax_percentage: 0.3, + transaction_id: '170000869511114', + type: 'INITIAL_PURCHASE', + }, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + integration: { + name: 'RevenueCat', + }, + }, + integrations: { + RevenueCat: false, + }, + type: 'track', + properties: { + aliases: ['yourCustomerAliasedID', 'yourCustomerAliasedID'], + appId: 'yourAppID', + commissionPercentage: 0.3, + countryCode: 'US', + currency: 'USD', + entitlementId: 'pro_cat', + entitlementIds: ['pro_cat'], + environment: 'PRODUCTION', + eventTimestampMs: 1591121855319, + expirationAtMs: 1591726653000, + id: 'UniqueIdentifierOfEvent', + isFamilyShare: false, + offerCode: 'free_month', + originalTransactionId: '1530648507000', + periodType: 'NORMAL', + presentedOfferingId: 'OfferingID', + price: 2.49, + priceInPurchasedCurrency: 2.49, + productId: 'onemonth_no_trial', + purchasedAtMs: 1591121853000, + store: 'APP_STORE', + subscriberAttributes: { + '$Favorite Cat': { + updated_at_ms: 1581121853000, + value: 'Garfield', + }, + }, + takehomePercentage: 0.7, + taxPercentage: 0.3, + transactionId: '170000869511114', + type: 'INITIAL_PURCHASE', + }, + event: 'INITIAL_PURCHASE', + userId: '', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', messageId: 'UniqueIdentifierOfEvent', originalTimestamp: '2020-06-02T18:17:35.319Z', sentAt: '2020-06-02T18:17:35.319Z', @@ -282,5 +416,8 @@ export const data = [ ], }, }, + mockFns: () => { + defaultMockFns(); + }, }, ]; From 11ce96b3565fbafa0614f9326def5f7ec0163031 Mon Sep 17 00:00:00 2001 From: Jayachand Date: Fri, 21 Jun 2024 17:34:08 +0530 Subject: [PATCH 232/240] chore: remove unused stats (#3493) --- src/util/prometheus.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 480a39a425..ffcfda3784 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -879,24 +879,6 @@ class Prometheus { 'k8_namespace', ], }, - { - name: 'lambda_test_time', - help: 'lambda_test_time', - type: 'histogram', - labelNames: ['transformerVersionId', 'language', 'publish'], - }, - { - name: 'lambda_invoke_time', - help: 'lambda_invoke_time', - type: 'histogram', - labelNames: [ - 'transformerVersionId', - 'language', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, { name: 'marketo_bulk_upload_process_time', help: 'marketo_bulk_upload_process_time', From 3f9bfac53e3cf9021d217a364bdf724c553aaa82 Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Mon, 24 Jun 2024 11:14:03 +0530 Subject: [PATCH 233/240] chore: upgrade packages --- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index d2bc9b452b..2e567d7271 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,8 +20,8 @@ "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.10", - "@rudderstack/json-template-engine": "^0.14.1", - "@rudderstack/workflow-engine": "^0.8.8", + "@rudderstack/json-template-engine": "^0.15.0", + "@rudderstack/workflow-engine": "^0.8.9", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", @@ -4526,17 +4526,17 @@ } }, "node_modules/@rudderstack/json-template-engine": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.14.1.tgz", - "integrity": "sha512-frQ0UyN/hslA9K4cFyHLvLLTxHFhcWZRnF1ELbAHvj8AZJCblM8LZXh4A62aMmF0FbNqgsO5CeOStPqvYm3ugg==" + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.15.0.tgz", + "integrity": "sha512-RDBCn4oYvDjWhIEuV3d8QHmdTcFUyVbpBFxtRwOb1U7zSpTZ8H74/wxBG661ayN2psFh7UJYzZENju7cLWpFTw==" }, "node_modules/@rudderstack/workflow-engine": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.8.8.tgz", - "integrity": "sha512-6vX13wWlfQxFktq8MnAHTBpKulrjwYyZ8ZXcyugEU/BuVedtBTfjGGFsBrlKldLPuRsTZt8XuYbW8Ua7g2C5Kw==", + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.8.9.tgz", + "integrity": "sha512-4PxiXUeJ6ulhdlS7MHB6zPV6fdRVZZ0EDnl5fRbu7gDq1h4h32t5T44RwXjLbyupuu/vsPshkBNoEpo+heUBqg==", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", - "@rudderstack/json-template-engine": "^0.14.1", + "@rudderstack/json-template-engine": "^0.15.0", "jsonata": "^2.0.5", "lodash": "^4.17.21", "object-sizeof": "^2.6.4", diff --git a/package.json b/package.json index adeee0612b..96ec42b79c 100644 --- a/package.json +++ b/package.json @@ -65,8 +65,8 @@ "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", "@rudderstack/integrations-lib": "^0.2.10", - "@rudderstack/json-template-engine": "^0.14.1", - "@rudderstack/workflow-engine": "^0.8.8", + "@rudderstack/json-template-engine": "^0.15.0", + "@rudderstack/workflow-engine": "^0.8.9", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", From bba1a3b7f09e15f5a59aeed22593751d46960ebb Mon Sep 17 00:00:00 2001 From: Manish Kumar <144022547+manish339k@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:39:40 +0530 Subject: [PATCH 234/240] feat: onboard closeCRM source (#3467) * feat: onboard closeCRM source * fix: fixed timestamp issue * fix: fixed minor issues * fix: fixed payload * fix: test case --- src/v1/sources/close_crm/config.js | 3 + src/v1/sources/close_crm/transform.js | 57 +++ test/integrations/sources/close_crm/data.ts | 395 ++++++++++++++++++++ 3 files changed, 455 insertions(+) create mode 100644 src/v1/sources/close_crm/config.js create mode 100644 src/v1/sources/close_crm/transform.js create mode 100644 test/integrations/sources/close_crm/data.ts diff --git a/src/v1/sources/close_crm/config.js b/src/v1/sources/close_crm/config.js new file mode 100644 index 0000000000..410aff8b40 --- /dev/null +++ b/src/v1/sources/close_crm/config.js @@ -0,0 +1,3 @@ +const excludedFieldList = ['changed_fields', 'previous_data']; + +module.exports = { excludedFieldList }; diff --git a/src/v1/sources/close_crm/transform.js b/src/v1/sources/close_crm/transform.js new file mode 100644 index 0000000000..b597285e62 --- /dev/null +++ b/src/v1/sources/close_crm/transform.js @@ -0,0 +1,57 @@ +const moment = require('moment'); +const { + removeUndefinedAndNullValues, + removeUndefinedAndNullRecurse, + generateUUID, + formatTimeStamp, +} = require('../../../v0/util'); +const { excludedFieldList } = require('./config'); +const Message = require('../../../v0/sources/message'); + +function processEvent(inputEvent) { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { event, subscription_id } = inputEvent; + + const message = new Message('CloseCRM'); + + // Set event type track + message.setEventType('track'); + + // Set event name + const eventName = `${event.object_type} ${event.action}`; + message.setEventName(eventName); + + // Set userId + if (event.lead_id) { + message.setProperty('userId', event.lead_id); + } else { + message.setProperty('anonymousId', generateUUID()); + } + + // Set messageId + message.setProperty('messageId', event.id); + + // Set Timestamp + const timestamp = moment.utc(event.date_updated); + message.setProperty('originalTimestamp', formatTimeStamp(timestamp, 'yyyy-MM-ddTHH:mm:ss.SSSZ')); + + // Set properties + removeUndefinedAndNullRecurse(event); + message.setProperty('properties', event); + message.setProperty('properties.subscription_id', subscription_id); + + // Remove excluding fields + excludedFieldList.forEach((field) => { + delete message.properties[field]; + }); + + return message; +} + +function process(inputEvent) { + const { event } = inputEvent; + const response = processEvent(event); + return removeUndefinedAndNullValues(response); +} + +exports.process = process; diff --git a/test/integrations/sources/close_crm/data.ts b/test/integrations/sources/close_crm/data.ts new file mode 100644 index 0000000000..08b9d13a1b --- /dev/null +++ b/test/integrations/sources/close_crm/data.ts @@ -0,0 +1,395 @@ +import utils from '../../../../src/v0/util'; + +const defaultMockFns = () => { + jest.spyOn(utils, 'generateUUID').mockReturnValue('97fcd7b2-cc24-47d7-b776-057b7b199513'); +}; + +export const data = [ + { + name: 'close_crm', + description: 'lead update', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + event: { + date_created: '2019-01-15T12:48:23.395000', + meta: { + request_method: 'PUT', + request_path: '/api/v1/opportunity/object_id/', + }, + id: 'ev_123', + action: 'updated', + date_updated: '2019-01-15T12:48:23.395000', + changed_fields: [ + 'confidence', + 'date_updated', + 'status_id', + 'status_label', + 'status_type', + ], + previous_data: { + status_type: 'active', + confidence: 70, + date_updated: '2019-01-15T12:47:39.873000+00:00', + status_id: 'stat_123', + status_label: 'Active', + }, + organization_id: 'orga_123', + data: { + contact_name: 'Mr. Jones', + user_name: 'Joe Kemp', + value_period: 'one_time', + updated_by_name: 'Joe Kemp', + date_created: '2019-01-15T12:41:24.496000+00:00', + user_id: 'user_123', + updated_by: 'user_123', + value_currency: 'USD', + organization_id: 'orga_123', + status_label: 'Won', + contact_id: 'cont_123', + status_type: 'won', + created_by_name: 'Joe Kemp', + id: 'id_12', + lead_name: 'KLine', + date_lost: null, + note: '', + date_updated: '2019-01-15T12:48:23.392000+00:00', + status_id: 'stat_12', + value: 100000, + created_by: 'user_123', + value_formatted: '$1,000', + date_won: '2019-01-15', + lead_id: 'lead_123', + confidence: 100, + }, + request_id: 'req_123', + object_id: 'object_id', + user_id: 'user_123', + object_type: 'opportunity', + lead_id: 'lead_123', + }, + subscription_id: 'whsub_123', + }, + source: {}, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + integration: { + name: 'CloseCRM', + }, + }, + integrations: { + CloseCRM: false, + }, + type: 'track', + event: 'opportunity updated', + messageId: 'ev_123', + userId: 'lead_123', + originalTimestamp: '2019-01-TuT12:48:23.395+00:00', + properties: { + date_created: '2019-01-15T12:48:23.395000', + meta: { + request_method: 'PUT', + request_path: '/api/v1/opportunity/object_id/', + }, + id: 'ev_123', + action: 'updated', + date_updated: '2019-01-15T12:48:23.395000', + organization_id: 'orga_123', + data: { + contact_name: 'Mr. Jones', + user_name: 'Joe Kemp', + value_period: 'one_time', + updated_by_name: 'Joe Kemp', + date_created: '2019-01-15T12:41:24.496000+00:00', + user_id: 'user_123', + updated_by: 'user_123', + value_currency: 'USD', + organization_id: 'orga_123', + status_label: 'Won', + contact_id: 'cont_123', + status_type: 'won', + created_by_name: 'Joe Kemp', + id: 'id_12', + lead_name: 'KLine', + note: '', + date_updated: '2019-01-15T12:48:23.392000+00:00', + status_id: 'stat_12', + value: 100000, + created_by: 'user_123', + value_formatted: '$1,000', + date_won: '2019-01-15', + lead_id: 'lead_123', + confidence: 100, + }, + request_id: 'req_123', + object_id: 'object_id', + user_id: 'user_123', + object_type: 'opportunity', + lead_id: 'lead_123', + subscription_id: 'whsub_123', + }, + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'close_crm', + description: 'group creation', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + subscription_id: 'whsub_123', + event: { + id: 'ev_123', + date_created: '2024-06-13T03:53:33.917000', + date_updated: '2024-06-13T03:53:33.917000', + organization_id: 'orga_123', + user_id: 'user_123', + request_id: 'req_123', + api_key_id: null, + oauth_client_id: null, + oauth_scope: null, + object_type: 'group', + object_id: 'group_123', + lead_id: null, + action: 'created', + changed_fields: [], + meta: { + request_path: '/api/v1/graphql/', + request_method: 'POST', + }, + data: { + id: 'group_123', + name: 'Test group', + members: [ + { + user_id: 'user_123', + }, + ], + }, + previous_data: {}, + }, + }, + source: {}, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + integration: { + name: 'CloseCRM', + }, + library: { + name: 'unknown', + version: 'unknown', + }, + }, + event: 'group created', + integrations: { + CloseCRM: false, + }, + messageId: 'ev_123', + originalTimestamp: '2024-06-ThT03:53:33.917+00:00', + properties: { + action: 'created', + data: { + id: 'group_123', + members: [ + { + user_id: 'user_123', + }, + ], + name: 'Test group', + }, + date_created: '2024-06-13T03:53:33.917000', + date_updated: '2024-06-13T03:53:33.917000', + id: 'ev_123', + meta: { + request_method: 'POST', + request_path: '/api/v1/graphql/', + }, + object_id: 'group_123', + object_type: 'group', + organization_id: 'orga_123', + request_id: 'req_123', + subscription_id: 'whsub_123', + user_id: 'user_123', + }, + type: 'track', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'close_crm', + description: 'lead deletion', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + subscription_id: 'whsub_123', + event: { + id: 'ev_123', + date_created: '2024-06-14T05:16:04.138000', + date_updated: '2024-06-14T05:16:04.138000', + organization_id: 'orga_123', + user_id: 'user_123', + request_id: 'req_123', + api_key_id: 'api_123', + oauth_client_id: null, + oauth_scope: null, + object_type: 'lead', + object_id: 'lead_123', + lead_id: 'lead_123', + action: 'deleted', + changed_fields: [], + meta: { + request_path: '/api/v1/lead/lead_123/', + request_method: 'DELETE', + }, + data: {}, + previous_data: { + created_by_name: 'Rudder User', + addresses: [], + description: '', + url: null, + date_created: '2024-06-14T05:13:42.239000+00:00', + status_id: 'stat_123', + contact_ids: ['cont_123'], + id: 'lead_12', + date_updated: '2024-06-14T05:13:42.262000+00:00', + updated_by_name: 'Rudder User', + status_label: 'Potential', + name: 'test name', + display_name: 'test name', + organization_id: 'orga_123', + updated_by: 'user_123', + created_by: 'user_123', + }, + }, + }, + source: {}, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + integration: { + name: 'CloseCRM', + }, + }, + integrations: { + CloseCRM: false, + }, + type: 'track', + event: 'lead deleted', + userId: 'lead_123', + messageId: 'ev_123', + originalTimestamp: '2024-06-FrT05:16:04.138+00:00', + properties: { + id: 'ev_123', + date_created: '2024-06-14T05:16:04.138000', + date_updated: '2024-06-14T05:16:04.138000', + organization_id: 'orga_123', + user_id: 'user_123', + request_id: 'req_123', + api_key_id: 'api_123', + object_type: 'lead', + object_id: 'lead_123', + lead_id: 'lead_123', + action: 'deleted', + meta: { + request_path: '/api/v1/lead/lead_123/', + request_method: 'DELETE', + }, + data: {}, + subscription_id: 'whsub_123', + }, + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, +]; From a0f5c2f7fe2dc8bc983a35840e86655d0f92482b Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Mon, 24 Jun 2024 15:40:01 +0530 Subject: [PATCH 235/240] fix: mapping changes for aifa,andi and asid (#3465) --- .../singular/data/SINGULARAndroidEventConfig.json | 12 +++++++++--- .../singular/data/SINGULARAndroidSessionConfig.json | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/v0/destinations/singular/data/SINGULARAndroidEventConfig.json b/src/v0/destinations/singular/data/SINGULARAndroidEventConfig.json index 78a8ed20d2..f66cda7ee5 100644 --- a/src/v0/destinations/singular/data/SINGULARAndroidEventConfig.json +++ b/src/v0/destinations/singular/data/SINGULARAndroidEventConfig.json @@ -88,17 +88,23 @@ { "destKey": "aifa", "sourceKeys": "context.device.advertisingId", - "required": true + "required": false, + "metadata": { + "defaultValue": "" + } }, { "destKey": "andi", "sourceKeys": "context.device.id", - "required": true + "required": false, + "metadata": { + "defaultValue": "" + } }, { "destKey": "asid", "sourceKeys": "properties.asid", - "required": true, + "required": false, "metadata": { "defaultValue": "" } diff --git a/src/v0/destinations/singular/data/SINGULARAndroidSessionConfig.json b/src/v0/destinations/singular/data/SINGULARAndroidSessionConfig.json index ed3b410f43..e40857c61b 100644 --- a/src/v0/destinations/singular/data/SINGULARAndroidSessionConfig.json +++ b/src/v0/destinations/singular/data/SINGULARAndroidSessionConfig.json @@ -42,7 +42,7 @@ { "destKey": "asid", "sourceKeys": "properties.asid", - "required": true, + "required": false, "metadata": { "defaultValue": "" } @@ -91,12 +91,18 @@ { "destKey": "aifa", "sourceKeys": "context.device.advertisingId", - "required": true + "required": false, + "metadata": { + "defaultValue": "" + } }, { "destKey": "andi", "sourceKeys": "context.device.id", - "required": true + "required": false, + "metadata": { + "defaultValue": "" + } }, { "destKey": "utime", From 1c8e950f3d8789b33bba69a30c9eb21c40ce3d04 Mon Sep 17 00:00:00 2001 From: Anant Jain <62471433+anantjain45823@users.noreply.github.com> Date: Tue, 25 Jun 2024 12:19:19 +0530 Subject: [PATCH 236/240] fix: enhancement: introduce user model for one signal (#3499) * feat: onboard user API for onesignal * chore: add aliases from one signal intg object * chore: add test cases * chore: add test cases+1 * chore: add test cases+2 * chore: small fixes * fix: lint errors * fix: lint errors+1 * fix: lint errors+2 * chore: rename file * chore: address comments --- src/v0/destinations/one_signal/config.js | 21 ++ .../data/OneSignalIdentifyConfigV2.json | 54 +++ .../data/OneSignalSubscriptionConfig.json | 8 + src/v0/destinations/one_signal/transform.js | 9 +- src/v0/destinations/one_signal/transformV2.js | 159 ++++++++ src/v0/destinations/one_signal/util.js | 158 +++++++- src/v0/destinations/one_signal/utils.test.js | 35 ++ .../one_signal/processor/commonConfig.ts | 45 +++ .../destinations/one_signal/processor/data.ts | 2 + .../one_signal/processor/data_v2.ts | 5 + .../one_signal/processor/group.ts | 121 +++++++ .../one_signal/processor/identify.ts | 341 ++++++++++++++++++ .../one_signal/processor/track.ts | 249 +++++++++++++ .../one_signal/processor/validations.ts | 149 ++++++++ 14 files changed, 1351 insertions(+), 5 deletions(-) create mode 100644 src/v0/destinations/one_signal/data/OneSignalIdentifyConfigV2.json create mode 100644 src/v0/destinations/one_signal/data/OneSignalSubscriptionConfig.json create mode 100644 src/v0/destinations/one_signal/transformV2.js create mode 100644 src/v0/destinations/one_signal/utils.test.js create mode 100644 test/integrations/destinations/one_signal/processor/commonConfig.ts create mode 100644 test/integrations/destinations/one_signal/processor/data_v2.ts create mode 100644 test/integrations/destinations/one_signal/processor/group.ts create mode 100644 test/integrations/destinations/one_signal/processor/identify.ts create mode 100644 test/integrations/destinations/one_signal/processor/track.ts create mode 100644 test/integrations/destinations/one_signal/processor/validations.ts diff --git a/src/v0/destinations/one_signal/config.js b/src/v0/destinations/one_signal/config.js index 1a58f3f91f..fdaa3ecd7f 100644 --- a/src/v0/destinations/one_signal/config.js +++ b/src/v0/destinations/one_signal/config.js @@ -1,6 +1,7 @@ const { getMappingConfig } = require('../../util'); const BASE_URL = 'https://onesignal.com/api/v1'; +const BASE_URL_V2 = 'https://api.onesignal.com/apps/{{app_id}}/users'; const ENDPOINTS = { IDENTIFY: { @@ -16,13 +17,33 @@ const ENDPOINTS = { const ConfigCategory = { IDENTIFY: { name: 'OneSignalIdentifyConfig', endpoint: '/players' }, + IDENTIFY_V2: { name: 'OneSignalIdentifyConfigV2' }, + SUBSCRIPTION: { name: 'OneSignalSubscriptionConfig' }, }; const mappingConfig = getMappingConfig(ConfigCategory, __dirname); +// Used for User Model (V2) +const deviceTypesV2Enums = [ + 'iOSPush', + 'email', + 'sms', + 'AndroidPush', + 'HuaweiPush', + 'FireOSPush', + 'WindowsPush', + 'macOSPush', + 'ChromeExtensionPush', + 'ChromePush', + 'SafariLegacyPush', + 'FirefoxPush', + 'SafariPush', +]; module.exports = { BASE_URL, + BASE_URL_V2, ENDPOINTS, ConfigCategory, mappingConfig, + deviceTypesV2Enums, }; diff --git a/src/v0/destinations/one_signal/data/OneSignalIdentifyConfigV2.json b/src/v0/destinations/one_signal/data/OneSignalIdentifyConfigV2.json new file mode 100644 index 0000000000..61ab6e0109 --- /dev/null +++ b/src/v0/destinations/one_signal/data/OneSignalIdentifyConfigV2.json @@ -0,0 +1,54 @@ +[ + { "sourceKeys": "context.locale", "destKey": "properties.laguage", "required": false }, + { "sourceKeys": "context.ip", "destKey": "properties.ip", "required": false }, + { "sourceKeys": "context.timezone", "destKey": "properties.timezone_id", "required": false }, + { "sourceKeys": "context.location.latitude", "destKey": "properties.lat", "required": false }, + { "sourceKeys": "context.location.longitude", "destKey": "properties.long", "required": false }, + { + "sourceKeys": "createdAt", + "destKey": "properties.created_at", + "sourceFromGenericMap": true, + "metadata": { + "type": "secondTimestamp" + }, + "required": false + }, + { + "sourceKeys": "createdAt", + "destKey": "properties.last_active", + "sourceFromGenericMap": true, + "metadata": { + "type": "secondTimestamp" + }, + "required": false + }, + { + "sourceKeys": [ + "traits.country", + "context.traits.country", + "traits.address.country", + "context.traits.address.country" + ], + "destKey": "properties.country", + "required": false + }, + { + "sourceKeys": [ + "traits.firstActive", + "context.traits.firstActive", + "traits.first_active", + "context.traits.first_active" + ], + "metadata": { + "type": "secondTimestamp" + }, + "destKey": "properties.first_active", + "required": false + }, + { + "sourceKeys": "userIdOnly", + "destKey": "identity.external_id", + "sourceFromGenericMap": true, + "required": false + } +] diff --git a/src/v0/destinations/one_signal/data/OneSignalSubscriptionConfig.json b/src/v0/destinations/one_signal/data/OneSignalSubscriptionConfig.json new file mode 100644 index 0000000000..4a7a877daa --- /dev/null +++ b/src/v0/destinations/one_signal/data/OneSignalSubscriptionConfig.json @@ -0,0 +1,8 @@ +[ + { "sourceKeys": "enabled", "destKey": "enabled", "required": false }, + { "sourceKeys": "notification_types", "destKey": "notification_types", "required": false }, + { "sourceKeys": "session_time", "destKey": "session_time", "required": false }, + { "sourceKeys": "session_count", "destKey": "session_count", "required": false }, + { "sourceKeys": "app_version", "destKey": "app_version", "required": false }, + { "sourceKeys": "test_type", "destKey": "test_type", "required": false } +] diff --git a/src/v0/destinations/one_signal/transform.js b/src/v0/destinations/one_signal/transform.js index b025660fa4..aac48e3b4e 100644 --- a/src/v0/destinations/one_signal/transform.js +++ b/src/v0/destinations/one_signal/transform.js @@ -4,6 +4,7 @@ const { TransformationError, InstrumentationError, } = require('@rudderstack/integrations-lib'); +const { process: processV2 } = require('./transformV2'); const { EventType } = require('../../../constants'); const { ConfigCategory, mappingConfig, BASE_URL, ENDPOINTS } = require('./config'); const { @@ -186,10 +187,16 @@ const groupResponseBuilder = (message, { Config }) => { }; const processEvent = (message, destination) => { + const { Config } = destination; + const { version, appId } = Config; + if (version === 'V2') { + // This version is used to direct the request to user centric model + return processV2(message, destination); + } if (!message.type) { throw new InstrumentationError('Event type is required'); } - if (!destination.Config.appId) { + if (!appId) { throw new ConfigurationError('appId is a required field'); } const messageType = message.type.toLowerCase(); diff --git a/src/v0/destinations/one_signal/transformV2.js b/src/v0/destinations/one_signal/transformV2.js new file mode 100644 index 0000000000..3d084e3c8a --- /dev/null +++ b/src/v0/destinations/one_signal/transformV2.js @@ -0,0 +1,159 @@ +const get = require('get-value'); +const { + ConfigurationError, + TransformationError, + InstrumentationError, +} = require('@rudderstack/integrations-lib'); +const { EventType } = require('../../../constants'); +const { ConfigCategory, mappingConfig, BASE_URL_V2 } = require('./config'); +const { + defaultRequestConfig, + getFieldValueFromMessage, + constructPayload, + defaultPostRequestConfig, + removeUndefinedAndNullValues, +} = require('../../util'); +const { + populateTags, + getProductPurchasesDetails, + getSubscriptions, + getOneSignalAliases, +} = require('./util'); +const { JSON_MIME_TYPE } = require('../../util/constant'); + +const responseBuilder = (payload, Config) => { + const { appId } = Config; + if (payload) { + const response = defaultRequestConfig(); + response.endpoint = `${BASE_URL_V2.replace('{{app_id}}', appId)}`; + response.headers = { + Accept: JSON_MIME_TYPE, + 'Content-Type': JSON_MIME_TYPE, + }; + response.method = defaultPostRequestConfig.requestMethod; + response.body.JSON = removeUndefinedAndNullValues(payload); + return response; + } + throw new TransformationError('Payload could not be populated due to wrong input'); +}; + +/** + * This function is used for creating response for identify call, to create a new user or update an existing user. + * a responseArray for creating/updating user is being prepared. + * If the value of emailDeviceType/smsDeviceType(toggle in dashboard) is true, separate responses will also be created + * for new subscriptions to be added to user with email/sms as token. + * @param {*} message + * @param {*} param1 + * @returns + */ +const identifyResponseBuilder = (message, { Config }) => { + // Populating the tags + const tags = populateTags(message); + + const payload = constructPayload(message, mappingConfig[ConfigCategory.IDENTIFY_V2.name]); + if (!payload?.identity?.external_id) { + const alias = getOneSignalAliases(message); + if (Object.keys(alias).length === 0) { + throw new InstrumentationError('userId or any other alias is required for identify'); + } + payload.identity = alias; + } + // Following check is to intialise properties object in case we don't get properties object from construct payload + if (!payload.properties) { + payload.properties = {}; + } + payload.subscriptions = getSubscriptions(message, Config); + payload.properties.tags = tags; + return responseBuilder(removeUndefinedAndNullValues(payload), Config); +}; + +/** + * This function is used to build the response for track call and Group call. + * It is used to edit the OneSignal tags using external_id. + * It edits tags[event] as true for track call + * @param {*} message + * @param {*} param1 + * @returns + */ +const trackOrGroupResponseBuilder = (message, { Config }, msgtype) => { + const { eventAsTags, allowedProperties } = Config; + const event = get(message, 'event'); + const groupId = getFieldValueFromMessage(message, 'groupId'); + // validation and adding tags for track and group call respectively + const tags = {}; + const payload = { properties: {} }; + if (msgtype === EventType.TRACK) { + if (!event) { + throw new InstrumentationError('Event is not present in the input payloads'); + } + /* Populating event as true in tags. + eg. tags: { + "event_name": true + } + */ + tags[event] = true; + payload.properties.purchases = getProductPurchasesDetails(message); + } + if (msgtype === EventType.GROUP) { + if (!groupId) { + throw new InstrumentationError('groupId is required for group events'); + } + tags.groupId = groupId; + } + + const externalUserId = getFieldValueFromMessage(message, 'userIdOnly'); + if (!externalUserId) { + const alias = getOneSignalAliases(message); + if (Object.keys(alias).length === 0) { + throw new InstrumentationError('userId or any other alias is required for track and group'); + } + payload.identity = alias; + } else { + payload.identity = { + external_id: externalUserId, + }; + } + + // Populating tags using allowed properties(from dashboard) + const properties = get(message, 'properties'); + if (properties && allowedProperties && Array.isArray(allowedProperties)) { + allowedProperties.forEach((item) => { + if (typeof properties[item.propertyName] === 'string') { + const tagName = + event && eventAsTags ? `${event}_${[item.propertyName]}` : item.propertyName; + tags[tagName] = properties[item.propertyName]; + } + }); + } + payload.properties.tags = tags; + return responseBuilder(removeUndefinedAndNullValues(payload), Config); +}; + +const processEvent = (message, destination) => { + if (!message.type) { + throw new InstrumentationError('Event type is required'); + } + if (!destination.Config.appId) { + throw new ConfigurationError('appId is a required field'); + } + const messageType = message.type.toLowerCase(); + let response; + switch (messageType) { + case EventType.IDENTIFY: + response = identifyResponseBuilder(message, destination); + break; + case EventType.TRACK: + response = trackOrGroupResponseBuilder(message, destination, EventType.TRACK); + break; + case EventType.GROUP: + response = trackOrGroupResponseBuilder(message, destination, EventType.GROUP); + break; + default: + throw new InstrumentationError(`Message type ${messageType} is not supported`); + } + return response; +}; + +const process = (message, destination) => processEvent(message, destination); + +module.exports = { process }; diff --git a/src/v0/destinations/one_signal/util.js b/src/v0/destinations/one_signal/util.js index 2de57de1b4..69cbd5440c 100644 --- a/src/v0/destinations/one_signal/util.js +++ b/src/v0/destinations/one_signal/util.js @@ -1,6 +1,13 @@ const { InstrumentationError } = require('@rudderstack/integrations-lib'); -const { getIntegrationsObj, getFieldValueFromMessage, getBrowserInfo } = require('../../util'); - +const { + getIntegrationsObj, + getFieldValueFromMessage, + getBrowserInfo, + constructPayload, + removeUndefinedAndNullValues, +} = require('../../util'); +const { ConfigCategory, mappingConfig, deviceTypesV2Enums } = require('./config'); +const { isDefinedAndNotNullAndNotEmpty } = require('../../util'); // For mapping device_type value const deviceTypeMapping = { android: 1, @@ -45,7 +52,7 @@ const populateTags = (message) => { const populateDeviceType = (message, payload) => { const integrationsObj = getIntegrationsObj(message, 'one_signal'); const devicePayload = payload; - if (integrationsObj && integrationsObj.deviceType && integrationsObj.identifier) { + if (integrationsObj?.deviceType && integrationsObj?.identifier) { devicePayload.device_type = parseInt(integrationsObj.deviceType, 10); devicePayload.identifier = integrationsObj.identifier; if (!validateDeviceType(devicePayload.device_type)) { @@ -72,4 +79,147 @@ const populateDeviceType = (message, payload) => { } }; -module.exports = { populateDeviceType, populateTags }; +/** + * This function is used to populate device type required for creating a subscription + * it checks from integrations object and fall back to message.channel and fif nothing is given it return a n empty object + * @param {*} message + * @param {*} payload + * returns Object + */ +const getDeviceDetails = (message) => { + const integrationsObj = getIntegrationsObj(message, 'one_signal'); + const devicePayload = {}; + if (integrationsObj?.deviceType && integrationsObj?.identifier) { + devicePayload.type = integrationsObj.deviceType; + devicePayload.token = integrationsObj.token || integrationsObj.identifier; + } + // Mapping device type when it is not present in the integrationsObject + if (!devicePayload.type) { + // if channel is mobile, checking for type from `context.device.type` + if (message.channel === 'mobile') { + devicePayload.type = message.context?.device?.type; + devicePayload.token = message.context?.device?.token + ? message.context.device.token + : message.context?.device?.id; + } + // Parsing the UA to get the browser info to map the device_type + if (message.channel === 'web' && message.context?.userAgent) { + const browser = getBrowserInfo(message.context.userAgent); + devicePayload.type = `${browser.name}Push`; // For chrome it would be like ChromePush + devicePayload.token = message.anonymousId; + } + } + if (!deviceTypesV2Enums.includes(devicePayload.type)) { + return {}; // No device related information available + } + return devicePayload; +}; +/** + * This function maps and returns the product purchases details built from input message.properties.products + * @param {*} message + * @returns + */ +const getProductPurchasesDetails = (message) => { + const { properties } = message; + const purchases = properties?.products; + if (purchases && Array.isArray(purchases)) { + return purchases.map((product) => ({ + sku: product.sku, + iso: product.iso, + count: product.quantity, + amount: product.amount, + })); + } + const purchaseObject = removeUndefinedAndNullValues({ + sku: properties?.sku, + iso: properties?.iso, + count: properties?.quantity, + amount: properties?.amount, + }); + return Object.keys(purchaseObject).length > 0 ? [purchaseObject] : []; +}; + +/** + * This function generates the subscriptions Payload for the given deviceType and token + * https://documentation.onesignal.com/reference/create-user#:~:text=string-,subscriptions,-array%20of%20objects + * @param {*} message + * @param {*} deviceType + * @param {*} token + * @returns + */ +const constructSubscription = (message, subscriptionType, token, subscriptionField) => { + const deviceModel = message.context?.device?.model; + const deviceOs = message.context?.os?.version; + let deviceSubscriptionPayload = { + type: subscriptionType, + token, + device_model: deviceModel, + device_os: deviceOs, + }; + // Following mapping is used to do paticular and specific property mapping for subscription + const traits = message.context?.traits || message.traits; + if (traits?.subscriptions?.[subscriptionField]) { + deviceSubscriptionPayload = { + ...deviceSubscriptionPayload, + ...constructPayload( + traits.subscriptions[subscriptionField], + mappingConfig[ConfigCategory.SUBSCRIPTION.name], + ), + }; + } + return deviceSubscriptionPayload; +}; + +/** + * This function constructs subscriptions list from message and returns subscriptions list + * @param {*} message + * @param {*} Config + * @returns + */ +const getSubscriptions = (message, Config) => { + const { emailDeviceType, smsDeviceType } = Config; + // Creating response for creation of new device or updation of an existing device + const subscriptions = []; + const deviceTypeSubscription = getDeviceDetails(message); + if (deviceTypeSubscription.token) { + subscriptions.push( + constructSubscription(message, deviceTypeSubscription.type, deviceTypeSubscription.token), + ); + } + + // Creating a device with email as an identifier + if (emailDeviceType) { + const token = getFieldValueFromMessage(message, 'email'); + if (isDefinedAndNotNullAndNotEmpty(token)) { + subscriptions.push(constructSubscription(message, 'Email', token, 'email')); + } + } + // Creating a device with phone as an identifier + if (smsDeviceType) { + const token = getFieldValueFromMessage(message, 'phone'); + if (isDefinedAndNotNullAndNotEmpty(token)) { + subscriptions.push(constructSubscription(message, 'SMS', token, 'phone')); + } + } + return subscriptions.length > 0 ? subscriptions : undefined; +}; + +/** + * This function fetched all the aliases to be passed to one signal from integrations object + * @param {*} message + * @returns object + */ +const getOneSignalAliases = (message) => { + const integrationsObj = getIntegrationsObj(message, 'one_signal'); + if (integrationsObj?.aliases) { + return integrationsObj.aliases; + } + return {}; +}; +module.exports = { + populateDeviceType, + populateTags, + getProductPurchasesDetails, + getSubscriptions, + getOneSignalAliases, +}; diff --git a/src/v0/destinations/one_signal/utils.test.js b/src/v0/destinations/one_signal/utils.test.js new file mode 100644 index 0000000000..afcf746ab6 --- /dev/null +++ b/src/v0/destinations/one_signal/utils.test.js @@ -0,0 +1,35 @@ +const { getOneSignalAliases } = require('./util'); + +describe('getOneSignalAliases', () => { + // returns aliases when integrationsObj contains aliases + it('should return aliases when integrationsObj contains aliases', () => { + const message = { + someKey: 'someValue', + integrations: { one_signal: { aliases: { alias1: 'value1' } } }, + }; + const result = getOneSignalAliases(message); + expect(result).toEqual({ alias1: 'value1' }); + }); + + // handles null or undefined message parameter gracefully + it('should handle null or undefined message parameter gracefully', () => { + let result = getOneSignalAliases(null); + expect(result).toEqual({}); + result = getOneSignalAliases(undefined); + expect(result).toEqual({}); + }); + + // returns an empty object when integrationsObj does not contain aliases + it('should return an empty object when integrationsObj does not contain aliases', () => { + const message = { someKey: 'someValue', integrations: { one_signal: {} } }; + const result = getOneSignalAliases(message); + expect(result).toEqual({}); + }); + + // handles message parameter with unexpected structure + it('should handle message parameter with unexpected structure', () => { + const message = null; + const result = getOneSignalAliases(message); + expect(result).toEqual({}); + }); +}); diff --git a/test/integrations/destinations/one_signal/processor/commonConfig.ts b/test/integrations/destinations/one_signal/processor/commonConfig.ts new file mode 100644 index 0000000000..cdef3dbfb4 --- /dev/null +++ b/test/integrations/destinations/one_signal/processor/commonConfig.ts @@ -0,0 +1,45 @@ +export const destination = { + Config: { + appId: 'random-818c-4a28-b98e-6cd8a994eb22', + emailDeviceType: true, + smsDeviceType: true, + eventAsTags: false, + allowedProperties: [ + { propertyName: 'brand' }, + { propertyName: 'firstName' }, + { propertyName: 'price' }, + ], + version: 'V2', + }, +}; + +export const endpoint = 'https://api.onesignal.com/apps/random-818c-4a28-b98e-6cd8a994eb22/users'; + +export const headers = { Accept: 'application/json', 'Content-Type': 'application/json' }; + +export const commonTraits = { + brand: 'John Players', + price: '15000', + firstName: 'Test', +}; +export const commonTags = { + brand: 'John Players', + price: '15000', + firstName: 'Test', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', +}; + +export const commonProperties = { + products: [ + { + sku: 3, + iso: 'iso', + quantity: 2, + amount: 100, + }, + ], + brand: 'John Players', + price: '15000', + firstName: 'Test', + customKey: 'customVal', +}; diff --git a/test/integrations/destinations/one_signal/processor/data.ts b/test/integrations/destinations/one_signal/processor/data.ts index 4171157aef..13aa6043f1 100644 --- a/test/integrations/destinations/one_signal/processor/data.ts +++ b/test/integrations/destinations/one_signal/processor/data.ts @@ -1,3 +1,4 @@ +import { data_v2 } from './data_v2'; export const data = [ { name: 'one_signal', @@ -1542,4 +1543,5 @@ export const data = [ }, }, }, + ...data_v2, ]; diff --git a/test/integrations/destinations/one_signal/processor/data_v2.ts b/test/integrations/destinations/one_signal/processor/data_v2.ts new file mode 100644 index 0000000000..325dca336d --- /dev/null +++ b/test/integrations/destinations/one_signal/processor/data_v2.ts @@ -0,0 +1,5 @@ +import { identifyTests } from './identify'; +import { trackTests } from './track'; +import { validations } from './validations'; +import { groupTests } from './group'; +export const data_v2 = [...identifyTests, ...trackTests, ...validations, ...groupTests]; diff --git a/test/integrations/destinations/one_signal/processor/group.ts b/test/integrations/destinations/one_signal/processor/group.ts new file mode 100644 index 0000000000..e16d1e4fe6 --- /dev/null +++ b/test/integrations/destinations/one_signal/processor/group.ts @@ -0,0 +1,121 @@ +import { destination, endpoint, headers } from './commonConfig'; +export const groupTests = [ + { + name: 'one_signal', + id: 'One Signal V2-test-group-success-1', + description: + 'Group call for adding a tag groupId with value as group id with no userId available', + module: 'destination', + successCriteria: 'Request gets passed with 200 Status Code with userId mapped to external_id', + feature: 'processor', + scenario: 'Framework+Buisness', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + integrations: { + one_signal: { + aliases: { custom_alias: 'custom_alias_identifier' }, + }, + }, + type: 'group', + channel: 'server', + groupId: 'group@27', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + FORM: {}, + JSON: { + properties: { + tags: { + groupId: 'group@27', + }, + }, + identity: { + custom_alias: 'custom_alias_identifier', + }, + }, + JSON_ARRAY: {}, + XML: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-group-failure-1', + description: 'V2-> No Group Id Passes', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code and failure happened due no group id available', + feature: 'processor', + scenario: 'Framework', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'group', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + error: 'groupId is required for group events', + statTags: { + destType: 'ONE_SIGNAL', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/one_signal/processor/identify.ts b/test/integrations/destinations/one_signal/processor/identify.ts new file mode 100644 index 0000000000..175c18a8fa --- /dev/null +++ b/test/integrations/destinations/one_signal/processor/identify.ts @@ -0,0 +1,341 @@ +import { commonTags, commonTraits, destination, endpoint, headers } from './commonConfig'; + +export const identifyTests = [ + { + name: 'one_signal', + id: 'One Signal V2-test-identify-success-1', + description: + 'V2-> Identify call for creating new user with userId only available and no subscriptions available', + module: 'destination', + successCriteria: 'Request gets passed with 200 Status Code with userId mapped to external_id', + feature: 'processor', + scenario: 'Framework+Buisness', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + userId: 'user@27', + channel: 'server', + context: { + app: { + version: '1.1.11', + }, + traits: commonTraits, + locale: 'en-US', + screen: { density: 2 }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + FORM: {}, + JSON: { + properties: { + tags: commonTags, + laguage: 'en-US', + created_at: 1609693373, + last_active: 1609693373, + }, + identity: { + external_id: 'user@27', + }, + }, + JSON_ARRAY: {}, + XML: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-identify-success-2', + description: + 'V2-> Identify call for creating new user with userId and one device subscription available', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code with userId mapped to external_id and one subscription for device where identifier is mapped from anonymousId', + feature: 'processor', + scenario: 'Framework+Buisness', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + userId: 'user@27', + channel: 'web', + context: { + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.11', + namespace: 'com.rudderlabs.javascript', + }, + traits: commonTraits, + locale: 'en-US', + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + FORM: {}, + JSON: { + properties: { + tags: commonTags, + laguage: 'en-US', + created_at: 1609693373, + last_active: 1609693373, + }, + subscriptions: [ + { token: '97c46c81-3140-456d-b2a9-690d70aaca35', type: 'FirefoxPush' }, + ], + identity: { + external_id: 'user@27', + }, + }, + JSON_ARRAY: {}, + XML: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-identify-success-3', + description: + 'V2-> Identify call for creating new user with userId and three device subscription available', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code with userId mapped to external_id and three subscription for device where one is mapped from anonymousId, one from email and one from phone', + feature: 'processor', + scenario: 'Framework+Buisness', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + integrations: { + one_signal: { + aliases: { custom_alias: 'custom_alias_identifier' }, + }, + }, + channel: 'web', + context: { + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.11', + namespace: 'com.rudderlabs.javascript', + }, + device: { + model: 'dummy model', + }, + os: { version: '1.0.0' }, + traits: { + ...commonTraits, + email: 'example@abc.com', + phone: '12345678', + subscriptions: { + email: { + enabled: true, + notification_types: 'SMS', + session_time: 123456, + session_count: 22, + app_version: '1.0.0', + test_type: 'dev', + }, + }, + }, + locale: 'en-US', + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + FORM: {}, + JSON: { + properties: { + tags: { ...commonTags, email: 'example@abc.com', phone: '12345678' }, + laguage: 'en-US', + created_at: 1609693373, + last_active: 1609693373, + }, + subscriptions: [ + { + device_model: 'dummy model', + device_os: '1.0.0', + token: '97c46c81-3140-456d-b2a9-690d70aaca35', + type: 'FirefoxPush', + }, + { + app_version: '1.0.0', + device_model: 'dummy model', + device_os: '1.0.0', + enabled: true, + notification_types: 'SMS', + session_count: 22, + session_time: 123456, + test_type: 'dev', + token: 'example@abc.com', + type: 'Email', + }, + { + device_model: 'dummy model', + device_os: '1.0.0', + token: '12345678', + type: 'SMS', + }, + ], + identity: { custom_alias: 'custom_alias_identifier' }, + }, + JSON_ARRAY: {}, + XML: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-identify-failure-1', + description: 'V2-> Identify call without any aliases', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code and failure happened due no aliases present', + feature: 'processor', + scenario: 'Framework', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + channel: 'server', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + error: 'userId or any other alias is required for identify', + statTags: { + destType: 'ONE_SIGNAL', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/one_signal/processor/track.ts b/test/integrations/destinations/one_signal/processor/track.ts new file mode 100644 index 0000000000..41735fc7a2 --- /dev/null +++ b/test/integrations/destinations/one_signal/processor/track.ts @@ -0,0 +1,249 @@ +import { commonProperties, destination, endpoint, headers } from './commonConfig'; + +const commonTrackTags = { + brand: 'John Players', + price: '15000', + firstName: 'Test', +}; + +const purchases = [ + { + sku: 3, + iso: 'iso', + count: 2, + amount: 100, + }, +]; + +export const trackTests = [ + { + name: 'one_signal', + id: 'One Signal V2-test-track-success-1', + description: + 'V2-> Track call for updating user tags with userId available and products details available', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code with userId mapped to external_id and properties mapped to tags', + feature: 'processor', + scenario: 'Framework+Buisness', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'Order Completed', + userId: 'user@27', + channel: 'server', + properties: commonProperties, + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + FORM: {}, + JSON: { + properties: { purchases, tags: { ...commonTrackTags, 'Order Completed': true } }, + + identity: { + external_id: 'user@27', + }, + }, + JSON_ARRAY: {}, + XML: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-track-success-2', + description: 'V2-> Track call for products details available in properties directly', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code with userId mapped to external_id and purchases mapped from proeprties mapped to tags', + feature: 'processor', + scenario: 'Framework+Buisness', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'Order Completed', + userId: 'user@27', + channel: 'server', + properties: {}, + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + FORM: {}, + JSON: { + properties: { purchases: [], tags: { 'Order Completed': true } }, + identity: { + external_id: 'user@27', + }, + }, + JSON_ARRAY: {}, + XML: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-track-failure-1', + description: 'V2-> Track call without event name', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code and failure happened due instrumentation error', + feature: 'processor', + scenario: 'Framework+Buisness', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + userId: 'user@27', + channel: 'server', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Event is not present in the input payloads', + statTags: { + destType: 'ONE_SIGNAL', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-track-failure-2', + description: 'V2-> Track call without any aliases', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code and failure happened due no aliases present', + feature: 'processor', + scenario: 'Framework', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'dummy event', + channel: 'server', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + error: 'userId or any other alias is required for track and group', + statTags: { + destType: 'ONE_SIGNAL', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/one_signal/processor/validations.ts b/test/integrations/destinations/one_signal/processor/validations.ts new file mode 100644 index 0000000000..7cfa158eee --- /dev/null +++ b/test/integrations/destinations/one_signal/processor/validations.ts @@ -0,0 +1,149 @@ +import { destination } from './commonConfig'; +export const validations = [ + { + name: 'one_signal', + id: 'One Signal V2-test-validation-failure-1', + description: 'V2-> No Message type passed', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code and failure happened due no message type present', + feature: 'processor', + scenario: 'Framework', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + userId: 'user@27', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Event type is required', + statTags: { + destType: 'ONE_SIGNAL', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-validation-failure-2', + description: 'V2-> invalid Message type passed', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code and failure happened due invalid message type present', + feature: 'processor', + scenario: 'Framework', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'invalid', + userId: 'user@27', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Message type invalid is not supported', + statTags: { + destType: 'ONE_SIGNAL', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-validation-failure-3', + description: 'V2-> No App Id Present in destination Config', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code and failure happened due no Configuration Error', + feature: 'processor', + scenario: 'Framework', + version: 'v0', + input: { + request: { + body: [ + { + destination: { Config: {} }, + message: { + type: 'invalid', + userId: 'user@27', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + error: 'appId is a required field', + statTags: { + destType: 'ONE_SIGNAL', + errorCategory: 'dataValidation', + errorType: 'configuration', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; From dc5b9fca7eee94af54cb604e72a177b9edf38e3d Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 25 Jun 2024 06:52:47 +0000 Subject: [PATCH 237/240] chore(release): 1.69.1 --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42b22827da..dbe8ae98fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [1.69.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.69.0...v1.69.1) (2024-06-25) + + +### Bug Fixes + +* enhancement: introduce user model for one signal ([#3499](https://github.com/rudderlabs/rudder-transformer/issues/3499)) ([1c8e950](https://github.com/rudderlabs/rudder-transformer/commit/1c8e950f3d8789b33bba69a30c9eb21c40ce3d04)) + ## [1.69.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.68.2...v1.69.0) (2024-06-10) diff --git a/package-lock.json b/package-lock.json index 7fa0d6d3d4..c96e589aff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.69.0", + "version": "1.69.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.69.0", + "version": "1.69.1", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index 2c7c6711e0..89513532e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.69.0", + "version": "1.69.1", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From 8129a06e3a12c9bca17354598849750498c72d2e Mon Sep 17 00:00:00 2001 From: Jayachand Date: Tue, 25 Jun 2024 15:32:23 +0530 Subject: [PATCH 238/240] fix: metadata tags capturing in v0 transformation (#3492) * fix: metadata tags capturing in v0 transformation --- src/util/customTransformer.js | 6 ++++-- src/v0/util/index.js | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/util/customTransformer.js b/src/util/customTransformer.js index 787ce04d63..4f4620fd2d 100644 --- a/src/util/customTransformer.js +++ b/src/util/customTransformer.js @@ -253,8 +253,10 @@ async function runUserTransform( const tags = { identifier: 'v0', errored: transformationError ? true : false, - ...(events.length && events[0].metadata ? getMetadata(events[0].metadata) : {}), - ...(events.length && events[0].metadata ? getTransformationMetadata(events[0].metadata) : {}), + ...(Object.keys(eventsMetadata).length ? getMetadata(Object.values(eventsMetadata)[0]) : {}), + ...(Object.keys(eventsMetadata).length + ? getTransformationMetadata(Object.values(eventsMetadata)[0]) + : {}), }; stats.counter('user_transform_function_input_events', events.length, tags); diff --git a/src/v0/util/index.js b/src/v0/util/index.js index 366c58ce93..12b8d4dd7e 100644 --- a/src/v0/util/index.js +++ b/src/v0/util/index.js @@ -1447,13 +1447,13 @@ const getTrackingPlanMetadata = (metadata) => ({ workspaceId: metadata.workspaceId, }); -const getMetadata = (metadata) => ({ +const getMetadata = (metadata = {}) => ({ sourceType: metadata.sourceType, destinationType: metadata.destinationType, k8_namespace: metadata.namespace, }); -const getTransformationMetadata = (metadata) => ({ +const getTransformationMetadata = (metadata = {}) => ({ transformationId: metadata.transformationId, workspaceId: metadata.workspaceId, }); From 0379c4d8d5cf779f84e7d44a6d061ce988290e2e Mon Sep 17 00:00:00 2001 From: Jayachand Date: Tue, 25 Jun 2024 17:41:12 +0530 Subject: [PATCH 239/240] chore: remove version id, clean unused stats (#3494) * chore: remove versionId, unused or unimportant stats --- src/legacy/router.js | 18 +- src/util/customTransformer-faas.js | 2 +- src/util/customTransformer-v1.js | 1 - src/util/customTransformer.js | 22 +- src/util/customTransforrmationsStore-v1.js | 37 +- src/util/customTransforrmationsStore.js | 8 +- src/util/ivmFactory.js | 18 +- src/util/prometheus.js | 558 ++++++++------------- src/util/utils.js | 14 +- 9 files changed, 254 insertions(+), 424 deletions(-) diff --git a/src/legacy/router.js b/src/legacy/router.js index afc8c1a797..043e37b66d 100644 --- a/src/legacy/router.js +++ b/src/legacy/router.js @@ -539,9 +539,7 @@ if (startDestTransformer) { (event) => `${event.metadata.destinationId}_${event.metadata.sourceId}`, ); } - stats.counter('user_transform_function_group_size', Object.entries(groupedEvents).length, { - processSessions, - }); + stats.counter('user_transform_function_group_size', Object.entries(groupedEvents).length, {}); let ctxStatusCode = 200; const transformedEvents = []; @@ -646,16 +644,10 @@ if (startDestTransformer) { ctx.status = ctxStatusCode; ctx.set('apiVersion', API_VERSION); - stats.timing('user_transform_request_latency', startTime, { - processSessions, - }); - stats.timingSummary('user_transform_request_latency_summary', startTime, { - processSessions, - }); - stats.increment('user_transform_requests', { processSessions }); - stats.histogram('user_transform_output_events', transformedEvents.length, { - processSessions, - }); + stats.timing('user_transform_request_latency', startTime, {}); + stats.timingSummary('user_transform_request_latency_summary', startTime, {}); + stats.increment('user_transform_requests', {}); + stats.histogram('user_transform_output_events', transformedEvents.length, {}); }); } } diff --git a/src/util/customTransformer-faas.js b/src/util/customTransformer-faas.js index 9ac9804097..07dc205582 100644 --- a/src/util/customTransformer-faas.js +++ b/src/util/customTransformer-faas.js @@ -91,7 +91,7 @@ async function setOpenFaasUserTransform( trMetadata = {}, ) { const tags = { - transformerVersionId: userTransformation.versionId, + transformationId: userTransformation.id, identifier: 'openfaas', testMode, }; diff --git a/src/util/customTransformer-v1.js b/src/util/customTransformer-v1.js index b1865dee6b..12dab547e6 100644 --- a/src/util/customTransformer-v1.js +++ b/src/util/customTransformer-v1.js @@ -65,7 +65,6 @@ async function userTransformHandlerV1( const isolatevmFactory = await getFactory( userTransformation.code, libraryVersionIds, - userTransformation.versionId, userTransformation.id, userTransformation.workspaceId, credentialsMap, diff --git a/src/util/customTransformer.js b/src/util/customTransformer.js index 4f4620fd2d..5ca1fae47c 100644 --- a/src/util/customTransformer.js +++ b/src/util/customTransformer.js @@ -16,9 +16,11 @@ async function runUserTransform( code, secrets, eventsMetadata, - versionId, + transformationId, + workspaceId, testMode = false, ) { + const trTags = { identifier: 'v0', transformationId, workspaceId }; // TODO: Decide on the right value for memory limit const isolate = new ivm.Isolate({ memoryLimit: ISOLATE_VM_MEMORY }); const context = await isolate.createContext(); @@ -36,9 +38,9 @@ async function runUserTransform( new ivm.Reference(async (resolve, ...args) => { try { const fetchStartTime = new Date(); - const res = await fetchWithDnsWrapper(versionId, ...args); + const res = await fetchWithDnsWrapper(trTags, ...args); const data = await res.json(); - stats.timing('fetch_call_duration', fetchStartTime, { versionId }); + stats.timing('fetch_call_duration', fetchStartTime, trTags); resolve.applyIgnored(undefined, [new ivm.ExternalCopy(data).copyInto()]); } catch (error) { resolve.applyIgnored(undefined, [new ivm.ExternalCopy('ERROR').copyInto()]); @@ -51,7 +53,7 @@ async function runUserTransform( new ivm.Reference(async (resolve, reject, ...args) => { try { const fetchStartTime = new Date(); - const res = await fetchWithDnsWrapper(versionId, ...args); + const res = await fetchWithDnsWrapper(trTags, ...args); const headersContent = {}; res.headers.forEach((value, header) => { headersContent[header] = value; @@ -67,7 +69,7 @@ async function runUserTransform( data.body = JSON.parse(data.body); } catch (e) {} - stats.timing('fetchV2_call_duration', fetchStartTime, { versionId }); + stats.timing('fetchV2_call_duration', fetchStartTime, trTags); resolve.applyIgnored(undefined, [new ivm.ExternalCopy(data).copyInto()]); } catch (error) { const err = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error))); @@ -93,7 +95,7 @@ async function runUserTransform( throw new Error(`request to fetch geolocation failed with status code: ${res.status}`); } const geoData = await res.json(); - stats.timing('geo_call_duration', geoStartTime, { versionId }); + stats.timing('geo_call_duration', geoStartTime, trTags); resolve.applyIgnored(undefined, [new ivm.ExternalCopy(geoData).copyInto()]); } catch (error) { const err = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error))); @@ -251,12 +253,9 @@ async function runUserTransform( isolate.dispose(); const tags = { - identifier: 'v0', errored: transformationError ? true : false, ...(Object.keys(eventsMetadata).length ? getMetadata(Object.values(eventsMetadata)[0]) : {}), - ...(Object.keys(eventsMetadata).length - ? getTransformationMetadata(Object.values(eventsMetadata)[0]) - : {}), + ...trTags, }; stats.counter('user_transform_function_input_events', events.length, tags); @@ -318,7 +317,8 @@ async function userTransformHandler( res.code, res.secrets || {}, eventsMetadata, - versionId, + res.id, + res.workspaceId, testMode, ); diff --git a/src/util/customTransforrmationsStore-v1.js b/src/util/customTransforrmationsStore-v1.js index 6e2d799f3a..d2d14f318e 100644 --- a/src/util/customTransforrmationsStore-v1.js +++ b/src/util/customTransforrmationsStore-v1.js @@ -19,25 +19,19 @@ const getRudderLibrariesUrl = `${CONFIG_BACKEND_URL}/rudderstackTransformationLi async function getTransformationCodeV1(versionId) { const transformation = transformationCache[versionId]; if (transformation) return transformation; - const tags = { - versionId, - version: 1, - }; try { const url = `${getTransformationURL}?versionId=${versionId}`; - const startTime = new Date(); const response = await fetchWithProxy(url); responseStatusHandler(response.status, 'Transformation', versionId, url); - stats.increment('get_transformation_code', { success: 'true', ...tags }); - stats.timing('get_transformation_code_time', startTime, tags); - stats.timingSummary('get_transformation_code_time_summary', startTime, tags); const myJson = await response.json(); transformationCache[versionId] = myJson; return myJson; } catch (error) { - logger.error(error); - stats.increment('get_transformation_code', { success: 'false', ...tags }); + logger.error( + `Error fetching transformation V1 code for versionId: ${versionId}`, + error.message, + ); throw error; } } @@ -45,25 +39,16 @@ async function getTransformationCodeV1(versionId) { async function getLibraryCodeV1(versionId) { const library = libraryCache[versionId]; if (library) return library; - const tags = { - libraryVersionId: versionId, - version: 1, - }; try { const url = `${getLibrariesUrl}?versionId=${versionId}`; - const startTime = new Date(); const response = await fetchWithProxy(url); responseStatusHandler(response.status, 'Transformation Library', versionId, url); - stats.increment('get_libraries_code', { success: 'true', ...tags }); - stats.timing('get_libraries_code_time', startTime, tags); - stats.timingSummary('get_libraries_code_time_summary', startTime, tags); const myJson = await response.json(); libraryCache[versionId] = myJson; return myJson; } catch (error) { - logger.error(error); - stats.increment('get_libraries_code', { success: 'false', ...tags }); + logger.error(`Error fetching library code for versionId: ${versionId}`, error.message); throw error; } } @@ -71,27 +56,17 @@ async function getLibraryCodeV1(versionId) { async function getRudderLibByImportName(importName) { const rudderLibrary = rudderLibraryCache[importName]; if (rudderLibrary) return rudderLibrary; - const tags = { - libraryVersionId: importName, - version: 1, - type: 'rudderlibrary', - }; try { const [name, version] = importName.split('/').slice(-2); const url = `${getRudderLibrariesUrl}/${name}?version=${version}`; - const startTime = new Date(); const response = await fetchWithProxy(url); responseStatusHandler(response.status, 'Rudder Library', importName, url); - stats.increment('get_libraries_code', { success: 'true', ...tags }); - stats.timing('get_libraries_code_time', startTime, tags); - stats.timingSummary('get_libraries_code_time_summary', startTime, tags); const myJson = await response.json(); rudderLibraryCache[importName] = myJson; return myJson; } catch (error) { - logger.error(error); - stats.increment('get_libraries_code', { success: 'false', ...tags }); + logger.error(`Error fetching rudder library code for importName: ${importName}`, error.message); throw error; } } diff --git a/src/util/customTransforrmationsStore.js b/src/util/customTransforrmationsStore.js index 2c5a7b446d..2043d18875 100644 --- a/src/util/customTransforrmationsStore.js +++ b/src/util/customTransforrmationsStore.js @@ -2,7 +2,6 @@ const NodeCache = require('node-cache'); const { fetchWithProxy } = require('./fetch'); const logger = require('../logger'); const { responseStatusHandler } = require('./utils'); -const stats = require('./stats'); const myCache = new NodeCache({ stdTTL: 60 * 60 * 24 * 1 }); @@ -18,19 +17,14 @@ async function getTransformationCode(versionId) { if (transformation) return transformation; try { const url = `${getTransformationURL}?versionId=${versionId}`; - const startTime = new Date(); const response = await fetchWithProxy(url); responseStatusHandler(response.status, 'Transformation', versionId, url); - stats.increment('get_transformation_code', { versionId, success: 'true' }); - stats.timing('get_transformation_code_time', startTime, { versionId }); - stats.timingSummary('get_transformation_code_time_summary', startTime, { versionId }); const myJson = await response.json(); myCache.set(versionId, myJson); return myJson; } catch (error) { - logger.error(error); - stats.increment('get_transformation_code', { versionId, success: 'false' }); + logger.error(`Error fetching transformation code for versionId: ${versionId}`, error.message); throw error; } } diff --git a/src/util/ivmFactory.js b/src/util/ivmFactory.js index 44beff01dc..625591964c 100644 --- a/src/util/ivmFactory.js +++ b/src/util/ivmFactory.js @@ -33,13 +33,13 @@ async function loadModule(isolateInternal, contextInternal, moduleName, moduleCo async function createIvm( code, libraryVersionIds, - versionId, transformationId, workspaceId, credentials, secrets, testMode, ) { + const trTags = { identifier: 'V1', transformationId, workspaceId }; const createIvmStartTime = new Date(); const logs = []; const libraries = await Promise.all( @@ -187,9 +187,9 @@ async function createIvm( new ivm.Reference(async (resolve, ...args) => { try { const fetchStartTime = new Date(); - const res = await fetchWithDnsWrapper(versionId, ...args); + const res = await fetchWithDnsWrapper(trTags, ...args); const data = await res.json(); - stats.timing('fetch_call_duration', fetchStartTime, { versionId }); + stats.timing('fetch_call_duration', fetchStartTime, trTags); resolve.applyIgnored(undefined, [new ivm.ExternalCopy(data).copyInto()]); } catch (error) { resolve.applyIgnored(undefined, [new ivm.ExternalCopy('ERROR').copyInto()]); @@ -202,7 +202,7 @@ async function createIvm( new ivm.Reference(async (resolve, reject, ...args) => { try { const fetchStartTime = new Date(); - const res = await fetchWithDnsWrapper(versionId, ...args); + const res = await fetchWithDnsWrapper(trTags, ...args); const headersContent = {}; res.headers.forEach((value, header) => { headersContent[header] = value; @@ -218,7 +218,7 @@ async function createIvm( data.body = JSON.parse(data.body); } catch (e) {} - stats.timing('fetchV2_call_duration', fetchStartTime, { versionId }); + stats.timing('fetchV2_call_duration', fetchStartTime, trTags); resolve.applyIgnored(undefined, [new ivm.ExternalCopy(data).copyInto()]); } catch (error) { const err = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error))); @@ -243,7 +243,7 @@ async function createIvm( throw new Error(`request to fetch geolocation failed with status code: ${res.status}`); } const geoData = await res.json(); - stats.timing('geo_call_duration', geoStartTime, { versionId }); + stats.timing('geo_call_duration', geoStartTime, trTags); resolve.applyIgnored(undefined, [new ivm.ExternalCopy(geoData).copyInto()]); } catch (error) { const err = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error))); @@ -257,7 +257,7 @@ async function createIvm( logger.error( `Error fetching credentials map for transformationID: ${transformationId} and workspaceId: ${workspaceId}`, ); - stats.increment('credential_error_total', { transformationId, workspaceId }); + stats.increment('credential_error_total', trTags); return undefined; } if (key === null || key === undefined) { @@ -416,7 +416,7 @@ async function createIvm( reference: true, }); const fName = availableFuncNames[0]; - stats.timing('createivm_duration', createIvmStartTime); + stats.timing('createivm_duration', createIvmStartTime, trTags); // TODO : check if we can resolve this // eslint-disable-next-line no-async-promise-executor @@ -446,7 +446,6 @@ async function getFactory( libraryVersionIds, transformationId, workspaceId, - versionId, credentials, secrets, testMode, @@ -456,7 +455,6 @@ async function getFactory( return createIvm( code, libraryVersionIds, - versionId, transformationId, workspaceId, credentials, diff --git a/src/util/prometheus.js b/src/util/prometheus.js index ffcfda3784..860c266565 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -239,79 +239,12 @@ class Prometheus { 'implementation', ], }, - { - name: 'tp_violation_type', - help: 'tp_violation_type', - type: 'counter', - labelNames: ['violationType', 'sourceType', 'destinationType', 'k8_namespace'], - }, - { - name: 'tp_propagated_events', - help: 'tp_propagated_events', - type: 'counter', - labelNames: ['sourceType', 'destinationType', 'k8_namespace'], - }, - { - name: 'tp_errors', - help: 'tp_errors', - type: 'counter', - labelNames: [ - 'sourceType', - 'destinationType', - 'k8_namespace', - 'workspaceId', - 'trackingPlanId', - ], - }, - { - name: 'tp_events_count', - help: 'tp_events_count', - type: 'counter', - labelNames: ['sourceType', 'destinationType', 'k8_namespace'], - }, - { - name: 'user_transform_function_group_size', - help: 'user_transform_function_group_size', - type: 'counter', - labelNames: ['processSessions'], - }, - { - name: 'user_transform_errors', - help: 'user_transform_errors', - type: 'counter', - labelNames: [ - 'workspaceId', - 'transformationId', - 'status', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, - { - name: 'c2', - help: 'h2', - type: 'counter', - labelNames: [ - 'transformationVersionId', - 'processSessions', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, { name: 'dest_transform_requests', help: 'dest_transform_requests', type: 'counter', labelNames: ['destination', 'version', 'sourceType', 'destinationType', 'k8_namespace'], }, - { - name: 'user_transform_requests', - help: 'user_transform_requests', - type: 'counter', - labelNames: ['processSessions'], - }, { name: 'source_transform_requests', help: 'source_transform_requests', @@ -342,56 +275,6 @@ class Prometheus { type: 'counter', labelNames: ['success'], }, - { - name: 'create_zip_error', - help: 'create_zip_error', - type: 'counter', - labelNames: ['fileName'], - }, - { - name: 'delete_zip_error', - help: 'delete_zip_error', - type: 'counter', - labelNames: ['functionName'], - }, - { - name: 'hv_metrics', - help: 'hv_metrics', - type: 'counter', - labelNames: [ - 'destination', - 'version', - 'sourceType', - 'destinationType', - 'k8_namespace', - 'dropped', - 'violationType', - ], - }, - { - name: 'events_into_vm', - help: 'events_into_vm', - type: 'counter', - labelNames: [ - 'transformerVersionId', - 'version', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, - { - name: 'missing_handle', - help: 'missing_handle', - type: 'counter', - labelNames: [ - 'transformerVersionId', - 'language', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, { name: 'proxy_test_error', help: 'proxy_test_error', @@ -495,18 +378,6 @@ class Prometheus { type: 'counter', labelNames: ['destinationId'], }, - { - name: 'get_eventSchema_error', - help: 'get_eventSchema_error', - type: 'counter', - labelNames: [], - }, - { - name: 'get_tracking_plan_error', - help: 'get_tracking_plan_error', - type: 'counter', - labelNames: [], - }, { name: 'redis_error', help: 'redis_error', @@ -537,18 +408,6 @@ class Prometheus { type: 'counter', labelNames: ['writeKey', 'source'], }, - { - name: 'get_transformation_code', - help: 'get_transformation_code', - type: 'counter', - labelNames: ['versionId', 'version', 'success'], - }, - { - name: 'get_libraries_code', - help: 'get_libraries_code', - type: 'counter', - labelNames: ['libraryVersionId', 'version', 'type', 'success'], - }, { name: 'invalid_shopify_event', help: 'invalid_shopify_event', @@ -574,12 +433,6 @@ class Prometheus { 'sourceId', ], }, - { - name: 'credential_error_total', - help: 'Error in fetching credentials count', - type: 'counter', - labelNames: ['transformationId', 'workspaceId'], - }, // Gauges { @@ -667,118 +520,18 @@ class Prometheus { type: 'histogram', labelNames: ['method', 'route', 'code', 'destType'], }, - { - name: 'tp_batch_size', - help: 'Size of batch of events for tracking plan validation', - type: 'histogram', - buckets: [ - 1024, 102400, 524288, 1048576, 10485760, 20971520, 52428800, 104857600, 209715200, - 524288000, - ], - labelNames: [ - 'sourceType', - 'destinationType', - 'k8_namespace', - 'workspaceId', - 'trackingPlanId', - ], - }, - { - name: 'tp_event_validation_latency', - help: 'Latency of validating tracking plan at event level', - type: 'histogram', - labelNames: [ - 'sourceType', - 'destinationType', - 'k8_namespace', - 'workspaceId', - 'trackingPlanId', - 'status', - 'exception', - ], - }, - { - name: 'tp_batch_validation_latency', - help: 'Latency of validating tracking plan at batch level', - type: 'histogram', - labelNames: [ - 'sourceType', - 'destinationType', - 'k8_namespace', - 'workspaceId', - 'trackingPlanId', - ], - }, { name: 'cdk_events_latency', help: 'cdk_events_latency', type: 'histogram', labelNames: ['destination', 'sourceType', 'destinationType', 'k8_namespace'], }, - { - name: 'tp_event_latency', - help: 'tp_event_latency', - type: 'histogram', - labelNames: ['sourceType', 'destinationType', 'k8_namespace'], - }, { name: 'regulation_worker_requests_dest_latency', help: 'regulation_worker_requests_dest_latency', type: 'histogram', labelNames: ['feature', 'implementation', 'destType'], }, - { - name: 'user_transform_request_latency', - help: 'user_transform_request_latency', - type: 'histogram', - labelNames: [ - 'workspaceId', - 'transformationId', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, - { - name: 'user_transform_request_latency_summary', - help: 'user_transform_request_latency_summary', - type: 'summary', - labelNames: [ - 'workspaceId', - 'transformationId', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, - { - name: 'user_transform_batch_size', - help: 'user_transform_batch_size', - type: 'histogram', - labelNames: [ - 'workspaceId', - 'transformationId', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - buckets: [ - 1024, 102400, 524288, 1048576, 10485760, 20971520, 52428800, 104857600, 209715200, - 524288000, - ], // 1KB, 100KB, 0.5MB, 1MB, 10MB, 20MB, 50MB, 100MB, 200MB, 500MB - }, - { - name: 'user_transform_batch_size_summary', - help: 'user_transform_batch_size_summary', - type: 'summary', - labelNames: [ - 'workspaceId', - 'transformationId', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, { name: 'source_transform_request_latency', help: 'source_transform_request_latency', @@ -797,88 +550,6 @@ class Prometheus { type: 'histogram', labelNames: ['destination', 'version'], }, - { - name: 'creation_time', - help: 'creation_time', - type: 'histogram', - labelNames: ['transformerVersionId', 'language', 'identifier', 'publish', 'testMode'], - }, - { name: 'get_tracking_plan', help: 'get_tracking_plan', type: 'histogram', labelNames: [] }, - { name: 'createivm_duration', help: 'createivm_duration', type: 'histogram', labelNames: [] }, - { - name: 'fetchV2_call_duration', - help: 'fetchV2_call_duration', - type: 'histogram', - labelNames: ['versionId'], - }, - { - name: 'fetch_call_duration', - help: 'fetch_call_duration', - type: 'histogram', - labelNames: ['versionId'], - }, - { - name: 'fetch_dns_resolve_time', - help: 'fetch_dns_resolve_time', - type: 'histogram', - labelNames: ['transformerVersionId', 'error'], - }, - { - name: 'geo_call_duration', - help: 'geo_call_duration', - type: 'histogram', - labelNames: ['versionId'], - }, - { - name: 'get_transformation_code_time', - help: 'get_transformation_code_time', - type: 'histogram', - labelNames: ['versionId', 'version'], - }, - { - name: 'get_transformation_code_time_summary', - help: 'get_transformation_code_time_summary', - type: 'summary', - labelNames: ['versionId', 'version'], - }, - { - name: 'get_libraries_code_time', - help: 'get_libraries_code_time', - type: 'histogram', - labelNames: ['libraryVersionId', 'versionId', 'type', 'version'], - }, - { - name: 'get_libraries_code_time_summary', - help: 'get_libraries_code_time_summary', - type: 'summary', - labelNames: ['libraryVersionId', 'versionId', 'type', 'version'], - }, - { - name: 'isolate_cpu_time', - help: 'isolate_cpu_time', - type: 'histogram', - labelNames: [ - 'transformerVersionId', - 'identifier', - 'version', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, - { - name: 'isolate_wall_time', - help: 'isolate_wall_time', - type: 'histogram', - labelNames: [ - 'transformerVersionId', - 'identifier', - 'version', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, { name: 'marketo_bulk_upload_process_time', help: 'marketo_bulk_upload_process_time', @@ -1004,20 +675,6 @@ class Prometheus { labelNames: ['destination', 'version', 'sourceType', 'destinationType', 'k8_namespace'], buckets: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 150, 200], }, - { - name: 'user_transform_input_events', - help: 'Number of input events to user transform', - type: 'histogram', - labelNames: ['processSessions'], - buckets: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 150, 200], - }, - { - name: 'user_transform_output_events', - help: 'user_transform_output_events', - type: 'histogram', - labelNames: ['processSessions'], - buckets: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 150, 200], - }, { name: 'marketo_bulk_upload_create_header_time', help: 'marketo_bulk_upload_create_header_time', @@ -1054,6 +711,117 @@ class Prometheus { type: 'histogram', labelNames: [], }, + // tracking plan metrics: + // counter + { + name: 'hv_metrics', + help: 'hv_metrics', + type: 'counter', + labelNames: [ + 'destination', + 'version', + 'sourceType', + 'destinationType', + 'k8_namespace', + 'dropped', + 'violationType', + ], + }, + { + name: 'get_eventSchema_error', + help: 'get_eventSchema_error', + type: 'counter', + labelNames: [], + }, + { + name: 'get_tracking_plan_error', + help: 'get_tracking_plan_error', + type: 'counter', + labelNames: [], + }, + // histogram + { + name: 'tp_batch_size', + help: 'Size of batch of events for tracking plan validation', + type: 'histogram', + buckets: [ + 1024, 102400, 524288, 1048576, 10485760, 20971520, 52428800, 104857600, 209715200, + 524288000, + ], + labelNames: [ + 'sourceType', + 'destinationType', + 'k8_namespace', + 'workspaceId', + 'trackingPlanId', + ], + }, + { + name: 'tp_event_validation_latency', + help: 'Latency of validating tracking plan at event level', + type: 'histogram', + labelNames: [ + 'sourceType', + 'destinationType', + 'k8_namespace', + 'workspaceId', + 'trackingPlanId', + 'status', + 'exception', + ], + }, + { + name: 'tp_batch_validation_latency', + help: 'Latency of validating tracking plan at batch level', + type: 'histogram', + labelNames: [ + 'sourceType', + 'destinationType', + 'k8_namespace', + 'workspaceId', + 'trackingPlanId', + ], + }, + { + name: 'tp_event_latency', + help: 'tp_event_latency', + type: 'histogram', + labelNames: ['sourceType', 'destinationType', 'k8_namespace'], + }, + { name: 'get_tracking_plan', help: 'get_tracking_plan', type: 'histogram', labelNames: [] }, + // User transform metrics + // counter + { + name: 'user_transform_function_group_size', + help: 'user_transform_function_group_size', + type: 'counter', + labelNames: [], + }, + { + name: 'user_transform_errors', + help: 'user_transform_errors', + type: 'counter', + labelNames: [ + 'workspaceId', + 'transformationId', + 'status', + 'sourceType', + 'destinationType', + 'k8_namespace', + ], + }, + { + name: 'user_transform_requests', + help: 'user_transform_requests', + type: 'counter', + labelNames: [], + }, + { + name: 'credential_error_total', + help: 'Error in fetching credentials count', + type: 'counter', + labelNames: ['identifier', 'transformationId', 'workspaceId'], + }, { name: 'user_transform_function_input_events', help: 'user_transform_function_input_events', @@ -1070,6 +838,85 @@ class Prometheus { 'workspaceId', ], }, + // histogram + { + name: 'user_transform_request_latency', + help: 'user_transform_request_latency', + type: 'histogram', + labelNames: [ + 'workspaceId', + 'transformationId', + 'sourceType', + 'destinationType', + 'k8_namespace', + ], + }, + { + name: 'user_transform_batch_size', + help: 'user_transform_batch_size', + type: 'histogram', + labelNames: [ + 'workspaceId', + 'transformationId', + 'sourceType', + 'destinationType', + 'k8_namespace', + ], + buckets: [ + 1024, 102400, 524288, 1048576, 10485760, 20971520, 52428800, 104857600, 209715200, + 524288000, + ], // 1KB, 100KB, 0.5MB, 1MB, 10MB, 20MB, 50MB, 100MB, 200MB, 500MB + }, + { + name: 'creation_time', + help: 'creation_time', + type: 'histogram', + labelNames: ['transformationId', 'identifier', 'testMode'], + }, + { + name: 'createivm_duration', + help: 'createivm_duration', + type: 'histogram', + labelNames: ['identifier', 'transformationId', 'workspaceId'], + }, + { + name: 'fetchV2_call_duration', + help: 'fetchV2_call_duration', + type: 'histogram', + labelNames: ['identifier', 'transformationId', 'workspaceId'], + }, + { + name: 'fetch_call_duration', + help: 'fetch_call_duration', + type: 'histogram', + labelNames: ['identifier', 'transformationId', 'workspaceId'], + }, + { + name: 'fetch_dns_resolve_time', + help: 'fetch_dns_resolve_time', + type: 'histogram', + labelNames: ['identifier', 'transformationId', 'workspaceId', 'error'], + }, + { + name: 'geo_call_duration', + help: 'geo_call_duration', + type: 'histogram', + labelNames: ['identifier', 'transformationId', 'workspaceId'], + }, + { + name: 'user_transform_input_events', + help: 'Number of input events to user transform', + type: 'histogram', + labelNames: [], + buckets: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 150, 200], + }, + { + name: 'user_transform_output_events', + help: 'user_transform_output_events', + type: 'histogram', + labelNames: [], + buckets: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 150, 200], + }, { name: 'user_transform_function_latency', help: 'user_transform_function_latency', @@ -1086,6 +933,31 @@ class Prometheus { 'workspaceId', ], }, + // summary + { + name: 'user_transform_request_latency_summary', + help: 'user_transform_request_latency_summary', + type: 'summary', + labelNames: [ + 'workspaceId', + 'transformationId', + 'sourceType', + 'destinationType', + 'k8_namespace', + ], + }, + { + name: 'user_transform_batch_size_summary', + help: 'user_transform_batch_size_summary', + type: 'summary', + labelNames: [ + 'workspaceId', + 'transformationId', + 'sourceType', + 'destinationType', + 'k8_namespace', + ], + }, { name: 'user_transform_function_latency_summary', help: 'user_transform_function_latency_summary', diff --git a/src/util/utils.js b/src/util/utils.js index 1ac70b9541..eb5a011444 100644 --- a/src/util/utils.js +++ b/src/util/utils.js @@ -16,7 +16,7 @@ const LOCAL_HOST_NAMES_LIST = ['localhost', '127.0.0.1', '[::]', '[::1]']; const LOCALHOST_OCTET = '127.'; const RECORD_TYPE_A = 4; // ipv4 -const staticLookup = (transformerVersionId) => async (hostname, _, cb) => { +const staticLookup = (transformationTags) => async (hostname, _, cb) => { let ips; const resolveStartTime = new Date(); try { @@ -24,13 +24,13 @@ const staticLookup = (transformerVersionId) => async (hostname, _, cb) => { } catch (error) { logger.error(`DNS Error Code: ${error.code} | Message : ${error.message}`); stats.timing('fetch_dns_resolve_time', resolveStartTime, { - transformerVersionId, + ...transformationTags, error: 'true', }); cb(null, `unable to resolve IP address for ${hostname}`, RECORD_TYPE_A); return; } - stats.timing('fetch_dns_resolve_time', resolveStartTime, { transformerVersionId }); + stats.timing('fetch_dns_resolve_time', resolveStartTime, transformationTags); if (ips.length === 0) { cb(null, `resolved empty list of IP address for ${hostname}`, RECORD_TYPE_A); @@ -48,9 +48,9 @@ const staticLookup = (transformerVersionId) => async (hostname, _, cb) => { cb(null, ips[0], RECORD_TYPE_A); }; -const httpAgentWithDnsLookup = (scheme, transformerVersionId) => { +const httpAgentWithDnsLookup = (scheme, transformationTags) => { const httpModule = scheme === 'http' ? http : https; - return new httpModule.Agent({ lookup: staticLookup(transformerVersionId) }); + return new httpModule.Agent({ lookup: staticLookup(transformationTags) }); }; const blockLocalhostRequests = (url) => { @@ -74,7 +74,7 @@ const blockInvalidProtocolRequests = (url) => { } }; -const fetchWithDnsWrapper = async (transformerVersionId, ...args) => { +const fetchWithDnsWrapper = async (transformationTags, ...args) => { if (process.env.DNS_RESOLVE_FETCH_HOST !== 'true') { return await fetch(...args); } @@ -88,7 +88,7 @@ const fetchWithDnsWrapper = async (transformerVersionId, ...args) => { const fetchOptions = args[1] || {}; const schemeName = fetchURL.startsWith('https') ? 'https' : 'http'; // assign resolved agent to fetch - fetchOptions.agent = httpAgentWithDnsLookup(schemeName, transformerVersionId); + fetchOptions.agent = httpAgentWithDnsLookup(schemeName, transformationTags); return await fetch(fetchURL, fetchOptions); }; From dc83798f1e02b1116ca4704a37c96b18db253e99 Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Wed, 26 Jun 2024 10:14:14 +0530 Subject: [PATCH 240/240] chore: resolve bugsnag issue in braze (#3474) * chore: resolve bugsnag issue in braze * chore: resolve bugsnag issue in braze * chore: added tests * chore: added tests * fix: instead of throwing error now ignoring the undefined value in the products array --- src/v0/destinations/braze/util.js | 4 +- .../destinations/braze/processor/data.ts | 214 ++++++++++++++++++ 2 files changed, 217 insertions(+), 1 deletion(-) diff --git a/src/v0/destinations/braze/util.js b/src/v0/destinations/braze/util.js index b3b29cdf96..45063d0ba2 100644 --- a/src/v0/destinations/braze/util.js +++ b/src/v0/destinations/braze/util.js @@ -570,7 +570,8 @@ function getPurchaseObjs(message, config) { 'Invalid Order Completed event: Properties object is missing in the message', ); } - const { products, currency: currencyCode } = properties; + const { currency: currencyCode } = properties; + let { products } = properties; if (!products) { throw new InstrumentationError( 'Invalid Order Completed event: Products array is missing in the message', @@ -581,6 +582,7 @@ function getPurchaseObjs(message, config) { throw new InstrumentationError('Invalid Order Completed event: Products is not an array'); } + products = products.filter((product) => isDefinedAndNotNull(product)); if (products.length === 0) { throw new InstrumentationError('Invalid Order Completed event: Products array is empty'); } diff --git a/test/integrations/destinations/braze/processor/data.ts b/test/integrations/destinations/braze/processor/data.ts index dc5a84470f..644bebace1 100644 --- a/test/integrations/destinations/braze/processor/data.ts +++ b/test/integrations/destinations/braze/processor/data.ts @@ -4111,4 +4111,218 @@ export const data = [ }, }, }, + { + name: 'braze', + description: 'Test 30', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-09-14T12:09:37.491Z', + userId: 'Randomuser2222', + channel: 'web', + context: { + os: { + name: '', + version: '', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.3', + namespace: 'com.rudderlabs.javascript', + }, + page: { + url: 'file:///Users/manashi/Desktop/rudder-all-sdk-application-testing/Fullstory%20test%20By%20JS%20SDK/braze.html', + path: '/Users/manashi/Desktop/rudder-all-sdk-application-testing/Fullstory%20test%20By%20JS%20SDK/braze.html', + title: 'Fullstory Test', + search: '', + referrer: '', + }, + locale: 'en-GB', + screen: { + density: 2, + }, + traits: { + email: 'manashi@gmaiol.com', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.1.3', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36', + }, + messageId: '24ecc509-ce3e-473c-8483-ba1ea2c195cb', + properties: { + products: [ + null, + { + sku: '45790-32', + url: 'https://www.example.com/product/path', + key1: { + key11: 'value1', + key22: 'value2', + }, + name: 'Monopoly: 3rd Edition', + price: 19, + category: 'Games', + quantity: 1, + image_url: 'https:///www.example.com/product/path.jpg', + currency78: 'USD', + product_id: '507f1f77bcf86cd799439011', + }, + ], + }, + anonymousId: 'c6ff1462-b692-43d6-8f6a-659efedc99ea', + integrations: { + All: true, + }, + originalTimestamp: '2020-09-14T12:09:37.491Z', + }, + destination: { + Config: { + restApiKey: 'dummyApiKey', + prefixProperties: true, + useNativeSDK: false, + }, + DestinationDefinition: { + DisplayName: 'Braze', + ID: '1WhbSZ6uA3H5ChVifHpfL2H6sie', + Name: 'BRAZE', + }, + Enabled: true, + ID: '1WhcOCGgj9asZu850HvugU2C3Aq', + Name: 'Braze', + Transformations: [], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'Invalid Order Completed event: Message properties and product at index: 0 is missing currency', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'BRAZE', + module: 'destination', + implementation: 'native', + feature: 'processor', + }, + }, + ], + }, + }, + }, + { + name: 'braze', + description: 'Test 31', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-09-14T12:09:37.491Z', + userId: 'Randomuser2222', + channel: 'web', + context: { + os: { + name: '', + version: '', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.3', + namespace: 'com.rudderlabs.javascript', + }, + page: { + url: 'file:///Users/manashi/Desktop/rudder-all-sdk-application-testing/Fullstory%20test%20By%20JS%20SDK/braze.html', + path: '/Users/manashi/Desktop/rudder-all-sdk-application-testing/Fullstory%20test%20By%20JS%20SDK/braze.html', + title: 'Fullstory Test', + search: '', + referrer: '', + }, + locale: 'en-GB', + screen: { + density: 2, + }, + traits: { + email: 'manashi@gmaiol.com', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.1.3', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36', + }, + messageId: '24ecc509-ce3e-473c-8483-ba1ea2c195cb', + properties: { + products: [undefined], + }, + anonymousId: 'c6ff1462-b692-43d6-8f6a-659efedc99ea', + integrations: { + All: true, + }, + originalTimestamp: '2020-09-14T12:09:37.491Z', + }, + destination: { + Config: { + restApiKey: 'dummyApiKey', + prefixProperties: true, + useNativeSDK: false, + }, + DestinationDefinition: { + DisplayName: 'Braze', + ID: '1WhbSZ6uA3H5ChVifHpfL2H6sie', + Name: 'BRAZE', + }, + Enabled: true, + ID: '1WhcOCGgj9asZu850HvugU2C3Aq', + Name: 'Braze', + Transformations: [], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'Invalid Order Completed event: Products array is empty', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'BRAZE', + module: 'destination', + implementation: 'native', + feature: 'processor', + }, + }, + ], + }, + }, + }, ];