diff --git a/CHANGELOG.md b/CHANGELOG.md index 854f45b096..99875dc1b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ 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.80.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.79.1...v1.80.0) (2024-09-30) + + +### Features + +* add unity source support in Singular ([#3634](https://github.com/rudderlabs/rudder-transformer/issues/3634)) ([12996d7](https://github.com/rudderlabs/rudder-transformer/commit/12996d7a7ce23de7c150c1c1e012d4dda8668977)) +* onboard shopify to v1 ([#3665](https://github.com/rudderlabs/rudder-transformer/issues/3665)) ([d40e772](https://github.com/rudderlabs/rudder-transformer/commit/d40e772f1a3741c1c4e9ab2365ed464b3988812e)) + + +### Bug Fixes + +* add correct validation for purchase events ([#3766](https://github.com/rudderlabs/rudder-transformer/issues/3766)) ([9cc72f2](https://github.com/rudderlabs/rudder-transformer/commit/9cc72f2288f99ee394977ffeb209faaae657f6d2)) +* braze include fields_to_export to lookup users ([#3761](https://github.com/rudderlabs/rudder-transformer/issues/3761)) ([173b989](https://github.com/rudderlabs/rudder-transformer/commit/173b9895fb2a0bed615f6e3a9c670abe42d5754f)) +* correct typo for order fulfillment event, add test ([#3764](https://github.com/rudderlabs/rudder-transformer/issues/3764)) ([6f92bd3](https://github.com/rudderlabs/rudder-transformer/commit/6f92bd31b60caaa07d18bb86ce5939cd7cc9a416)) +* fixing lytics user_id and anonymousId mapping ([#3745](https://github.com/rudderlabs/rudder-transformer/issues/3745)) ([45b1067](https://github.com/rudderlabs/rudder-transformer/commit/45b1067d81f3883e19d35634ffec52434fef452f)) +* payment info entered event in facebook_conversions ([#3762](https://github.com/rudderlabs/rudder-transformer/issues/3762)) ([7fa7c8d](https://github.com/rudderlabs/rudder-transformer/commit/7fa7c8d3a4f6aefb580cf0de2e64e2f8aef5b5ce)) +* posthog alias mapping swap ([#3765](https://github.com/rudderlabs/rudder-transformer/issues/3765)) ([b6240d0](https://github.com/rudderlabs/rudder-transformer/commit/b6240d06a9d1f7f3bc8f245807f72a72ab40f170)), closes [#3507](https://github.com/rudderlabs/rudder-transformer/issues/3507) + ### [1.79.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.79.0...v1.79.1) (2024-09-24) diff --git a/package-lock.json b/package-lock.json index ae018a5ec9..13425b9682 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.79.1", + "version": "1.80.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.79.1", + "version": "1.80.0", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index ede36441eb..85e5a7152c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.79.1", + "version": "1.80.0", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { diff --git a/src/cdk/v2/destinations/bluecore/utils.js b/src/cdk/v2/destinations/bluecore/utils.js index 91eda60d0d..543b6de745 100644 --- a/src/cdk/v2/destinations/bluecore/utils.js +++ b/src/cdk/v2/destinations/bluecore/utils.js @@ -46,12 +46,12 @@ const verifyPayload = (payload, message) => { } break; case 'purchase': - if (!payload?.properties?.order_id) { + if (!isDefinedAndNotNull(payload?.properties?.order_id)) { throw new InstrumentationError( '[Bluecore] property:: order_id is required for purchase event', ); } - if (!payload?.properties?.total) { + if (!isDefinedAndNotNull(payload?.properties?.total)) { throw new InstrumentationError( '[Bluecore] property:: total is required for purchase event', ); diff --git a/src/cdk/v2/destinations/lytics/config.ts b/src/cdk/v2/destinations/lytics/config.ts new file mode 100644 index 0000000000..6756834caa --- /dev/null +++ b/src/cdk/v2/destinations/lytics/config.ts @@ -0,0 +1,9 @@ +import { getMappingConfig } from '../../../../v0/util'; + +const CONFIG_CATEGORIES = { + CUSTOMER_PROPERTIES_CONFIG: { name: 'LYTICSIdentifyConfig' }, +}; + +const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); +export const CUSTOMER_PROPERTIES_CONFIG = + MAPPING_CONFIG[CONFIG_CATEGORIES.CUSTOMER_PROPERTIES_CONFIG.name]; diff --git a/src/cdk/v2/destinations/lytics/data/LYTICSIdentifyConfig.json b/src/cdk/v2/destinations/lytics/data/LYTICSIdentifyConfig.json new file mode 100644 index 0000000000..c1996d34f7 --- /dev/null +++ b/src/cdk/v2/destinations/lytics/data/LYTICSIdentifyConfig.json @@ -0,0 +1,25 @@ +[ + { + "destKey": "user_id", + "sourceKeys": "userIdOnly", + "sourceFromGenericMap": true, + "required": false + }, + { + "destKey": "anonymous_id", + "sourceKeys": "anonymousId", + "required": false + }, + { + "destKey": "first_name", + "sourceKeys": "firstName", + "sourceFromGenericMap": true, + "required": false + }, + { + "destKey": "last_name", + "sourceKeys": "lastName", + "sourceFromGenericMap": true, + "required": false + } +] diff --git a/src/cdk/v2/destinations/lytics/procWorkflow.yaml b/src/cdk/v2/destinations/lytics/procWorkflow.yaml index 2622146221..834493e79c 100644 --- a/src/cdk/v2/destinations/lytics/procWorkflow.yaml +++ b/src/cdk/v2/destinations/lytics/procWorkflow.yaml @@ -5,9 +5,12 @@ bindings: path: ../../../../v0/util - name: removeUndefinedAndNullValues path: ../../../../v0/util + - name: constructPayload + path: ../../../../v0/util - path: ../../bindings/jsontemplate - name: defaultRequestConfig path: ../../../../v0/util + - path: ./config steps: - name: validateInput @@ -24,20 +27,19 @@ steps: condition: $.context.messageType === {{$.EventType.IDENTIFY}} template: | const flattenTraits = $.flattenJson(.message.traits ?? .message.context.traits); - $.context.payload = .message.({ + const payload = $.constructPayload(.message, $.CUSTOMER_PROPERTIES_CONFIG); + $.context.payload = { ...flattenTraits, - first_name: {{{{$.getGenericPaths("firstName")}}}}, - last_name: {{{{$.getGenericPaths("lastName")}}}}, - user_id: {{{{$.getGenericPaths("userId")}}}} - }) + ...payload, + } else: name: payloadForOthers template: | const flattenProperties = $.flattenJson(.message.properties); + const customerPropertiesInfo = $.constructPayload(.message, $.CUSTOMER_PROPERTIES_CONFIG); $.context.payload = .message.({ ...flattenProperties, - first_name: .properties.firstName ?? .properties.firstname, - last_name: .properties.lastName ?? .properties.lastname + ...customerPropertiesInfo }) - name: trackPayload condition: $.context.messageType === {{$.EventType.TRACK}} diff --git a/src/v0/destinations/braze/braze.util.test.js b/src/v0/destinations/braze/braze.util.test.js index 5ec48d29f1..71052f8d77 100644 --- a/src/v0/destinations/braze/braze.util.test.js +++ b/src/v0/destinations/braze/braze.util.test.js @@ -304,6 +304,20 @@ describe('dedup utility tests', () => { { external_ids: ['user1', 'user2'], user_aliases: [{ alias_name: 'user3', alias_label: 'rudder_id' }], + fields_to_export: [ + 'created_at', + 'custom_attributes', + 'dob', + 'email', + 'first_name', + 'gender', + 'home_city', + 'last_name', + 'phone', + 'time_zone', + 'external_id', + 'user_aliases', + ], }, { headers: { diff --git a/src/v0/destinations/braze/util.js b/src/v0/destinations/braze/util.js index e5df75b562..6c8cf64265 100644 --- a/src/v0/destinations/braze/util.js +++ b/src/v0/destinations/braze/util.js @@ -141,19 +141,36 @@ const BrazeDedupUtility = { const identfierChunks = _.chunk(identifiers, 50); return identfierChunks; }, - + getFieldsToExport() { + return [ + 'created_at', + 'custom_attributes', + 'dob', + 'email', + 'first_name', + 'gender', + 'home_city', + 'last_name', + 'phone', + 'time_zone', + 'external_id', + 'user_aliases', + // 'country' and 'language' not needed because it is not billable so we don't use it + ]; + }, async doApiLookup(identfierChunks, { destination, metadata }) { return Promise.all( identfierChunks.map(async (ids) => { const externalIdentifiers = ids.filter((id) => id.external_id); const aliasIdentifiers = ids.filter((id) => id.alias_name !== undefined); - + const fieldsToExport = this.getFieldsToExport(); const { processedResponse: lookUpResponse } = await handleHttpRequest( 'post', `${getEndpointFromConfig(destination)}/users/export/ids`, { external_ids: externalIdentifiers.map((extId) => extId.external_id), user_aliases: aliasIdentifiers, + fields_to_export: fieldsToExport, }, { headers: { diff --git a/src/v0/destinations/facebook_conversions/utils.js b/src/v0/destinations/facebook_conversions/utils.js index 87fb0ea606..9119bfdca5 100644 --- a/src/v0/destinations/facebook_conversions/utils.js +++ b/src/v0/destinations/facebook_conversions/utils.js @@ -79,16 +79,31 @@ const validateProductSearchedData = (eventTypeCustomData) => { } }; +const getProducts = (message, category) => { + let products = message.properties?.products; + if (['product added', 'product viewed', 'products searched'].includes(category.type)) { + return [message.properties]; + } + if ( + ['payment info entered', 'product added to wishlist'].includes(category.type) && + !Array.isArray(products) + ) { + products = [message.properties]; + } + return products; +}; + const populateCustomDataBasedOnCategory = (customData, message, category, categoryToContent) => { let eventTypeCustomData = {}; if (category.name) { eventTypeCustomData = constructPayload(message, MAPPING_CONFIG[category.name]); } + const products = getProducts(message, category); switch (category.type) { case 'product list viewed': { const { contentIds, contents } = populateContentsAndContentIDs( - message.properties?.products, + products, message.properties?.quantity, ); @@ -119,9 +134,7 @@ const populateCustomDataBasedOnCategory = (customData, message, category, catego } case 'product added': case 'product viewed': - case 'products searched': - case 'payment info entered': - case 'product added to wishlist': { + case 'products searched': { const contentCategory = eventTypeCustomData.content_category; const contentType = message.properties?.content_type || @@ -131,7 +144,7 @@ const populateCustomDataBasedOnCategory = (customData, message, category, catego categoryToContent, DESTINATION.toLowerCase(), ); - const { contentIds, contents } = populateContentsAndContentIDs([message.properties]); + const { contentIds, contents } = populateContentsAndContentIDs(products); eventTypeCustomData = { ...eventTypeCustomData, content_ids: contentIds.length === 1 ? contentIds[0] : contentIds, @@ -142,10 +155,12 @@ const populateCustomDataBasedOnCategory = (customData, message, category, catego validateProductSearchedData(eventTypeCustomData); break; } + case 'payment info entered': + case 'product added to wishlist': case 'order completed': case 'checkout started': { const { contentIds, contents } = populateContentsAndContentIDs( - message.properties?.products, + products, message.properties?.quantity, message.properties?.delivery_category, ); diff --git a/src/v0/destinations/lytics/config.js b/src/v0/destinations/lytics/config.js deleted file mode 100644 index c7843eda46..0000000000 --- a/src/v0/destinations/lytics/config.js +++ /dev/null @@ -1,27 +0,0 @@ -const { getMappingConfig } = require('../../util'); - -const ENDPOINT = 'https://api.lytics.io/collect/json'; -const CONFIG_CATEGORIES = { - IDENTIFY: { - name: 'LYTICSIdentifyConfig', - }, - PAGESCREEN: { - name: 'LYTICSPageScreenConfig', - }, - TRACK: { - name: 'LYTICSTrackConfig', - }, -}; - -const forFirstName = ['firstname', 'firstName']; -const forLastName = ['lastname', 'lastName']; - -const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); - -module.exports = { - ENDPOINT, - MAPPING_CONFIG, - CONFIG_CATEGORIES, - forFirstName, - forLastName, -}; diff --git a/src/v0/destinations/lytics/data/LYTICSIdentifyConfig.json b/src/v0/destinations/lytics/data/LYTICSIdentifyConfig.json deleted file mode 100644 index d9765490e3..0000000000 --- a/src/v0/destinations/lytics/data/LYTICSIdentifyConfig.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "destKey": "user_id", - "sourceKeys": [ - "userId", - "traits.userId", - "traits.id", - "context.traits.userId", - "context.traits.id", - "anonymousId" - ], - "required": false - }, - { - "destKey": "", - "sourceKeys": ["traits", "context.traits"], - "required": false - } -] diff --git a/src/v0/destinations/lytics/data/LYTICSPageScreenConfig.json b/src/v0/destinations/lytics/data/LYTICSPageScreenConfig.json deleted file mode 100644 index f925dbc5bd..0000000000 --- a/src/v0/destinations/lytics/data/LYTICSPageScreenConfig.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - { - "destKey": "event", - "sourceKeys": "name", - "required": false - }, - { - "destKey": "", - "sourceKeys": "properties", - "required": false - } -] diff --git a/src/v0/destinations/lytics/data/LYTICSTrackConfig.json b/src/v0/destinations/lytics/data/LYTICSTrackConfig.json deleted file mode 100644 index 5de09eb10b..0000000000 --- a/src/v0/destinations/lytics/data/LYTICSTrackConfig.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - { - "destKey": "_e", - "sourceKeys": "event", - "required": false - }, - { - "destKey": "", - "sourceKeys": "properties", - "required": false - } -] diff --git a/src/v0/destinations/lytics/transform.js b/src/v0/destinations/lytics/transform.js deleted file mode 100644 index c3a971adba..0000000000 --- a/src/v0/destinations/lytics/transform.js +++ /dev/null @@ -1,77 +0,0 @@ -const { InstrumentationError } = require('@rudderstack/integrations-lib'); -const { EventType } = require('../../../constants'); -const { - CONFIG_CATEGORIES, - MAPPING_CONFIG, - ENDPOINT, - forFirstName, - forLastName, -} = require('./config'); -const { - constructPayload, - defaultPostRequestConfig, - removeUndefinedAndNullValues, - defaultRequestConfig, - flattenJson, - simpleProcessRouterDest, -} = require('../../util'); -const { JSON_MIME_TYPE } = require('../../util/constant'); - -const responseBuilderSimple = (message, category, destination) => { - const payload = constructPayload(message, MAPPING_CONFIG[category.name]); - const response = defaultRequestConfig(); - const { stream, apiKey } = destination.Config; - response.endpoint = `${ENDPOINT}/${stream}?access_token=${apiKey}`; - response.method = defaultPostRequestConfig.requestMethod; - const flattenedPayload = removeUndefinedAndNullValues(flattenJson(payload)); - forFirstName.forEach((key) => { - if (flattenedPayload[key]) { - flattenedPayload.first_name = flattenedPayload[key]; - delete flattenedPayload[key]; - } - }); - forLastName.forEach((key) => { - if (flattenedPayload[key]) { - flattenedPayload.last_name = flattenedPayload[key]; - delete flattenedPayload[key]; - } - }); - response.body.JSON = flattenedPayload; - response.headers = { - 'Content-Type': JSON_MIME_TYPE, - }; - return response; -}; - -const processEvent = (message, destination) => { - if (!message.type) { - throw new InstrumentationError('Event type is required'); - } - const messageType = message.type; - let category; - switch (messageType.toLowerCase()) { - case EventType.IDENTIFY: - category = CONFIG_CATEGORIES.IDENTIFY; - break; - case EventType.PAGE: - case EventType.SCREEN: - category = CONFIG_CATEGORIES.PAGESCREEN; - break; - case EventType.TRACK: - category = CONFIG_CATEGORIES.TRACK; - break; - default: - throw new InstrumentationError(`Event type ${messageType} is not supported`); - } - // build the response - return responseBuilderSimple(message, category, destination); -}; - -const process = (event) => processEvent(event.message, event.destination); - -const processRouterDest = async (inputs, reqMetadata) => { - const respList = await simpleProcessRouterDest(inputs, process, reqMetadata); - return respList; -}; - -module.exports = { process, processRouterDest }; diff --git a/src/v0/destinations/posthog/data/PHAliasConfig.json b/src/v0/destinations/posthog/data/PHAliasConfig.json index 1992349e22..26fb61ea78 100644 --- a/src/v0/destinations/posthog/data/PHAliasConfig.json +++ b/src/v0/destinations/posthog/data/PHAliasConfig.json @@ -1,12 +1,12 @@ [ { - "destKey": "properties.alias", + "destKey": "properties.distinct_id", "sourceKeys": "userId", "sourceFromGenericMap": true, "required": true }, { - "destKey": "properties.distinct_id", + "destKey": "properties.alias", "sourceKeys": "previousId", "required": true }, diff --git a/src/v0/destinations/singular/config.js b/src/v0/destinations/singular/config.js index d3fd284963..97824b809b 100644 --- a/src/v0/destinations/singular/config.js +++ b/src/v0/destinations/singular/config.js @@ -13,6 +13,10 @@ const CONFIG_CATEGORIES = { name: 'SINGULARIosSessionConfig', type: 'track', }, + SESSION_UNITY: { + name: 'SINGULARUnitySessionConfig', + type: 'track', + }, EVENT_ANDROID: { name: 'SINGULARAndroidEventConfig', type: 'track', @@ -21,6 +25,10 @@ const CONFIG_CATEGORIES = { name: 'SINGULARIosEventConfig', type: 'track', }, + EVENT_UNITY: { + name: 'SINGULARUnityEventConfig', + type: 'track', + }, PRODUCT_PROPERTY: { name: 'SINGULAREventProductConfig', }, @@ -29,8 +37,15 @@ const CONFIG_CATEGORIES = { const SUPPORTED_PLATFORM = { android: 'ANDROID', ios: 'IOS', + pc: 'unity', + xbox: 'unity', + playstation: 'unity', + nintendo: 'unity', + metaquest: 'unity', }; +const SUPPORTED_UNTIY_SUBPLATFORMS = ['pc', 'xbox', 'playstation', 'nintendo', 'metaquest']; + const SINGULAR_SESSION_ANDROID_EXCLUSION = [ 'referring_application', 'asid', @@ -93,5 +108,6 @@ module.exports = { SINGULAR_EVENT_ANDROID_EXCLUSION, SINGULAR_EVENT_IOS_EXCLUSION, SUPPORTED_PLATFORM, + SUPPORTED_UNTIY_SUBPLATFORMS, BASE_URL, }; diff --git a/src/v0/destinations/singular/data/SINGULARUnityEventConfig.json b/src/v0/destinations/singular/data/SINGULARUnityEventConfig.json new file mode 100644 index 0000000000..97cdfda229 --- /dev/null +++ b/src/v0/destinations/singular/data/SINGULARUnityEventConfig.json @@ -0,0 +1,112 @@ +[ + { + "destKey": "p", + "sourceKeys": "context.os.name", + "required": true + }, + { + "destKey": "i", + "sourceKeys": "context.app.namespace", + "required": true + }, + { + "destKey": "sdid", + "sourceKeys": "context.device.id", + "required": false + }, + { + "destKey": "is_revenue_event", + "sourceKeys": "properties.is_revenue_event", + "required": false + }, + { + "destKey": "n", + "sourceKeys": "event", + "required": true + }, + { + "destKey": "av", + "sourceKeys": "context.app.version", + "required": false + }, + { + "destKey": "ve", + "sourceKeys": "context.os.version", + "required": false + }, + { + "destKey": "os", + "sourceKeys": "properties.os", + "required": true + }, + { + "destKey": "ip", + "sourceKeys": ["context.ip", "request_ip"], + "required": true + }, + { + "destKey": "use_ip", + "sourceKeys": "properties.use_ip", + "required": false + }, + { + "destKey": "install_source", + "sourceKeys": "properties.install_source", + "required": true + }, + { + "destKey": "data_sharing_options", + "sourceKeys": "properties.data_sharing_options", + "required": false + }, + { + "destKey": "amt", + "sourceKeys": [ + "properties.total", + "properties.value", + "properties.revenue", + { + "operation": "multiplication", + "args": [ + { + "sourceKeys": "properties.price" + }, + { + "sourceKeys": "properties.quantity", + "default": 1 + } + ] + } + ], + "required": false + }, + { + "destKey": "cur", + "sourceKeys": "properties.currency", + "required": false + }, + { + "destKey": "ua", + "sourceKeys": "context.userAgent", + "required": false + }, + { + "destKey": "utime", + "sourceKeys": "timestamp", + "sourceFromGenericMap": true, + "required": false, + "metadata": { + "type": "secondTimestamp" + } + }, + { + "destKey": "custom_user_id", + "sourceKeys": "properties.custom_user_id", + "required": false + }, + { + "destKey": "install", + "sourceKeys": "properties.install", + "required": false + } +] diff --git a/src/v0/destinations/singular/data/SINGULARUnitySessionConfig.json b/src/v0/destinations/singular/data/SINGULARUnitySessionConfig.json new file mode 100644 index 0000000000..aca561bc59 --- /dev/null +++ b/src/v0/destinations/singular/data/SINGULARUnitySessionConfig.json @@ -0,0 +1,76 @@ +[ + { + "destKey": "p", + "sourceKeys": "context.os.name", + "required": true + }, + { + "destKey": "i", + "sourceKeys": "context.app.namespace", + "required": true + }, + { + "destKey": "sdid", + "sourceKeys": "context.device.id", + "required": false + }, + { + "destKey": "av", + "sourceKeys": "context.app.version", + "required": false + }, + { + "destKey": "ve", + "sourceKeys": "context.os.version", + "required": false + }, + { + "destKey": "os", + "sourceKeys": "properties.os", + "required": true + }, + { + "destKey": "ip", + "sourceKeys": ["context.ip", "request_ip"], + "required": true + }, + { + "destKey": "use_ip", + "sourceKeys": "properties.use_ip", + "required": false + }, + { + "destKey": "install_source", + "sourceKeys": "properties.install_source", + "required": true + }, + { + "destKey": "ua", + "sourceKeys": "context.userAgent", + "required": false + }, + { + "destKey": "utime", + "sourceKeys": "timestamp", + "sourceFromGenericMap": true, + "required": false, + "metadata": { + "type": "secondTimestamp" + } + }, + { + "destKey": "data_sharing_options", + "sourceKeys": "properties.data_sharing_options", + "required": false + }, + { + "destKey": "custom_user_id", + "sourceKeys": "properties.custom_user_id", + "required": false + }, + { + "destKey": "install", + "sourceKeys": "properties.install", + "required": false + } +] diff --git a/src/v0/destinations/singular/transform.js b/src/v0/destinations/singular/transform.js index ff5d18db9a..ed6757c47b 100644 --- a/src/v0/destinations/singular/transform.js +++ b/src/v0/destinations/singular/transform.js @@ -20,7 +20,7 @@ const responseBuilderSimple = (message, { Config }) => { } const sessionEvent = isSessionEvent(Config, eventName); - const { eventAttributes, payload } = platformWisePayloadGenerator(message, sessionEvent); + const { eventAttributes, payload } = platformWisePayloadGenerator(message, sessionEvent, Config); const endpoint = sessionEvent ? `${BASE_URL}/launch` : `${BASE_URL}/evt`; // If we have an event where we have an array of Products, example Order Completed diff --git a/src/v0/destinations/singular/util.js b/src/v0/destinations/singular/util.js index 4c5aeb8964..61db0472ab 100644 --- a/src/v0/destinations/singular/util.js +++ b/src/v0/destinations/singular/util.js @@ -9,6 +9,7 @@ const { SINGULAR_EVENT_IOS_EXCLUSION, BASE_URL, SUPPORTED_PLATFORM, + SUPPORTED_UNTIY_SUBPLATFORMS, SESSIONEVENTS, } = require('./config'); const { @@ -85,7 +86,7 @@ const isSessionEvent = (Config, eventName) => { * @param {*} sessionEvent * @returns */ -const platformWisePayloadGenerator = (message, sessionEvent) => { +const platformWisePayloadGenerator = (message, sessionEvent, Config) => { let eventAttributes; const clonedMessage = { ...message }; let platform = getValueFromMessage(clonedMessage, 'context.os.name'); @@ -99,55 +100,68 @@ const platformWisePayloadGenerator = (message, sessionEvent) => { platform = 'iOS'; } platform = platform.toLowerCase(); - if (!SUPPORTED_PLATFORM[platform]) { + if (!SUPPORTED_PLATFORM[platform] && !SUPPORTED_UNTIY_SUBPLATFORMS[platform]) { throw new InstrumentationError(`Platform ${platform} is not supported`); } - - const payload = constructPayload( - clonedMessage, - MAPPING_CONFIG[CONFIG_CATEGORIES[`${typeOfEvent}_${SUPPORTED_PLATFORM[platform]}`].name], - ); - - if (!payload) { - throw new TransformationError(`Failed to Create ${platform} ${typeOfEvent} Payload`); - } - if (sessionEvent) { - // context.device.adTrackingEnabled = true implies Singular's do not track (dnt) - // to be 0 and vice-versa. - const adTrackingEnabled = getValueFromMessage( + let payload; + if (SUPPORTED_UNTIY_SUBPLATFORMS.includes(platform)) { + payload = constructPayload( clonedMessage, - 'context.device.adTrackingEnabled', + MAPPING_CONFIG[CONFIG_CATEGORIES[`${typeOfEvent}_UNITY`].name], ); - if (adTrackingEnabled === true) { - payload.dnt = 0; - } else { - payload.dnt = 1; - } - // by default, the value of openuri and install_source should be "", i.e empty string if nothing is passed - payload.openuri = clonedMessage.properties.url || ''; - if (platform === 'android' || platform === 'Android') { - payload.install_source = clonedMessage.properties.referring_application || ''; - } } else { - // Custom Attribues is not supported by session events - eventAttributes = extractExtraFields( + payload = constructPayload( clonedMessage, - exclusionList[`${SUPPORTED_PLATFORM[platform]}_${typeOfEvent}_EXCLUSION_LIST`], + MAPPING_CONFIG[CONFIG_CATEGORIES[`${typeOfEvent}_${SUPPORTED_PLATFORM[platform]}`].name], ); - eventAttributes = removeUndefinedAndNullValues(eventAttributes); + } - // If anyone out of value, revenue, total is set,we will have amt in payload - // and we will consider the event as revenue event. - if (!isDefinedAndNotNull(payload.is_revenue_event) && payload.amt) { - payload.is_revenue_event = true; - } + if (!payload) { + throw new TransformationError(`Failed to Create ${platform} ${typeOfEvent} Payload`); } + if (!SUPPORTED_UNTIY_SUBPLATFORMS.includes(platform)) { + if (sessionEvent) { + // context.device.adTrackingEnabled = true implies Singular's do not track (dnt) + // to be 0 and vice-versa. + const adTrackingEnabled = getValueFromMessage( + clonedMessage, + 'context.device.adTrackingEnabled', + ); + if (adTrackingEnabled === true) { + payload.dnt = 0; + } else { + payload.dnt = 1; + } + // by default, the value of openuri and install_source should be "", i.e empty string if nothing is passed + payload.openuri = clonedMessage.properties.url || ''; + if (platform === 'android' || platform === 'Android') { + payload.install_source = clonedMessage.properties.referring_application || ''; + } + } else { + // Custom Attribues is not supported by session events + eventAttributes = extractExtraFields( + clonedMessage, + exclusionList[`${SUPPORTED_PLATFORM[platform]}_${typeOfEvent}_EXCLUSION_LIST`], + ); + eventAttributes = removeUndefinedAndNullValues(eventAttributes); - // Singular maps Connection Type to either wifi or carrier - if (clonedMessage.context?.network?.wifi) { - payload.c = 'wifi'; - } else { - payload.c = 'carrier'; + // If anyone out of value, revenue, total is set,we will have amt in payload + // and we will consider the event as revenue event. + if (!isDefinedAndNotNull(payload.is_revenue_event) && payload.amt) { + payload.is_revenue_event = true; + } + } + + // Singular maps Connection Type to either wifi or carrier + if (clonedMessage.context?.network?.wifi) { + payload.c = 'wifi'; + } else { + payload.c = 'carrier'; + } + } else if (Config.match_id === 'advertisingId') { + payload.match_id = clonedMessage?.context?.device?.advertisingId; + } else if (message.properties.match_id) { + payload.match_id = message.properties.match_id; } return { payload, eventAttributes }; }; diff --git a/src/v0/sources/shopify/config.js b/src/v0/sources/shopify/config.js index b8b3cde284..7f5d41ec96 100644 --- a/src/v0/sources/shopify/config.js +++ b/src/v0/sources/shopify/config.js @@ -35,7 +35,33 @@ const SHOPIFY_TRACK_MAP = { orders_cancelled: 'Order Cancelled', orders_fulfilled: 'Order Fulfilled', orders_paid: 'Order Paid', - orders_partially_fullfilled: 'Order Partially Fulfilled', + orders_partially_fulfilled: 'Order Partially Fulfilled', + // following are the events that supported by rudderstack pixel app as generic track events + customer_tags_added: 'Customer Tags Added', + customer_tags_removed: 'Customer Tags Removed', + customer_email_updated: 'Customer Email Updated', + collections_create: 'Collection Created', + collections_update: 'Collection Updated', + collections_delete: 'Collection Deleted', + collection_listings_add: 'Collection Listings Added', + collection_listings_remove: 'Collection Listings Removed', + collection_listings_update: 'Collection Listings Updated', + collection_publications_create: 'Collection Publications Created', + collection_publications_delete: 'Collection Publications Deleted', + collection_publications_update: 'Collection Publications Updated', + discounts_create: 'Discount Created', + discounts_delete: 'Discount Deleted', + discounts_update: 'Discount Updated', + draft_orders_create: 'Draft Order Created', + draft_orders_delete: 'Draft Order Deleted', + draft_orders_update: 'Draft Order Updated', + fulfillment_order_split: 'Fulfillment Order Split', + inventory_items_create: 'Inventory Items Created', + inventory_items_delete: 'Inventory Items Deleted', + inventory_items_update: 'Inventory Items Updated', + inventory_levels_connect: 'Inventory Levels Connected', + inventory_levels_disconnect: 'Inventory Levels Disconnected', + inventory_levels_update: 'Inventory Levels Updated', }; const identifyMappingJSON = JSON.parse( @@ -99,7 +125,33 @@ const SUPPORTED_TRACK_EVENTS = [ 'orders_cancelled', 'orders_fulfilled', 'orders_paid', - 'orders_partially_fullfilled', + 'orders_partially_fulfilled', + // following are the events that supported by rudderstack pixel app as generic track events + 'customer_tags_added', + 'customer_tags_removed', + 'customer_email_updated', + 'collections_create', + 'collections_update', + 'collections_delete', + 'collection_listings_add', + 'collection_listings_remove', + 'collection_listings_update', + 'collection_publications_create', + 'collection_publications_delete', + 'collection_publications_update', + 'discounts_create', + 'discounts_delete', + 'discounts_update', + 'draft_orders_create', + 'draft_orders_delete', + 'draft_orders_update', + 'fulfillment_order_split', + 'inventory_items_create', + 'inventory_items_delete', + 'inventory_items_update', + 'inventory_levels_connect', + 'inventory_levels_disconnect', + 'inventory_levels_update', ]; const maxTimeToIdentifyRSGeneratedCall = 10000; // in ms diff --git a/src/v0/sources/shopify/transform.js b/src/v0/sources/shopify/transform.js index b55fc61327..8cf39dfa5c 100644 --- a/src/v0/sources/shopify/transform.js +++ b/src/v0/sources/shopify/transform.js @@ -287,4 +287,10 @@ const process = async (event) => { return response; }; -exports.process = process; +module.exports = { + process, + processEvent, + identifyPayloadBuilder, + ecomPayloadBuilder, + trackPayloadBuilder, +}; diff --git a/src/v1/destinations/ga4_v2/networkHandler.ts b/src/v1/destinations/ga4_v2/networkHandler.ts new file mode 100644 index 0000000000..aece0411c1 --- /dev/null +++ b/src/v1/destinations/ga4_v2/networkHandler.ts @@ -0,0 +1,3 @@ +import { networkHandler } from '../../../v0/destinations/ga4/networkHandler'; + +module.exports = { networkHandler }; diff --git a/src/v1/sources/shopify/config.js b/src/v1/sources/shopify/config.js new file mode 100644 index 0000000000..5a3ce99b40 --- /dev/null +++ b/src/v1/sources/shopify/config.js @@ -0,0 +1,76 @@ +const path = require('path'); +const fs = require('fs'); + +const PIXEL_EVENT_TOPICS = { + CART_VIEWED: 'cart_viewed', + PRODUCT_ADDED_TO_CART: 'product_added_to_cart', + PRODUCT_REMOVED_FROM_CART: 'product_removed_from_cart', + PAGE_VIEWED: 'page_viewed', + PRODUCT_VIEWED: 'product_viewed', + COLLECTION_VIEWED: 'collection_viewed', + CHECKOUT_STARTED: 'checkout_started', + CHECKOUT_COMPLETED: 'checkout_completed', + CHECKOUT_ADDRESS_INFO_SUBMITTED: 'checkout_address_info_submitted', + CHECKOUT_CONTACT_INFO_SUBMITTED: 'checkout_contact_info_submitted', + CHECKOUT_SHIPPING_INFO_SUBMITTED: 'checkout_shipping_info_submitted', + PAYMENT_INFO_SUBMITTED: 'payment_info_submitted', + SEARCH_SUBMITTED: 'search_submitted', +}; + +const PIXEL_EVENT_MAPPING = { + cart_viewed: 'Cart Viewed', + product_added_to_cart: 'Product Added', + product_removed_from_cart: 'Product Removed', + page_viewed: 'Page Viewed', + product_viewed: 'Product Viewed', + collection_viewed: 'Collection Viewed', + checkout_started: 'Checkout Started', + checkout_completed: 'Checkout Completed', + checkout_address_info_submitted: 'Checkout Address Info Submitted', + checkout_contact_info_submitted: 'Checkout Contact Info Submitted', + checkout_shipping_info_submitted: 'Checkout Shipping Info Submitted', + payment_info_submitted: 'Payment Info Submitted', + search_submitted: 'Search Submitted', +}; + +const contextualFieldMappingJSON = JSON.parse( + fs.readFileSync(path.resolve(__dirname, 'pixelEventsMappings', 'contextualFieldMapping.json')), +); + +const cartViewedEventMappingJSON = JSON.parse( + fs.readFileSync(path.resolve(__dirname, 'pixelEventsMappings', 'cartViewedEventMapping.json')), +); + +const productListViewedEventMappingJSON = JSON.parse( + fs.readFileSync( + path.resolve(__dirname, 'pixelEventsMappings', 'productListViewedEventMapping.json'), + ), +); + +const productViewedEventMappingJSON = JSON.parse( + fs.readFileSync(path.resolve(__dirname, 'pixelEventsMappings', 'productViewedEventMapping.json')), +); + +const productToCartEventMappingJSON = JSON.parse( + fs.readFileSync(path.resolve(__dirname, 'pixelEventsMappings', 'productToCartEventMapping.json')), +); + +const checkoutStartedCompletedEventMappingJSON = JSON.parse( + fs.readFileSync( + path.resolve(__dirname, 'pixelEventsMappings', 'checkoutStartedCompletedEventMapping.json'), + ), +); + +const INTEGERATION = 'SHOPIFY'; + +module.exports = { + INTEGERATION, + PIXEL_EVENT_TOPICS, + PIXEL_EVENT_MAPPING, + contextualFieldMappingJSON, + cartViewedEventMappingJSON, + productListViewedEventMappingJSON, + productViewedEventMappingJSON, + productToCartEventMappingJSON, + checkoutStartedCompletedEventMappingJSON, +}; diff --git a/src/v1/sources/shopify/pixelEventsMappings/cartViewedEventMapping.json b/src/v1/sources/shopify/pixelEventsMappings/cartViewedEventMapping.json new file mode 100644 index 0000000000..b74320c758 --- /dev/null +++ b/src/v1/sources/shopify/pixelEventsMappings/cartViewedEventMapping.json @@ -0,0 +1,42 @@ +[ + { + "sourceKeys": "merchandise.product.id", + "destKeys": "product_id" + }, + { + "sourceKeys": "merchandise.product.title", + "destKeys": "variant" + }, + { + "sourceKeys": "merchandise.image.src", + "destKeys": "image_url" + }, + { + "sourceKeys": "merchandise.price.amount", + "destKeys": "price" + }, + { + "sourceKeys": "merchandise.product.type", + "destKeys": "category" + }, + { + "sourceKeys": "merchandise.product.url", + "destKeys": "url" + }, + { + "sourceKeys": "merchandise.product.vendor", + "destKeys": "brand" + }, + { + "sourceKeys": "merchandise.sku", + "destKeys": "sku" + }, + { + "sourceKeys": "merchandise.title", + "destKeys": "name" + }, + { + "sourceKeys": "quantity", + "destKeys": "quantity" + } +] diff --git a/src/v1/sources/shopify/pixelEventsMappings/checkoutStartedCompletedEventMapping.json b/src/v1/sources/shopify/pixelEventsMappings/checkoutStartedCompletedEventMapping.json new file mode 100644 index 0000000000..478bcfc270 --- /dev/null +++ b/src/v1/sources/shopify/pixelEventsMappings/checkoutStartedCompletedEventMapping.json @@ -0,0 +1,42 @@ +[ + { + "sourceKeys": "quantity", + "destKeys": "quantity" + }, + { + "sourceKeys": "title", + "destKeys": "name" + }, + { + "sourceKeys": "variant.image.src", + "destKeys": "image_url" + }, + { + "sourceKeys": "variant.price.amount", + "destKeys": "price" + }, + { + "sourceKeys": "variant.sku", + "destKeys": "sku" + }, + { + "sourceKeys": "variant.product.id", + "destKeys": "product_id" + }, + { + "sourceKeys": "variant.product.title", + "destKeys": "variant" + }, + { + "sourceKeys": "variant.product.type", + "destKeys": "category" + }, + { + "sourceKeys": "variant.product.url", + "destKeys": "url" + }, + { + "sourceKeys": "variant.product.vendor", + "destKeys": "brand" + } +] diff --git a/src/v1/sources/shopify/pixelEventsMappings/contextualFieldMapping.json b/src/v1/sources/shopify/pixelEventsMappings/contextualFieldMapping.json new file mode 100644 index 0000000000..f314684948 --- /dev/null +++ b/src/v1/sources/shopify/pixelEventsMappings/contextualFieldMapping.json @@ -0,0 +1,34 @@ +[ + { + "sourceKeys": "context.document.referrer", + "destKeys": "page.referrer" + }, + { + "sourceKeys": "document.title", + "destKeys": "page.title" + }, + { + "sourceKeys": "navigator.userAgent", + "destKeys": "userAgent" + }, + { + "sourceKeys": "window.location.href", + "destKeys": "page.url" + }, + { + "sourceKeys": "window.location.pathname", + "destKeys": "page.path" + }, + { + "sourceKeys": "window.location.search", + "destKeys": "page.search" + }, + { + "sourceKeys": "window.screen.height", + "destKeys": "screen.height" + }, + { + "sourceKeys": "window.screen.width", + "destKeys": "screen.width" + } +] diff --git a/src/v1/sources/shopify/pixelEventsMappings/productListViewedEventMapping.json b/src/v1/sources/shopify/pixelEventsMappings/productListViewedEventMapping.json new file mode 100644 index 0000000000..fb8e8a4567 --- /dev/null +++ b/src/v1/sources/shopify/pixelEventsMappings/productListViewedEventMapping.json @@ -0,0 +1,38 @@ +[ + { + "sourceKeys": "image.src", + "destKeys": "image_url" + }, + { + "sourceKeys": "price.amount", + "destKeys": "price" + }, + { + "sourceKeys": "product.id", + "destKeys": "product_id" + }, + { + "sourceKeys": "product.title", + "destKeys": "variant" + }, + { + "sourceKeys": "product.type", + "destKeys": "category" + }, + { + "sourceKeys": "product.url", + "destKeys": "url" + }, + { + "sourceKeys": "product.vendor", + "destKeys": "brand" + }, + { + "sourceKeys": "sku", + "destKeys": "sku" + }, + { + "sourceKeys": "title", + "destKeys": "name" + } +] diff --git a/src/v1/sources/shopify/pixelEventsMappings/productToCartEventMapping.json b/src/v1/sources/shopify/pixelEventsMappings/productToCartEventMapping.json new file mode 100644 index 0000000000..2f9d71bc03 --- /dev/null +++ b/src/v1/sources/shopify/pixelEventsMappings/productToCartEventMapping.json @@ -0,0 +1,42 @@ +[ + { + "sourceKeys": "cartLine.merchandise.image.src", + "destKeys": "image_url" + }, + { + "sourceKeys": "cartLine.merchandise.price.amount", + "destKeys": "price" + }, + { + "sourceKeys": "cartLine.merchandise.product.id", + "destKeys": "product_id" + }, + { + "sourceKeys": "cartLine.merchandise.product.title", + "destKeys": "variant" + }, + { + "sourceKeys": "cartLine.merchandise.product.type", + "destKeys": "category" + }, + { + "sourceKeys": "cartLine.merchandise.product.vendor", + "destKeys": "brand" + }, + { + "sourceKeys": "cartLine.merchandise.product.url", + "destKeys": "url" + }, + { + "sourceKeys": "cartLine.merchandise.sku", + "destKeys": "sku" + }, + { + "sourceKeys": "cartLine.merchandise.title", + "destKeys": "name" + }, + { + "sourceKeys": "cartLine.quantity", + "destKeys": "quantity" + } +] diff --git a/src/v1/sources/shopify/pixelEventsMappings/productViewedEventMapping.json b/src/v1/sources/shopify/pixelEventsMappings/productViewedEventMapping.json new file mode 100644 index 0000000000..becb15acfc --- /dev/null +++ b/src/v1/sources/shopify/pixelEventsMappings/productViewedEventMapping.json @@ -0,0 +1,46 @@ +[ + { + "sourceKeys": "productVariant.product.id", + "destKeys": "product_id" + }, + { + "sourceKeys": "productVariant.product.title", + "destKeys": "variant" + }, + { + "sourceKeys": "productVariant.product.vendor", + "destKeys": "brand" + }, + { + "sourceKeys": "productVariant.product.type", + "destKeys": "category" + }, + { + "sourceKeys": "productVariant.product.image.src", + "destKeys": "image_url" + }, + { + "sourceKeys": "productVariant.price.amount", + "destKeys": "price" + }, + { + "sourceKeys": "productVariant.price.currencyCode", + "destKeys": "currency" + }, + { + "sourceKeys": "productVariant.product.url", + "destKeys": "url" + }, + { + "sourceKeys": "productVariant.product.sku", + "destKeys": "sku" + }, + { + "sourceKeys": "productVariant.product.title", + "destKeys": "name" + }, + { + "sourceKeys": "cartLine.quantity", + "destKeys": "quantity" + } +] diff --git a/src/v1/sources/shopify/pixelTransform.js b/src/v1/sources/shopify/pixelTransform.js new file mode 100644 index 0000000000..0ca64b4123 --- /dev/null +++ b/src/v1/sources/shopify/pixelTransform.js @@ -0,0 +1,91 @@ +const stats = require('../../../util/stats'); +const logger = require('../../../logger'); +const { removeUndefinedAndNullValues } = require('../../../v0/util'); +const { + pageViewedEventBuilder, + cartViewedEventBuilder, + productListViewedEventBuilder, + productViewedEventBuilder, + productToCartEventBuilder, + checkoutEventBuilder, + checkoutStepEventBuilder, + searchEventBuilder, +} = require('./pixelUtils'); +const { INTEGERATION, PIXEL_EVENT_TOPICS } = require('./config'); + +const NO_OPERATION_SUCCESS = { + outputToSource: { + body: Buffer.from('OK').toString('base64'), + contentType: 'text/plain', + }, + statusCode: 200, +}; + +function processPixelEvent(inputEvent) { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { name, query_parameters, clientId, data } = inputEvent; + const { checkout } = data ?? {}; + const { order } = checkout ?? {}; + const { customer } = order ?? {}; + let message = {}; + switch (name) { + case PIXEL_EVENT_TOPICS.PAGE_VIEWED: + message = pageViewedEventBuilder(inputEvent); + break; + case PIXEL_EVENT_TOPICS.CART_VIEWED: + message = cartViewedEventBuilder(inputEvent); + break; + case PIXEL_EVENT_TOPICS.COLLECTION_VIEWED: + message = productListViewedEventBuilder(inputEvent); + break; + case PIXEL_EVENT_TOPICS.PRODUCT_VIEWED: + message = productViewedEventBuilder(inputEvent); + break; + case PIXEL_EVENT_TOPICS.PRODUCT_ADDED_TO_CART: + case PIXEL_EVENT_TOPICS.PRODUCT_REMOVED_FROM_CART: + message = productToCartEventBuilder(inputEvent); + break; + case PIXEL_EVENT_TOPICS.CHECKOUT_STARTED: + case PIXEL_EVENT_TOPICS.CHECKOUT_COMPLETED: + if (customer.id) message.userId = customer.id || ''; + message = checkoutEventBuilder(inputEvent); + break; + case PIXEL_EVENT_TOPICS.CHECKOUT_ADDRESS_INFO_SUBMITTED: + case PIXEL_EVENT_TOPICS.CHECKOUT_CONTACT_INFO_SUBMITTED: + case PIXEL_EVENT_TOPICS.CHECKOUT_SHIPPING_INFO_SUBMITTED: + case PIXEL_EVENT_TOPICS.PAYMENT_INFO_SUBMITTED: + if (customer.id) message.userId = customer.id || ''; + message = checkoutStepEventBuilder(inputEvent); + break; + case PIXEL_EVENT_TOPICS.SEARCH_SUBMITTED: + message = searchEventBuilder(inputEvent); + break; + default: + logger.debug(`{{SHOPIFY::}} Invalid pixel event ${name}`); + stats.increment('invalid_shopify_event', { + writeKey: query_parameters.writeKey, + source: 'SHOPIFY', + shopifyTopic: name, + }); + return NO_OPERATION_SUCCESS; + } + message.anonymousId = clientId; + message.setProperty(`integrations.${INTEGERATION}`, true); + message.setProperty('context.library', { + name: 'RudderStack Shopify Cloud', + eventOrigin: 'client', + version: '2.0.0', + }); + message.setProperty('context.topic', name); + message = removeUndefinedAndNullValues(message); + return message; +} + +const processEventFromPixel = async (event) => { + const pixelEvent = processPixelEvent(event); + return removeUndefinedAndNullValues(pixelEvent); +}; + +module.exports = { + processEventFromPixel, +}; diff --git a/src/v1/sources/shopify/pixelUtils.js b/src/v1/sources/shopify/pixelUtils.js new file mode 100644 index 0000000000..92197a558b --- /dev/null +++ b/src/v1/sources/shopify/pixelUtils.js @@ -0,0 +1,195 @@ +/* eslint-disable no-param-reassign */ +const Message = require('../../../v0/sources/message'); +const { EventType } = require('../../../constants'); +const { + INTEGERATION, + PIXEL_EVENT_MAPPING, + contextualFieldMappingJSON, + cartViewedEventMappingJSON, + productListViewedEventMappingJSON, + productViewedEventMappingJSON, + productToCartEventMappingJSON, + checkoutStartedCompletedEventMappingJSON, +} = require('./config'); + +function getNestedValue(object, path) { + const keys = path.split('.'); + return keys.reduce((nestedObject, key) => nestedObject && nestedObject[key], object); +} + +function setNestedValue(object, path, value) { + const keys = path.split('.'); + const lastKeyIndex = keys.length - 1; + keys.reduce((nestedObject, key, index) => { + if (index === lastKeyIndex) { + nestedObject[key] = value; + } else if (!nestedObject[key]) { + nestedObject[key] = {}; + } + return nestedObject[key]; + }, object); +} + +function mapObjectKeys(sourceObject, keyMappings) { + if (!Array.isArray(keyMappings)) { + throw new TypeError('keyMappings should be an array'); + } + const resultObject = { ...sourceObject }; + + // eslint-disable-next-line @typescript-eslint/no-shadow + return keyMappings.reduce((resultObject, { sourceKeys, destKeys }) => { + const value = getNestedValue(sourceObject, sourceKeys); + if (value !== undefined) { + setNestedValue(resultObject, destKeys, value); + } + return resultObject; + }, resultObject); +} + +const createMessage = (eventType, eventName, properties, context) => { + const message = new Message(INTEGERATION); + message.setEventType(eventType); + message.setEventName(eventName); + message.properties = properties; + message.context = context; + return message; +}; + +const pageViewedEventBuilder = (inputEvent) => { + const { data, context } = inputEvent; + const pageEventContextValues = mapObjectKeys(context, contextualFieldMappingJSON); + const message = new Message(INTEGERATION); + message.name = 'Page View'; + message.setEventType(EventType.PAGE); + message.properties = { ...data }; + message.context = { ...pageEventContextValues }; + return message; +}; + +const cartViewedEventBuilder = (inputEvent) => { + const lines = inputEvent?.data?.cart?.lines; + const products = []; + let total; + if (lines) { + lines.forEach((line) => { + const product = mapObjectKeys(line, cartViewedEventMappingJSON); + products.push(product); + total = line.cost.totalAmount.amount; + }); + } + + const properties = { + products, + cart_id: inputEvent.data.cart.id, + total, + }; + const contextualPayload = mapObjectKeys(inputEvent.context, contextualFieldMappingJSON); + return createMessage(EventType.TRACK, 'Cart Viewed', properties, contextualPayload); +}; + +const productListViewedEventBuilder = (inputEvent) => { + const productVariants = inputEvent?.data?.collection?.productVariants; + const products = []; + + productVariants.forEach((productVariant) => { + const mappedProduct = mapObjectKeys(productVariant, productListViewedEventMappingJSON); + products.push(mappedProduct); + }); + + const properties = { + cart_id: inputEvent.clientId, + list_id: inputEvent.id, + products, + }; + + const contextualPayload = mapObjectKeys(inputEvent.context, contextualFieldMappingJSON); + return createMessage(EventType.TRACK, 'Product List Viewed', properties, contextualPayload); +}; + +const productViewedEventBuilder = (inputEvent) => { + const properties = { + ...mapObjectKeys(inputEvent.data, productViewedEventMappingJSON), + }; + const contextualPayload = mapObjectKeys(inputEvent.context, contextualFieldMappingJSON); + return createMessage(EventType.TRACK, 'Product Viewed', properties, contextualPayload); +}; + +const productToCartEventBuilder = (inputEvent) => { + const properties = { + ...mapObjectKeys(inputEvent.data, productToCartEventMappingJSON), + }; + const contextualPayload = mapObjectKeys(inputEvent.context, contextualFieldMappingJSON); + return createMessage( + EventType.TRACK, + PIXEL_EVENT_MAPPING[inputEvent.name], + properties, + contextualPayload, + ); +}; + +const checkoutEventBuilder = (inputEvent) => { + const lineItems = inputEvent?.data?.checkout?.lineItems; + const products = []; + + lineItems.forEach((lineItem) => { + const mappedProduct = mapObjectKeys(lineItem, checkoutStartedCompletedEventMappingJSON); + products.push(mappedProduct); + }); + + const properties = { + products, + order_id: inputEvent.id, + checkout_id: inputEvent?.data?.checkout?.token, + total: inputEvent?.data?.checkout?.totalPrice?.amount, + currency: inputEvent?.data?.checkout?.currencyCode, + discount: inputEvent?.data?.checkout?.discountsAmount?.amount, + shipping: inputEvent?.data?.checkout?.shippingLine?.price?.amount, + revenue: inputEvent?.data?.checkout?.subtotalPrice?.amount, + value: inputEvent?.data?.checkout?.totalPrice?.amount, + tax: inputEvent?.data?.checkout?.totalTax?.amount, + }; + const contextualPayload = mapObjectKeys(inputEvent.context, contextualFieldMappingJSON); + return createMessage( + EventType.TRACK, + PIXEL_EVENT_MAPPING[inputEvent.name], + properties, + contextualPayload, + ); +}; + +const checkoutStepEventBuilder = (inputEvent) => { + const contextualPayload = mapObjectKeys(inputEvent.context, contextualFieldMappingJSON); + const properties = { + ...inputEvent.data.checkout, + }; + return createMessage( + EventType.TRACK, + PIXEL_EVENT_MAPPING[inputEvent.name], + properties, + contextualPayload, + ); +}; + +const searchEventBuilder = (inputEvent) => { + const properties = { + query: inputEvent.data.searchResult.query, + }; + const contextualPayload = mapObjectKeys(inputEvent.context, contextualFieldMappingJSON); + return createMessage( + EventType.TRACK, + PIXEL_EVENT_MAPPING[inputEvent.name], + properties, + contextualPayload, + ); +}; + +module.exports = { + pageViewedEventBuilder, + cartViewedEventBuilder, + productListViewedEventBuilder, + productViewedEventBuilder, + productToCartEventBuilder, + checkoutEventBuilder, + checkoutStepEventBuilder, + searchEventBuilder, +}; diff --git a/src/v1/sources/shopify/pixelUtils.test.js b/src/v1/sources/shopify/pixelUtils.test.js new file mode 100644 index 0000000000..cd544568cd --- /dev/null +++ b/src/v1/sources/shopify/pixelUtils.test.js @@ -0,0 +1,244 @@ +const { + pageViewedEventBuilder, + cartViewedEventBuilder, + productListViewedEventBuilder, + productViewedEventBuilder, + productToCartEventBuilder, + checkoutEventBuilder, + checkoutStepEventBuilder, + searchEventBuilder, +} = require('./pixelUtils'); +const { EventType } = require('../../../constants'); +const Message = require('../../../v0/sources/message'); +jest.mock('ioredis', () => require('../../../../test/__mocks__/redis')); +jest.mock('../../../v0/sources/message'); + +describe('utilV2.js', () => { + beforeEach(() => { + Message.mockClear(); + }); + + describe('pageViewedEventBuilder', () => { + it('should build a page viewed event message', () => { + const inputEvent = { + data: { url: 'https://example.com' }, + context: { userAgent: 'Mozilla/5.0' }, + }; + const message = pageViewedEventBuilder(inputEvent); + expect(message).toBeInstanceOf(Message); + expect(message.name).toBe('Page View'); + expect(message.properties).toEqual(inputEvent.data); + expect(message.context).toEqual({ userAgent: 'Mozilla/5.0' }); + }); + }); + + describe('cartViewedEventBuilder', () => { + it('should build a cart viewed event message', () => { + const inputEvent = { + data: { + cart: { + cost: { + totalAmount: { + amount: 1259.9, + currencyCode: 'USD', + }, + }, + lines: [ + { + cost: { + totalAmount: { + amount: 1259.9, + currencyCode: 'USD', + }, + }, + merchandise: { + price: { + amount: 629.95, + currencyCode: 'USD', + }, + product: { + title: 'The Multi-managed Snowboard', + }, + id: '41327143157873', + title: 'Default Title', + untranslatedTitle: 'Default Title', + }, + quantity: 2, + }, + ], + totalQuantity: 2, + attributes: [], + id: '123', + }, + }, + context: { userAgent: 'Mozilla/5.0' }, + }; + const message = cartViewedEventBuilder(inputEvent); + expect(message).toBeInstanceOf(Message); + expect(message.properties).toEqual({ + products: [ + { + name: 'Default Title', + price: 629.95, + quantity: 2, + variant: 'The Multi-managed Snowboard', + merchandise: { + id: '41327143157873', + price: { + amount: 629.95, + currencyCode: 'USD', + }, + product: { + title: 'The Multi-managed Snowboard', + }, + title: 'Default Title', + untranslatedTitle: 'Default Title', + }, + cost: { + totalAmount: { amount: 1259.9, currencyCode: 'USD' }, + }, + }, + ], + cart_id: '123', + total: 1259.9, + }); + expect(message.context).toEqual({ userAgent: 'Mozilla/5.0' }); + }); + }); + + describe('productListViewedEventBuilder', () => { + it('should build a product list viewed event message', () => { + const inputEvent = { + data: { + collection: { + productVariants: [ + { id: 'product123', name: 'Product 123' }, + { id: 'product456', name: 'Product 456' }, + ], + }, + }, + clientId: 'client123', + id: 'list123', + context: { userAgent: 'Mozilla/5.0' }, + }; + const message = productListViewedEventBuilder(inputEvent); + expect(message).toBeInstanceOf(Message); + expect(message.properties).toEqual({ + cart_id: 'client123', + list_id: 'list123', + products: [ + { id: 'product123', name: 'Product 123' }, + { id: 'product456', name: 'Product 456' }, + ], + }); + expect(message.context).toEqual({ userAgent: 'Mozilla/5.0' }); + }); + }); + + describe('productViewedEventBuilder', () => { + it('should build a product viewed event message', () => { + const inputEvent = { + data: { id: 'product123', name: 'Product 123' }, + context: { userAgent: 'Mozilla/5.0' }, + }; + const message = productViewedEventBuilder(inputEvent); + expect(message).toBeInstanceOf(Message); + expect(message.properties).toEqual({ id: 'product123', name: 'Product 123' }); + expect(message.context).toEqual({ userAgent: 'Mozilla/5.0' }); + }); + }); + + describe('productToCartEventBuilder', () => { + it('should build a product to cart event message', () => { + const inputEvent = { + data: { id: 'product123', name: 'Product 123' }, + context: { userAgent: 'Mozilla/5.0' }, + name: 'add_to_cart', + }; + const message = productToCartEventBuilder(inputEvent); + expect(message).toBeInstanceOf(Message); + expect(message.properties).toEqual({ id: 'product123', name: 'Product 123' }); + expect(message.context).toEqual({ userAgent: 'Mozilla/5.0' }); + }); + }); + + describe('checkoutEventBuilder', () => { + it('should build a checkout event message', () => { + const inputEvent = { + data: { + checkout: { + lineItems: [ + { id: 'product123', name: 'Product 123' }, + { id: 'product456', name: 'Product 456' }, + ], + token: 'checkout123', + totalPrice: { amount: 200 }, + currencyCode: 'USD', + discountsAmount: { amount: 10 }, + shippingLine: { price: { amount: 5 } }, + subtotalPrice: { amount: 185 }, + totalTax: { amount: 15 }, + }, + }, + id: 'order123', + context: { userAgent: 'Mozilla/5.0' }, + name: 'checkout_started', + }; + const message = checkoutEventBuilder(inputEvent); + expect(message).toBeInstanceOf(Message); + expect(message.properties).toEqual({ + products: [ + { id: 'product123', name: 'Product 123' }, + { id: 'product456', name: 'Product 456' }, + ], + order_id: 'order123', + checkout_id: 'checkout123', + total: 200, + currency: 'USD', + discount: 10, + shipping: 5, + revenue: 185, + value: 200, + tax: 15, + }); + expect(message.context).toEqual({ userAgent: 'Mozilla/5.0' }); + }); + }); + + describe('checkoutStepEventBuilder', () => { + it('should build a checkout step event message', () => { + const inputEvent = { + data: { + checkout: { + step: 1, + action: 'shipping_info_submitted', + }, + }, + context: { userAgent: 'Mozilla/5.0' }, + name: 'checkout_step', + }; + const message = checkoutStepEventBuilder(inputEvent); + expect(message).toBeInstanceOf(Message); + expect(message.properties).toEqual({ step: 1, action: 'shipping_info_submitted' }); + expect(message.context).toEqual({ userAgent: 'Mozilla/5.0' }); + }); + }); + + describe('searchEventBuilder', () => { + it('should build a search event message', () => { + const inputEvent = { + data: { + searchResult: { + query: 'test query', + }, + }, + context: { userAgent: 'Mozilla/5.0' }, + name: 'search_submitted', + }; + const message = searchEventBuilder(inputEvent); + expect(message).toBeInstanceOf(Message); + expect(message.properties).toEqual({ query: 'test query' }); + expect(message.context).toEqual({ userAgent: 'Mozilla/5.0' }); + }); + }); +}); diff --git a/src/v1/sources/shopify/transform.js b/src/v1/sources/shopify/transform.js new file mode 100644 index 0000000000..dee5a14a9d --- /dev/null +++ b/src/v1/sources/shopify/transform.js @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +const { processEventFromPixel } = require('./pixelTransform'); +const { process: processWebhookEvents } = require('../../../v0/sources/shopify/transform'); + +const process = async (inputEvent) => { + const { event } = inputEvent; + // check on the source Config to identify the event is from the tracker-based (legacy) + // or the pixel-based (latest) implementation. + const { pixelEventLabel: pixelClientEventLabel } = event; + if (pixelClientEventLabel) { + // this is a event fired from the web pixel loaded on the browser + // by the user interactions with the store. + const responseV2 = await processEventFromPixel(event); + return responseV2; + } + // this is for common logic for server-side events processing for both pixel and tracker apps. + const response = await processWebhookEvents(event); + return response; +}; + +module.exports = { process }; diff --git a/test/integrations/destinations/braze/network.ts b/test/integrations/destinations/braze/network.ts index ae093ce1f4..bcfa78de5d 100644 --- a/test/integrations/destinations/braze/network.ts +++ b/test/integrations/destinations/braze/network.ts @@ -406,6 +406,20 @@ const deleteNwData = [ { alias_name: '77e278c9-e984-4cdd-950c-cd0b61befd03', alias_label: 'rudder_id' }, { alias_name: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', alias_label: 'rudder_id' }, ], + fields_to_export: [ + 'created_at', + 'custom_attributes', + 'dob', + 'email', + 'first_name', + 'gender', + 'home_city', + 'last_name', + 'phone', + 'time_zone', + 'external_id', + 'user_aliases', + ], }, headers: { Authorization: 'Bearer dummyApiKey' }, url: 'https://rest.iad-03.braze.com/users/export/ids', @@ -416,12 +430,8 @@ const deleteNwData = [ { created_at: '2023-03-17T20:51:58.297Z', external_id: 'braze_test_user', - user_aliases: [], - appboy_id: '6414d2ee33326e3354e3040b', - braze_id: '6414d2ee33326e3354e3040b', first_name: 'Jackson', last_name: 'Miranda', - random_bucket: 8134, email: 'jackson24miranda@gmail.com', custom_attributes: { pwa: false, @@ -437,17 +447,6 @@ const deleteNwData = [ }, custom_arr: [1, 2, 'str1'], }, - custom_events: [ - { - name: 'Sign In Completed', - first: '2023-03-10T18:36:05.028Z', - last: '2023-03-10T18:36:05.028Z', - count: 2, - }, - ], - total_revenue: 0, - push_subscribe: 'subscribed', - email_subscribe: 'subscribed', }, ], }, diff --git a/test/integrations/destinations/facebook_conversions/processor/data.ts b/test/integrations/destinations/facebook_conversions/processor/data.ts index d72114c15b..49d2416726 100644 --- a/test/integrations/destinations/facebook_conversions/processor/data.ts +++ b/test/integrations/destinations/facebook_conversions/processor/data.ts @@ -1045,7 +1045,7 @@ export const data = [ JSON_ARRAY: {}, FORM: { data: [ - '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"AddToWishlist","event_time":1699784211,"action_source":"website","custom_data":{"revenue":400,"additional_bet_index":0,"content_ids":[],"contents":[],"currency":"USD","value":400}}', + '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"AddToWishlist","event_time":1699784211,"action_source":"website","custom_data":{"revenue":400,"additional_bet_index":0,"content_ids":[],"contents":[],"currency":"USD","value":400,"num_items":0}}', ], }, }, @@ -1104,8 +1104,143 @@ export const data = [ }, message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', properties: { - revenue: 400, - additional_bet_index: 0, + application_tracking_enabled: 1, + content_name: 'Checkout', + content_type: 'product', + num_items: 1, + products: [ + { + id: '12809', + price: 80, + quantity: 1, + }, + ], + revenue: 93.99, + }, + 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/v20.0/dummyID/events?access_token=09876', + headers: {}, + params: {}, + body: { + JSON: {}, + XML: {}, + JSON_ARRAY: {}, + FORM: { + data: [ + '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"AddPaymentInfo","event_time":1699784211,"action_source":"website","custom_data":{"application_tracking_enabled":1,"content_name":"Checkout","content_type":"product","num_items":1,"products":[{"id":"12809","price":80,"quantity":1}],"revenue":93.99,"content_ids":["12809"],"contents":[{"id":"12809","quantity":1,"item_price":80}],"currency":"USD","value":93.99}}', + ], + }, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + name: 'facebook_conversions', + description: 'Track event with standard event payment info entered without products', + 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: 'payment info entered', + integrations: { + All: true, + }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { + application_tracking_enabled: 1, + content_name: 'Checkout', + content_type: 'product', + num_items: 1, + id: '12809', + price: 80, + quantity: 1, + revenue: 93.99, }, timestamp: '2023-11-12T15:46:51.693229+05:30', type: 'track', @@ -1164,7 +1299,7 @@ export const data = [ JSON_ARRAY: {}, FORM: { data: [ - '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"AddPaymentInfo","event_time":1699784211,"action_source":"website","custom_data":{"revenue":400,"additional_bet_index":0,"content_ids":[],"contents":[],"currency":"USD","value":400}}', + '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"AddPaymentInfo","event_time":1699784211,"action_source":"website","custom_data":{"application_tracking_enabled":1,"content_name":"Checkout","content_type":"product","num_items":1,"id":"12809","price":80,"quantity":1,"revenue":93.99,"content_ids":["12809"],"contents":[{"id":"12809","quantity":1,"item_price":80}],"currency":"USD","value":93.99}}', ], }, }, diff --git a/test/integrations/destinations/lytics/processor/data.ts b/test/integrations/destinations/lytics/processor/data.ts index 344eecc3cd..dd5511140a 100644 --- a/test/integrations/destinations/lytics/processor/data.ts +++ b/test/integrations/destinations/lytics/processor/data.ts @@ -143,6 +143,7 @@ export const data = [ body: { JSON: { _e: 'Order Completed', + anonymous_id: '4eb021e9-a2af-4926-ae82-fe996d12f3c5', checkout_id: 'what is checkout id here??', coupon: 'APPARELSALE', currency: 'GBP', @@ -190,6 +191,7 @@ export const data = [ 'products[2].url': 'https://www.example.com/product/offer-t-shirt', 'products[2].value': 12.99, 'products[2].variant': 'Black', + user_id: 'rudder123', revenue: 31.98, shipping: 4, value: 31.98, @@ -293,6 +295,7 @@ export const data = [ params: {}, body: { JSON: { + anonymous_id: '4eb021e9-a2af-4926-ae82-fe996d12f3c5', user_id: 'rudder123', 'company.id': 'abc123', createdAt: 'Thu Mar 24 2016 17:46:45 GMT+0000 (UTC)', @@ -407,6 +410,7 @@ export const data = [ params: {}, body: { JSON: { + anonymous_id: '4eb021e9-a2af-4926-ae82-fe996d12f3c5', user_id: 'rudder123', 'company.id': 'abc123', createdAt: 'Thu Mar 24 2016 17:46:45 GMT+0000 (UTC)', @@ -514,6 +518,7 @@ export const data = [ params: {}, body: { JSON: { + anonymous_id: '4eb021e9-a2af-4926-ae82-fe996d12f3c5', user_id: 'rudder123', 'company.id': 'abc123', createdAt: 'Thu Mar 24 2016 17:46:45 GMT+0000 (UTC)', @@ -626,6 +631,7 @@ export const data = [ email: 'rudderTest@gmail.com', name: 'Rudder Test', plan: 'Enterprise', + anonymous_id: '4eb021e9-a2af-4926-ae82-fe996d12f3c5', }, XML: {}, JSON_ARRAY: {}, @@ -732,6 +738,7 @@ export const data = [ email: 'rudderTest@gmail.com', name: 'Rudder Test', plan: 'Enterprise', + anonymous_id: '4eb021e9-a2af-4926-ae82-fe996d12f3c5', }, XML: {}, JSON_ARRAY: {}, @@ -1116,6 +1123,7 @@ export const data = [ revenue: 31.98, shipping: 4, value: 31.98, + user_id: 'rudder123', }, XML: {}, JSON_ARRAY: {}, @@ -1209,11 +1217,13 @@ export const data = [ body: { JSON: { event: 'ApplicationLoaded', + anonymous_id: '00000000000000000000000000', path: '', referrer: '', search: '', title: '', url: '', + user_id: '12345', }, XML: {}, JSON_ARRAY: {}, @@ -1307,11 +1317,13 @@ export const data = [ body: { JSON: { event: 'ApplicationLoaded', + anonymous_id: '00000000000000000000000000', path: '', referrer: '', search: '', title: '', url: '', + user_id: '12345', }, XML: {}, JSON_ARRAY: {}, @@ -1414,6 +1426,7 @@ export const data = [ params: {}, body: { JSON: { + anonymous_id: '4eb021e9-a2af-4926-ae82-fe996d12f3c5', user_id: 'rudder123', 'company.id': 'abc123', createdAt: 'Thu Mar 24 2016 17:46:45 GMT+0000 (UTC)', @@ -1440,4 +1453,113 @@ export const data = [ }, }, }, + { + name: 'lytics', + description: 'Test 11: user_id is mapped to userIdOnly', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '4eb021e9-a2af-4926-ae82-fe996d12f3c5', + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.1.6', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.1.6' }, + locale: 'en-GB', + os: { name: '', version: '' }, + page: { + path: '/testing/script-test.html', + referrer: '', + search: '', + title: '', + url: 'http://localhost:3243/testing/script-test.html', + }, + screen: { density: 2 }, + traits: { + company: { id: 'abc123' }, + createdAt: 'Thu Mar 24 2016 17:46:45 GMT+0000 (UTC)', + email: 'rudderTest@gmail.com', + name: 'Rudder Test', + plan: 'Enterprise', + firstName: 'Rudderstack', + lastname: 'Test', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.80 Safari/537.36', + }, + integrations: { All: true }, + messageId: 'e108eb05-f6cd-4624-ba8c-568f2e2b3f92', + originalTimestamp: '2020-10-16T08:26:14.938Z', + receivedAt: '2020-10-16T13:56:14.945+05:30', + request_ip: '[::1]', + sentAt: '2020-10-16T08:26:14.939Z', + timestamp: '2020-10-16T13:56:14.944+05:30', + type: 'identify', + }, + destination: { + DestinationDefinition: { Config: { cdkV2Enabled: true } }, + Config: { apiKey: 'dummyApiKey', stream: 'default' }, + Enabled: true, + Transformations: [], + IsProcessorEnabled: true, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.lytics.io/collect/json/default?access_token=dummyApiKey', + headers: { 'Content-Type': 'application/json' }, + params: {}, + body: { + JSON: { + anonymous_id: '4eb021e9-a2af-4926-ae82-fe996d12f3c5', + 'company.id': 'abc123', + createdAt: 'Thu Mar 24 2016 17:46:45 GMT+0000 (UTC)', + email: 'rudderTest@gmail.com', + name: 'Rudder Test', + plan: 'Enterprise', + first_name: 'Rudderstack', + last_name: 'Test', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/lytics/router/data.ts b/test/integrations/destinations/lytics/router/data.ts deleted file mode 100644 index e5d9adae5c..0000000000 --- a/test/integrations/destinations/lytics/router/data.ts +++ /dev/null @@ -1,299 +0,0 @@ -export const data = [ - { - name: 'lytics', - description: 'Test 0', - feature: 'router', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - input: [ - { - message: { - anonymousId: '4eb021e9-a2af-4926-ae82-fe996d12f3c5', - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.1.6', - }, - library: { name: 'RudderLabs JavaScript SDK', version: '1.1.6' }, - locale: 'en-GB', - os: { name: '', version: '' }, - page: { - path: '/testing/script-test.html', - referrer: '', - search: '', - title: '', - url: 'http://localhost:3243/testing/script-test.html', - }, - screen: { density: 2 }, - traits: { - company: { id: 'abc123' }, - createdAt: 'Thu Mar 24 2016 17:46:45 GMT+0000 (UTC)', - email: 'rudderTest@gmail.com', - name: 'Rudder Test', - plan: 'Enterprise', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.80 Safari/537.36', - }, - event: 'Order Completed', - integrations: { All: true }, - messageId: 'a0adfab9-baf7-4e09-a2ce-bbe2844c324a', - timestamp: '2020-10-16T08:10:12.782Z', - properties: { - checkout_id: 'what is checkout id here??', - coupon: 'APPARELSALE', - currency: 'GBP', - order_id: 'transactionId', - products: [ - { - brand: '', - category: 'Merch', - currency: 'GBP', - image_url: 'https://www.example.com/product/bacon-jam.jpg', - name: 'Food/Drink', - position: 1, - price: 3, - product_id: 'product-bacon-jam', - quantity: 2, - sku: 'sku-1', - typeOfProduct: 'Food', - url: 'https://www.example.com/product/bacon-jam', - value: 6, - variant: 'Extra topped', - }, - { - brand: 'Levis', - category: 'Merch', - currency: 'GBP', - image_url: 'https://www.example.com/product/t-shirt.jpg', - name: 'T-Shirt', - position: 2, - price: 12.99, - product_id: 'product-t-shirt', - quantity: 1, - sku: 'sku-2', - typeOfProduct: 'Shirt', - url: 'https://www.example.com/product/t-shirt', - value: 12.99, - variant: 'White', - }, - { - brand: 'Levis', - category: 'Merch', - coupon: 'APPARELSALE', - currency: 'GBP', - image_url: 'https://www.example.com/product/offer-t-shirt.jpg', - name: 'T-Shirt-on-offer', - position: 1, - price: 12.99, - product_id: 'offer-t-shirt', - quantity: 1, - sku: 'sku-3', - typeOfProduct: 'Shirt', - url: 'https://www.example.com/product/offer-t-shirt', - value: 12.99, - variant: 'Black', - }, - ], - revenue: 31.98, - shipping: 4, - value: 31.98, - }, - receivedAt: '2020-10-16T13:40:12.792+05:30', - request_ip: '[::1]', - sentAt: '2020-10-16T08:10:12.783Z', - type: 'track', - userId: 'rudder123', - }, - metadata: { jobId: 1, userId: 'u1' }, - destination: { - Config: { apiKey: 'dummyApiKey', stream: 'default' }, - Enabled: true, - Transformations: [], - IsProcessorEnabled: true, - }, - }, - { - message: { - anonymousId: '4eb021e9-a2af-4926-ae82-fe996d12f3c5', - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.1.6', - }, - library: { name: 'RudderLabs JavaScript SDK', version: '1.1.6' }, - locale: 'en-GB', - os: { name: '', version: '' }, - page: { - path: '/testing/script-test.html', - referrer: '', - search: '', - title: '', - url: 'http://localhost:3243/testing/script-test.html', - }, - screen: { density: 2 }, - traits: { - company: { id: 'abc123' }, - createdAt: 'Thu Mar 24 2016 17:46:45 GMT+0000 (UTC)', - email: 'rudderTest@gmail.com', - name: 'Rudder Test', - plan: 'Enterprise', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.80 Safari/537.36', - }, - integrations: { All: true }, - messageId: 'e108eb05-f6cd-4624-ba8c-568f2e2b3f92', - originalTimestamp: '2020-10-16T08:26:14.938Z', - receivedAt: '2020-10-16T13:56:14.945+05:30', - request_ip: '[::1]', - sentAt: '2020-10-16T08:26:14.939Z', - timestamp: '2020-10-16T13:56:14.944+05:30', - type: 'identify', - userId: 'rudder123', - }, - metadata: { jobId: 2, userId: 'u1' }, - destination: { - Config: { apiKey: 'dummyApiKey', stream: 'default' }, - Enabled: true, - Transformations: [], - IsProcessorEnabled: true, - }, - }, - ], - destType: 'lytics', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: [ - { - batchedRequest: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.lytics.io/collect/json/default?access_token=dummyApiKey', - headers: { 'Content-Type': 'application/json' }, - params: {}, - body: { - JSON: { - _e: 'Order Completed', - checkout_id: 'what is checkout id here??', - coupon: 'APPARELSALE', - currency: 'GBP', - order_id: 'transactionId', - 'products[0].brand': '', - 'products[0].category': 'Merch', - 'products[0].currency': 'GBP', - 'products[0].image_url': 'https://www.example.com/product/bacon-jam.jpg', - 'products[0].name': 'Food/Drink', - 'products[0].position': 1, - 'products[0].price': 3, - 'products[0].product_id': 'product-bacon-jam', - 'products[0].quantity': 2, - 'products[0].sku': 'sku-1', - 'products[0].typeOfProduct': 'Food', - 'products[0].url': 'https://www.example.com/product/bacon-jam', - 'products[0].value': 6, - 'products[0].variant': 'Extra topped', - 'products[1].brand': 'Levis', - 'products[1].category': 'Merch', - 'products[1].currency': 'GBP', - 'products[1].image_url': 'https://www.example.com/product/t-shirt.jpg', - 'products[1].name': 'T-Shirt', - 'products[1].position': 2, - 'products[1].price': 12.99, - 'products[1].product_id': 'product-t-shirt', - 'products[1].quantity': 1, - 'products[1].sku': 'sku-2', - 'products[1].typeOfProduct': 'Shirt', - 'products[1].url': 'https://www.example.com/product/t-shirt', - 'products[1].value': 12.99, - 'products[1].variant': 'White', - 'products[2].brand': 'Levis', - 'products[2].category': 'Merch', - 'products[2].coupon': 'APPARELSALE', - 'products[2].currency': 'GBP', - 'products[2].image_url': 'https://www.example.com/product/offer-t-shirt.jpg', - 'products[2].name': 'T-Shirt-on-offer', - 'products[2].position': 1, - 'products[2].price': 12.99, - 'products[2].product_id': 'offer-t-shirt', - 'products[2].quantity': 1, - 'products[2].sku': 'sku-3', - 'products[2].typeOfProduct': 'Shirt', - 'products[2].url': 'https://www.example.com/product/offer-t-shirt', - 'products[2].value': 12.99, - 'products[2].variant': 'Black', - revenue: 31.98, - shipping: 4, - value: 31.98, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - }, - metadata: [{ jobId: 1, userId: 'u1' }], - batched: false, - statusCode: 200, - destination: { - Config: { apiKey: 'dummyApiKey', stream: 'default' }, - Enabled: true, - Transformations: [], - IsProcessorEnabled: true, - }, - }, - { - batchedRequest: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.lytics.io/collect/json/default?access_token=dummyApiKey', - headers: { 'Content-Type': 'application/json' }, - params: {}, - body: { - JSON: { - user_id: 'rudder123', - 'company.id': 'abc123', - createdAt: 'Thu Mar 24 2016 17:46:45 GMT+0000 (UTC)', - email: 'rudderTest@gmail.com', - name: 'Rudder Test', - plan: 'Enterprise', - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - }, - metadata: [{ jobId: 2, userId: 'u1' }], - batched: false, - statusCode: 200, - destination: { - Config: { apiKey: 'dummyApiKey', stream: 'default' }, - Enabled: true, - Transformations: [], - IsProcessorEnabled: true, - }, - }, - ], - }, - }, - }, - }, -]; diff --git a/test/integrations/destinations/posthog/processor/data.ts b/test/integrations/destinations/posthog/processor/data.ts index f33aa268b7..35e12b4aab 100644 --- a/test/integrations/destinations/posthog/processor/data.ts +++ b/test/integrations/destinations/posthog/processor/data.ts @@ -74,7 +74,7 @@ export const data = [ $ip: '0.0.0.0', $timestamp: '2020-11-04T13:21:09.712Z', $anon_distinct_id: 'f3cf54d8-f237-45d2-89f7-ccd70d42cf31', - distinct_id: 'prevId_1', + distinct_id: 'uid-1', $device_manufacturer: 'Xiaomi', $os_version: '8.1.0', $app_version: '1.1.7', @@ -84,7 +84,7 @@ export const data = [ $device_model: 'Redmi 6', $app_namespace: 'com.rudderlabs.javascript', $app_build: '1.0.0', - alias: 'uid-1', + alias: 'prevId_1', }, timestamp: '2020-11-04T13:21:09.712Z', event: '$create_alias', diff --git a/test/integrations/destinations/posthog/router/data.ts b/test/integrations/destinations/posthog/router/data.ts index dab8ba8b1c..20870670dc 100644 --- a/test/integrations/destinations/posthog/router/data.ts +++ b/test/integrations/destinations/posthog/router/data.ts @@ -79,7 +79,7 @@ export const data = [ $ip: '0.0.0.0', $timestamp: '2020-11-04T13:21:09.712Z', $anon_distinct_id: 'f3cf54d8-f237-45d2-89f7-ccd70d42cf31', - distinct_id: 'prevId_1', + distinct_id: 'uid-1', $device_manufacturer: 'Xiaomi', $os_version: '8.1.0', $app_version: '1.1.7', @@ -89,7 +89,7 @@ export const data = [ $device_model: 'Redmi 6', $app_namespace: 'com.rudderlabs.javascript', $app_build: '1.0.0', - alias: 'uid-1', + alias: 'prevId_1', }, timestamp: '2020-11-04T13:21:09.712Z', event: '$create_alias', diff --git a/test/integrations/destinations/singular/processor/data.ts b/test/integrations/destinations/singular/processor/data.ts index 22c075fffc..6e749dd0a0 100644 --- a/test/integrations/destinations/singular/processor/data.ts +++ b/test/integrations/destinations/singular/processor/data.ts @@ -1903,4 +1903,289 @@ export const data = [ }, }, }, + { + name: 'singular', + description: '(Unity) Session Event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + apiKey: 'dummyApiKey', + sessionEventList: [ + { sessionEventName: 'mysessionevent' }, + { sessionEventName: 'randomuser' }, + { sessionEventName: 'titanium' }, + ], + }, + }, + message: { + type: 'track', + event: 'mysessionevent', + userId: 'ruddersampleX3', + request_ip: '14.5.67.21', + context: { + app: { + build: '1', + name: 'RudderAndroidClient', + namespace: 'com.singular.game', + version: '1.1.5.581823alpha', + }, + device: { + manufacturer: 'Google', + model: 'Android SDK built for x86', + name: 'generic_x86', + type: 'android', + advertisingId: '8ecd7512-2864-440c-93f3-a3cabe62525b', + attStatus: true, + id: '49c2d3a6-326e-4ec5-a16b-0a47e34ed953', + adTrackingEnabled: true, + token: 'testDeviceToken', + }, + library: { name: 'com.rudderstack.android.sdk.core', version: '0.1.4' }, + locale: 'en-US', + network: { carrier: 'Android', bluetooth: false, cellular: true, wifi: true }, + campaign: { + source: 'google', + medium: 'medium', + term: 'keyword', + content: 'some content', + }, + os: { name: 'nintendo', version: '360v2-2024h1' }, + screen: { density: 420, height: 1794, width: 1080 }, + timezone: 'Asia/Mumbai', + userAgent: + 'Mozilla/5.0 (Nintendo Switch; WebApplet) AppleWebKit/613.0 (KHTML, like Gecko) NF/6.0.3.25.0 NintendoBrowser/5.1.0.32061', + }, + properties: { + asid: 'IISqwYJKoZIcNqts0jvcNvPc', + url: 'myapp%3A%2F%2Fhome%2Fpage%3Fqueryparam1%3Dvalue1', + install: 'false', + install_source: 'nintendo', + category: 'Games', + affiliation: 'Apple Store', + receipt_signature: '1234dfghnh', + referring_application: '2134dfg', + os: 'nintendo_switch', + data_sharing_options: '%7B%22limit_data_sharing%22%3Atrue%7D', + }, + timestamp: '2024-06-01T11:26:50.000Z', + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'GET', + endpoint: 'https://s2s.singular.net/api/v1/launch', + headers: {}, + params: { + a: 'dummyApiKey', + av: '1.1.5.581823alpha', + data_sharing_options: '%7B%22limit_data_sharing%22%3Atrue%7D', + i: 'com.singular.game', + install: 'false', + install_source: 'nintendo', + ip: '14.5.67.21', + os: 'nintendo_switch', + p: 'nintendo', + sdid: '49c2d3a6-326e-4ec5-a16b-0a47e34ed953', + ua: 'Mozilla/5.0 (Nintendo Switch; WebApplet) AppleWebKit/613.0 (KHTML, like Gecko) NF/6.0.3.25.0 NintendoBrowser/5.1.0.32061', + utime: 1717241210, + ve: '360v2-2024h1', + }, + body: { JSON: {}, JSON_ARRAY: {}, XML: {}, FORM: {} }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'singular', + description: '(Unity) Custom Event with multiple products', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + apiKey: 'dummyApiKey', + sessionEventList: [ + { sessionEventName: 'mysessionevent' }, + { sessionEventName: 'randomuser' }, + { sessionEventName: 'titanium' }, + ], + }, + }, + message: { + type: 'track', + event: 'myevent', + userId: 'ruddersampleX4', + request_ip: '14.5.67.21', + context: { + app: { + build: '1', + name: 'RudderAndroidClient', + namespace: 'com.singular.game', + version: '1.1.5.581823alpha', + }, + device: { + manufacturer: 'Google', + model: 'Android SDK built for x86', + name: 'generic_x86', + type: 'android', + advertisingId: '8ecd7512-2864-440c-93f3-a3cabe62525b', + attStatus: true, + id: '49c2d3a6-326e-4ec5-a16b-0a47e34ed953', + adTrackingEnabled: true, + token: 'testDeviceToken', + }, + library: { name: 'com.rudderstack.android.sdk.core', version: '0.1.4' }, + locale: 'en-US', + network: { carrier: 'Android', bluetooth: false, cellular: true, wifi: true }, + campaign: { + source: 'google', + medium: 'medium', + term: 'keyword', + content: 'some content', + }, + os: { name: 'metaquest', version: 'qst2-2023h2' }, + screen: { density: 420, height: 1794, width: 1080 }, + timezone: 'Asia/Mumbai', + userAgent: + 'Mozilla/5.0 (Nintendo Switch; WebApplet) AppleWebKit/613.0 (KHTML, like Gecko) NF/6.0.3.25.0 NintendoBrowser/5.1.0.32061', + }, + properties: { + asid: 'IISqwYJKoZIcNqts0jvcNvPc', + url: 'myapp%3A%2F%2Fhome%2Fpage%3Fqueryparam1%3Dvalue1', + install: 'SM-G935F', + install_source: 'selfdistributed', + category: 'Games', + checkout_id: '12345', + order_id: '1234', + receipt_signature: '1234dfghnh', + referring_application: '2134dfg', + total: 20, + revenue: 15, + shipping: 22, + tax: 1, + discount: 1.5, + coupon: 'ImagePro', + currency: 'USD', + fetch_token: 'dummyFetchToken', + product_id: '123', + is_revenue_event: true, + os: 'metaquest_pro', + products: [ + { + product_id: '789', + sku: 'G-32', + name: 'Monopoly', + price: 14, + quantity: 2, + category: 'Games', + url: 'https://www.website.com/product/path', + image_url: 'https://www.website.com/product/path.jpg', + }, + { sku: 'F-32', name: 'UNO', price: 3.45, quantity: 2, category: 'Games' }, + ], + }, + timestamp: '2021-09-01T15:46:51.000Z', + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'GET', + endpoint: 'https://s2s.singular.net/api/v1/evt', + headers: {}, + params: { + n: 'myevent', + ip: '14.5.67.21', + av: '1.1.5.581823alpha', + is_revenue_event: true, + i: 'com.singular.game', + utime: 1630511211, + cur: 'USD', + amt: 28, + purchase_product_id: '789', + a: 'dummyApiKey', + install_source: 'selfdistributed', + os: 'metaquest_pro', + p: 'metaquest', + sdid: '49c2d3a6-326e-4ec5-a16b-0a47e34ed953', + ua: 'Mozilla/5.0 (Nintendo Switch; WebApplet) AppleWebKit/613.0 (KHTML, like Gecko) NF/6.0.3.25.0 NintendoBrowser/5.1.0.32061', + ve: 'qst2-2023h2', + install: 'SM-G935F', + }, + body: { JSON: {}, JSON_ARRAY: {}, XML: {}, FORM: {} }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + { + output: { + version: '1', + type: 'REST', + method: 'GET', + endpoint: 'https://s2s.singular.net/api/v1/evt', + headers: {}, + params: { + n: 'myevent', + i: 'com.singular.game', + ip: '14.5.67.21', + is_revenue_event: true, + utime: 1630511211, + cur: 'USD', + purchase_product_id: 'F-32', + install: 'SM-G935F', + install_source: 'selfdistributed', + amt: 6.9, + os: 'metaquest_pro', + p: 'metaquest', + a: 'dummyApiKey', + sdid: '49c2d3a6-326e-4ec5-a16b-0a47e34ed953', + ua: 'Mozilla/5.0 (Nintendo Switch; WebApplet) AppleWebKit/613.0 (KHTML, like Gecko) NF/6.0.3.25.0 NintendoBrowser/5.1.0.32061', + ve: 'qst2-2023h2', + av: '1.1.5.581823alpha', + }, + body: { JSON: {}, JSON_ARRAY: {}, XML: {}, FORM: {} }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/the_trade_desk/router/business.ts b/test/integrations/destinations/the_trade_desk/router/business.ts new file mode 100644 index 0000000000..556e69a909 --- /dev/null +++ b/test/integrations/destinations/the_trade_desk/router/business.ts @@ -0,0 +1,330 @@ +import { defaultMockFns } from '../mocks'; +import { + destType, + advertiserId, + dataProviderId, + segmentName, + sampleDestination, + sampleContext, +} from '../common'; + +export const business = [ + { + id: 'trade_desk-business-test-1', + name: destType, + description: 'Add IDs to the segment', + scenario: 'Framework', + successCriteria: 'Response should contain all the mapping and status code should be 200', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'record', + action: 'insert', + fields: { + DAID: 'test-daid-1', + UID2: 'test-uid2-1', + }, + channel: 'sources', + context: sampleContext, + recordId: '1', + }, + destination: sampleDestination, + metadata: { + jobId: 1, + userId: 'u1', + }, + }, + { + message: { + type: 'record', + action: 'insert', + fields: { + DAID: 'test-daid-2', + UID2: 'test-uid2-2', + }, + channel: 'sources', + context: sampleContext, + recordId: '2', + }, + destination: sampleDestination, + metadata: { + jobId: 2, + userId: 'u1', + }, + }, + ], + destType, + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://sin-data.adsrvr.org/data/advertiser', + headers: {}, + params: {}, + body: { + JSON: { + DataProviderId: dataProviderId, + AdvertiserId: advertiserId, + Items: [ + { + DAID: 'test-daid-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + { + UID2: 'test-uid2-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + { + DAID: 'test-daid-2', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://sin-data.adsrvr.org/data/advertiser', + headers: {}, + params: {}, + body: { + JSON: { + DataProviderId: dataProviderId, + AdvertiserId: advertiserId, + Items: [ + { + UID2: 'test-uid2-2', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + ], + metadata: [ + { + jobId: 1, + userId: 'u1', + }, + { + jobId: 2, + userId: 'u1', + }, + ], + batched: true, + statusCode: 200, + destination: sampleDestination, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'trade_desk-business-test-2', + name: destType, + description: + 'Add/Remove IDs to/from the segment and split into multiple requests based on size', + successCriteria: 'Response should contain all the mapping and status code should be 200', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'record', + action: 'insert', + fields: { + DAID: 'test-daid-1', + UID2: 'test-uid2-1', + }, + channel: 'sources', + context: sampleContext, + recordId: '1', + }, + destination: sampleDestination, + metadata: { + jobId: 1, + userId: 'u1', + }, + }, + { + message: { + type: 'record', + action: 'delete', + fields: { + DAID: 'test-daid-2', + UID2: 'test-uid2-2', + }, + channel: 'sources', + context: sampleContext, + recordId: '2', + }, + destination: sampleDestination, + metadata: { + jobId: 2, + userId: 'u1', + }, + }, + ], + destType, + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://sin-data.adsrvr.org/data/advertiser', + headers: {}, + params: {}, + body: { + JSON: { + DataProviderId: dataProviderId, + AdvertiserId: advertiserId, + Items: [ + { + DAID: 'test-daid-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + { + UID2: 'test-uid2-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + { + DAID: 'test-daid-2', + Data: [ + { + Name: segmentName, + TTLInMinutes: 0, + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://sin-data.adsrvr.org/data/advertiser', + headers: {}, + params: {}, + body: { + JSON: { + DataProviderId: dataProviderId, + AdvertiserId: advertiserId, + Items: [ + { + UID2: 'test-uid2-2', + Data: [ + { + Name: segmentName, + TTLInMinutes: 0, + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + ], + metadata: [ + { + jobId: 1, + userId: 'u1', + }, + { + jobId: 2, + userId: 'u1', + }, + ], + batched: true, + statusCode: 200, + destination: sampleDestination, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, +]; diff --git a/test/integrations/destinations/the_trade_desk/router/data.ts b/test/integrations/destinations/the_trade_desk/router/data.ts index d2dbf9a7cc..6de2069ff4 100644 --- a/test/integrations/destinations/the_trade_desk/router/data.ts +++ b/test/integrations/destinations/the_trade_desk/router/data.ts @@ -1,1064 +1,3 @@ -import { overrideDestination } from '../../../testUtils'; -import { defaultMockFns } from '../mocks'; -import { - destType, - destTypeInUpperCase, - advertiserId, - dataProviderId, - segmentName, - sampleDestination, - sampleContext, -} from '../common'; - -export const data = [ - { - name: destType, - description: 'Add IDs to the segment', - feature: 'router', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - input: [ - { - message: { - type: 'record', - action: 'insert', - fields: { - DAID: 'test-daid-1', - UID2: 'test-uid2-1', - }, - channel: 'sources', - context: sampleContext, - recordId: '1', - }, - destination: sampleDestination, - metadata: { - jobId: 1, - userId: 'u1', - }, - }, - { - message: { - type: 'record', - action: 'insert', - fields: { - DAID: 'test-daid-2', - UID2: 'test-uid2-2', - }, - channel: 'sources', - context: sampleContext, - recordId: '2', - }, - destination: sampleDestination, - metadata: { - jobId: 2, - userId: 'u1', - }, - }, - ], - destType, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: [ - { - batchedRequest: [ - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - body: { - JSON: { - DataProviderId: dataProviderId, - AdvertiserId: advertiserId, - Items: [ - { - DAID: 'test-daid-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - { - UID2: 'test-uid2-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - { - DAID: 'test-daid-2', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - body: { - JSON: { - DataProviderId: dataProviderId, - AdvertiserId: advertiserId, - Items: [ - { - UID2: 'test-uid2-2', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - ], - metadata: [ - { - jobId: 1, - userId: 'u1', - }, - { - jobId: 2, - userId: 'u1', - }, - ], - batched: true, - statusCode: 200, - destination: sampleDestination, - }, - ], - }, - }, - }, - mockFns: defaultMockFns, - }, - { - name: destType, - description: - 'Add/Remove IDs to/from the segment and split into multiple requests based on size', - feature: 'router', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - input: [ - { - message: { - type: 'record', - action: 'insert', - fields: { - DAID: 'test-daid-1', - UID2: 'test-uid2-1', - }, - channel: 'sources', - context: sampleContext, - recordId: '1', - }, - destination: sampleDestination, - metadata: { - jobId: 1, - userId: 'u1', - }, - }, - { - message: { - type: 'record', - action: 'delete', - fields: { - DAID: 'test-daid-2', - UID2: 'test-uid2-2', - }, - channel: 'sources', - context: sampleContext, - recordId: '2', - }, - destination: sampleDestination, - metadata: { - jobId: 2, - userId: 'u1', - }, - }, - ], - destType, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: [ - { - batchedRequest: [ - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - body: { - JSON: { - DataProviderId: dataProviderId, - AdvertiserId: advertiserId, - Items: [ - { - DAID: 'test-daid-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - { - UID2: 'test-uid2-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - { - DAID: 'test-daid-2', - Data: [ - { - Name: segmentName, - TTLInMinutes: 0, - }, - ], - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - body: { - JSON: { - DataProviderId: dataProviderId, - AdvertiserId: advertiserId, - Items: [ - { - UID2: 'test-uid2-2', - Data: [ - { - Name: segmentName, - TTLInMinutes: 0, - }, - ], - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - ], - metadata: [ - { - jobId: 1, - userId: 'u1', - }, - { - jobId: 2, - userId: 'u1', - }, - ], - batched: true, - statusCode: 200, - destination: sampleDestination, - }, - ], - }, - }, - }, - mockFns: defaultMockFns, - }, - { - name: destType, - description: - 'Missing segment name (audienceId) in the config (segment name will be populated from vdm)', - feature: 'router', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - input: [ - { - message: { - type: 'record', - action: 'insert', - fields: { - DAID: 'test-daid-1', - UID2: 'test-uid2-1', - }, - channel: 'sources', - context: sampleContext, - recordId: '1', - }, - destination: overrideDestination(sampleDestination, { audienceId: '' }), - metadata: { - jobId: 1, - userId: 'u1', - }, - }, - { - message: { - type: 'record', - action: 'insert', - fields: { - DAID: 'test-daid-2', - UID2: 'test-uid2-2', - }, - channel: 'sources', - context: sampleContext, - recordId: '2', - }, - destination: overrideDestination(sampleDestination, { audienceId: '' }), - metadata: { - jobId: 2, - userId: 'u1', - }, - }, - ], - destType, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: [ - { - batched: false, - metadata: [ - { jobId: 1, userId: 'u1' }, - { jobId: 2, userId: 'u1' }, - ], - statusCode: 400, - error: 'Segment name/Audience ID is not present. Aborting', - statTags: { - destType: destTypeInUpperCase, - implementation: 'cdkV2', - feature: 'router', - module: 'destination', - errorCategory: 'dataValidation', - errorType: 'configuration', - }, - }, - ], - }, - }, - }, - }, - { - name: destType, - description: 'Missing advertiser ID in the config', - feature: 'router', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - input: [ - { - message: { - type: 'record', - action: 'insert', - fields: { - DAID: 'test-daid-1', - UID2: 'test-uid2-1', - }, - channel: 'sources', - context: sampleContext, - recordId: '1', - }, - destination: overrideDestination(sampleDestination, { advertiserId: '' }), - metadata: { - jobId: 1, - userId: 'u1', - }, - }, - { - message: { - type: 'record', - action: 'insert', - fields: { - DAID: 'test-daid-2', - UID2: 'test-uid2-2', - }, - channel: 'sources', - context: sampleContext, - recordId: '1', - }, - destination: overrideDestination(sampleDestination, { advertiserId: '' }), - metadata: { - jobId: 2, - userId: 'u1', - }, - }, - ], - destType, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: [ - { - batched: false, - metadata: [ - { jobId: 1, userId: 'u1' }, - { jobId: 2, userId: 'u1' }, - ], - statusCode: 400, - error: 'Advertiser ID is not present. Aborting', - statTags: { - destType: destTypeInUpperCase, - implementation: 'cdkV2', - feature: 'router', - module: 'destination', - errorCategory: 'dataValidation', - errorType: 'configuration', - }, - }, - ], - }, - }, - }, - }, - { - name: destType, - description: 'Missing advertiser secret key in the config', - feature: 'router', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - input: [ - { - message: { - type: 'record', - action: 'insert', - fields: { - DAID: 'test-daid-1', - UID2: 'test-uid2-1', - }, - channel: 'sources', - context: sampleContext, - recordId: '1', - }, - destination: overrideDestination(sampleDestination, { advertiserSecretKey: '' }), - metadata: { - jobId: 1, - userId: 'u1', - }, - }, - ], - destType, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: [ - { - batched: false, - metadata: [{ jobId: 1, userId: 'u1' }], - statusCode: 400, - error: 'Advertiser Secret Key is not present. Aborting', - statTags: { - destType: destTypeInUpperCase, - implementation: 'cdkV2', - feature: 'router', - module: 'destination', - errorCategory: 'dataValidation', - errorType: 'configuration', - }, - }, - ], - }, - }, - }, - }, - { - name: destType, - description: 'TTL is out of range', - feature: 'router', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - input: [ - { - message: { - type: 'record', - action: 'insert', - fields: { - DAID: 'test-daid-1', - UID2: 'test-uid2-1', - }, - channel: 'sources', - context: sampleContext, - recordId: '1', - }, - destination: overrideDestination(sampleDestination, { ttlInDays: 190 }), - metadata: { - jobId: 1, - userId: 'u1', - }, - }, - ], - destType, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: [ - { - batched: false, - metadata: [{ jobId: 1, userId: 'u1' }], - statusCode: 400, - error: 'TTL is out of range. Allowed values are 0 to 180 days', - statTags: { - destType: destTypeInUpperCase, - implementation: 'cdkV2', - feature: 'router', - module: 'destination', - errorCategory: 'dataValidation', - errorType: 'configuration', - }, - }, - ], - }, - }, - }, - }, - { - name: destType, - description: 'Invalid action type', - feature: 'router', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - input: [ - { - message: { - type: 'record', - action: 'insert', - fields: { - DAID: 'test-daid-1', - UID2: 'test-uid2-1', - }, - channel: 'sources', - context: sampleContext, - recordId: '1', - }, - destination: sampleDestination, - metadata: { - jobId: 1, - }, - }, - { - message: { - type: 'record', - action: 'update', - fields: { - DAID: 'test-daid-2', - UID2: null, - }, - channel: 'sources', - context: sampleContext, - recordId: '2', - }, - destination: sampleDestination, - metadata: { - jobId: 2, - }, - }, - ], - destType, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: [ - { - batchedRequest: [ - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - body: { - JSON: { - DataProviderId: dataProviderId, - AdvertiserId: advertiserId, - Items: [ - { - DAID: 'test-daid-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - { - UID2: 'test-uid2-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - ], - metadata: [ - { - jobId: 1, - }, - ], - batched: true, - statusCode: 200, - destination: sampleDestination, - }, - { - batched: false, - metadata: [{ jobId: 2 }], - statusCode: 400, - error: - 'Invalid action type. You can only add or remove IDs from the audience/segment', - statTags: { - destType: destTypeInUpperCase, - implementation: 'cdkV2', - feature: 'router', - module: 'destination', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - }, - destination: sampleDestination, - }, - ], - }, - }, - }, - mockFns: defaultMockFns, - }, - { - name: destType, - description: 'Empty fields in the message', - feature: 'router', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - input: [ - { - message: { - type: 'record', - action: 'insert', - fields: {}, - channel: 'sources', - context: sampleContext, - recordId: '1', - }, - destination: sampleDestination, - metadata: { - jobId: 1, - }, - }, - { - message: { - type: 'record', - action: 'insert', - fields: { - DAID: 'test-daid-1', - UID2: 'test-uid2-1', - EUID: 'test-euid-1', - }, - channel: 'sources', - context: sampleContext, - recordId: '2', - }, - destination: sampleDestination, - metadata: { - jobId: 2, - }, - }, - { - message: { - type: 'record', - action: 'insert', - fields: { - DAID: 'test-daid-2', - UID2: null, - EUID: null, - }, - channel: 'sources', - context: sampleContext, - recordId: '3', - }, - destination: sampleDestination, - metadata: { - jobId: 3, - }, - }, - ], - destType, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: [ - { - batchedRequest: [ - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - body: { - JSON: { - DataProviderId: dataProviderId, - AdvertiserId: advertiserId, - Items: [ - { - DAID: 'test-daid-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - { - UID2: 'test-uid2-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - { - EUID: 'test-euid-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - ], - metadata: [ - { - jobId: 2, - }, - ], - batched: true, - statusCode: 200, - destination: sampleDestination, - }, - { - batched: false, - metadata: [{ jobId: 1 }], - statusCode: 400, - error: '`fields` cannot be empty', - statTags: { - destType: destTypeInUpperCase, - implementation: 'cdkV2', - feature: 'router', - module: 'destination', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - }, - destination: sampleDestination, - }, - { - batched: false, - metadata: [{ jobId: 3 }], - statusCode: 400, - error: '`fields` cannot be empty', - statTags: { - destType: destTypeInUpperCase, - implementation: 'cdkV2', - feature: 'router', - module: 'destination', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - }, - destination: sampleDestination, - }, - ], - }, - }, - }, - mockFns: defaultMockFns, - }, - { - name: destType, - description: '`fields` is missing', - feature: 'router', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - input: [ - { - message: { - type: 'record', - action: 'insert', - channel: 'sources', - context: sampleContext, - recordId: '1', - }, - destination: sampleDestination, - metadata: { - jobId: 1, - }, - }, - ], - destType, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: [ - { - batched: false, - metadata: [{ jobId: 1 }], - statusCode: 400, - error: '`fields` cannot be empty', - statTags: { - destType: destTypeInUpperCase, - implementation: 'cdkV2', - feature: 'router', - module: 'destination', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - }, - destination: sampleDestination, - }, - ], - }, - }, - }, - mockFns: defaultMockFns, - }, - { - name: destType, - description: 'Batch call with different event types', - feature: 'router', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - input: [ - { - message: { - type: 'record', - action: 'insert', - fields: { - DAID: 'test-daid-1', - }, - channel: 'sources', - context: sampleContext, - recordId: '1', - }, - destination: sampleDestination, - metadata: { - jobId: 1, - }, - }, - { - message: { - type: 'identify', - context: { - traits: { - name: 'John Doe', - email: 'johndoe@gmail.com', - age: 25, - }, - }, - }, - destination: sampleDestination, - metadata: { - jobId: 2, - }, - }, - ], - destType, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: [ - { - batchedRequest: [ - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - body: { - JSON: { - DataProviderId: dataProviderId, - AdvertiserId: advertiserId, - Items: [ - { - DAID: 'test-daid-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - ], - metadata: [ - { - jobId: 1, - }, - ], - batched: true, - statusCode: 200, - destination: sampleDestination, - }, - { - batched: false, - metadata: [{ jobId: 2 }], - statusCode: 400, - error: 'Event type identify is not supported', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'THE_TRADE_DESK', - module: 'destination', - implementation: 'cdkV2', - feature: 'router', - }, - destination: sampleDestination, - }, - ], - }, - }, - }, - }, -]; +import { business } from './business'; +import { validation } from './validation'; +export const data = [...business, ...validation]; diff --git a/test/integrations/destinations/the_trade_desk/router/validation.ts b/test/integrations/destinations/the_trade_desk/router/validation.ts new file mode 100644 index 0000000000..be7caf079a --- /dev/null +++ b/test/integrations/destinations/the_trade_desk/router/validation.ts @@ -0,0 +1,743 @@ +import { defaultMockFns } from '../mocks'; +import { generateMetadata } from '../../../testUtils'; +import { overrideDestination } from '../../../testUtils'; +import { + destType, + destTypeInUpperCase, + advertiserId, + dataProviderId, + segmentName, + sampleDestination, + sampleContext, +} from '../common'; + +export const validation = [ + { + id: 'trade_desk-validation-test-1', + name: destType, + description: 'Missing advertiser ID in the config', + scenario: 'Framework', + successCriteria: 'Partial Failure: Configuration Error', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'record', + action: 'insert', + fields: { + DAID: 'test-daid-1', + UID2: 'test-uid2-1', + }, + channel: 'sources', + context: sampleContext, + recordId: '1', + }, + destination: overrideDestination(sampleDestination, { advertiserId: '' }), + metadata: generateMetadata(1, 'u1'), + }, + { + message: { + type: 'record', + action: 'insert', + fields: { + DAID: 'test-daid-2', + UID2: 'test-uid2-2', + }, + channel: 'sources', + context: sampleContext, + recordId: '1', + }, + destination: overrideDestination(sampleDestination, { advertiserId: '' }), + metadata: generateMetadata(2, 'u1'), + }, + ], + destType, + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batched: false, + metadata: [generateMetadata(1, 'u1'), generateMetadata(2, 'u1')], + statusCode: 400, + error: 'Advertiser ID is not present. Aborting', + statTags: { + destType: destTypeInUpperCase, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + implementation: 'cdkV2', + feature: 'router', + module: 'destination', + errorCategory: 'dataValidation', + errorType: 'configuration', + }, + }, + ], + }, + }, + }, + }, + { + id: 'trade_desk-validation-test-2', + name: destType, + description: + 'Missing segment name (audienceId) in the config (segment name will be populated from vdm)', + scenario: 'Framework', + successCriteria: 'Partial Failure: Configuration Error', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'record', + action: 'insert', + fields: { + DAID: 'test-daid-1', + UID2: 'test-uid2-1', + }, + channel: 'sources', + context: sampleContext, + recordId: '1', + }, + destination: overrideDestination(sampleDestination, { audienceId: '' }), + metadata: generateMetadata(1, 'u1'), + }, + { + message: { + type: 'record', + action: 'insert', + fields: { + DAID: 'test-daid-2', + UID2: 'test-uid2-2', + }, + channel: 'sources', + context: sampleContext, + recordId: '2', + }, + destination: overrideDestination(sampleDestination, { audienceId: '' }), + metadata: generateMetadata(2, 'u1'), + }, + ], + destType, + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batched: false, + metadata: [generateMetadata(1, 'u1'), generateMetadata(2, 'u1')], + statusCode: 400, + error: 'Segment name/Audience ID is not present. Aborting', + statTags: { + destType: destTypeInUpperCase, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + implementation: 'cdkV2', + feature: 'router', + module: 'destination', + errorCategory: 'dataValidation', + errorType: 'configuration', + }, + }, + ], + }, + }, + }, + }, + { + id: 'trade_desk-validation-test-3', + name: destType, + description: 'Missing advertiser secret key in the config', + scenario: 'Framework', + successCriteria: 'Partial Failure: Configuration Error', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'record', + action: 'insert', + fields: { + DAID: 'test-daid-1', + UID2: 'test-uid2-1', + }, + channel: 'sources', + context: sampleContext, + recordId: '1', + }, + destination: overrideDestination(sampleDestination, { advertiserSecretKey: '' }), + metadata: generateMetadata(1, 'u1'), + }, + ], + destType, + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batched: false, + metadata: [generateMetadata(1, 'u1')], + statusCode: 400, + error: 'Advertiser Secret Key is not present. Aborting', + statTags: { + destType: destTypeInUpperCase, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + implementation: 'cdkV2', + feature: 'router', + module: 'destination', + errorCategory: 'dataValidation', + errorType: 'configuration', + }, + }, + ], + }, + }, + }, + }, + { + id: 'trade_desk-validation-test-4', + name: destType, + description: 'Invalid action type', + scenario: 'Framework', + successCriteria: 'Partial Failure: Instrumentation Error', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'record', + action: 'insert', + fields: { + DAID: 'test-daid-1', + UID2: 'test-uid2-1', + }, + channel: 'sources', + context: sampleContext, + recordId: '1', + }, + destination: sampleDestination, + metadata: generateMetadata(1, 'u1'), + }, + { + message: { + type: 'record', + action: 'update', + fields: { + DAID: 'test-daid-2', + UID2: null, + }, + channel: 'sources', + context: sampleContext, + recordId: '2', + }, + destination: sampleDestination, + metadata: generateMetadata(2, 'u1'), + }, + ], + destType, + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://sin-data.adsrvr.org/data/advertiser', + headers: {}, + params: {}, + body: { + JSON: { + DataProviderId: dataProviderId, + AdvertiserId: advertiserId, + Items: [ + { + DAID: 'test-daid-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + { + UID2: 'test-uid2-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + ], + metadata: [generateMetadata(1, 'u1')], + batched: true, + statusCode: 200, + destination: sampleDestination, + }, + { + batched: false, + metadata: [generateMetadata(2, 'u1')], + statusCode: 400, + error: + 'Invalid action type. You can only add or remove IDs from the audience/segment', + statTags: { + destType: destTypeInUpperCase, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + implementation: 'cdkV2', + feature: 'router', + module: 'destination', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + }, + destination: sampleDestination, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'trade_desk-validation-test-5', + name: destType, + description: 'Invalid action type', + scenario: 'Framework', + successCriteria: 'Partial Failure: Instrumentation Error', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'record', + action: 'insert', + fields: {}, + channel: 'sources', + context: sampleContext, + recordId: '1', + }, + destination: sampleDestination, + metadata: generateMetadata(1, 'u1'), + }, + { + message: { + type: 'record', + action: 'insert', + fields: { + DAID: 'test-daid-1', + UID2: 'test-uid2-1', + EUID: 'test-euid-1', + }, + channel: 'sources', + context: sampleContext, + recordId: '2', + }, + destination: sampleDestination, + metadata: generateMetadata(2, 'u1'), + }, + { + message: { + type: 'record', + action: 'insert', + fields: { + DAID: 'test-daid-2', + UID2: null, + EUID: null, + }, + channel: 'sources', + context: sampleContext, + recordId: '3', + }, + destination: sampleDestination, + metadata: generateMetadata(3, 'u1'), + }, + ], + destType, + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://sin-data.adsrvr.org/data/advertiser', + headers: {}, + params: {}, + body: { + JSON: { + DataProviderId: dataProviderId, + AdvertiserId: advertiserId, + Items: [ + { + DAID: 'test-daid-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + { + UID2: 'test-uid2-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + { + EUID: 'test-euid-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + ], + metadata: [generateMetadata(2, 'u1')], + batched: true, + statusCode: 200, + destination: sampleDestination, + }, + { + batched: false, + metadata: [generateMetadata(1, 'u1')], + statusCode: 400, + error: '`fields` cannot be empty', + statTags: { + destType: destTypeInUpperCase, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + implementation: 'cdkV2', + feature: 'router', + module: 'destination', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + }, + destination: sampleDestination, + }, + { + batched: false, + metadata: [generateMetadata(3, 'u1')], + statusCode: 400, + error: '`fields` cannot be empty', + statTags: { + destType: destTypeInUpperCase, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + implementation: 'cdkV2', + feature: 'router', + module: 'destination', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + }, + destination: sampleDestination, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'trade_desk-validation-test-6', + name: destType, + description: '`fields` is missing', + scenario: 'Framework', + successCriteria: 'Partial Failure: Instrumentation Error', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'record', + action: 'insert', + channel: 'sources', + context: sampleContext, + recordId: '1', + }, + destination: sampleDestination, + metadata: generateMetadata(1, 'u1'), + }, + ], + destType, + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batched: false, + metadata: [generateMetadata(1, 'u1')], + statusCode: 400, + error: '`fields` cannot be empty', + statTags: { + destType: destTypeInUpperCase, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + implementation: 'cdkV2', + feature: 'router', + module: 'destination', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + }, + destination: sampleDestination, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'trade_desk-validation-test-7', + name: destType, + description: 'Batch call with different event types', + scenario: 'Framework', + successCriteria: 'Partial Failure: Instrumentation Error', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'record', + action: 'insert', + fields: { + DAID: 'test-daid-1', + }, + channel: 'sources', + context: sampleContext, + recordId: '1', + }, + destination: sampleDestination, + metadata: generateMetadata(1, 'u1'), + }, + { + message: { + type: 'identify', + context: { + traits: { + name: 'John Doe', + email: 'johndoe@gmail.com', + age: 25, + }, + }, + }, + destination: sampleDestination, + metadata: generateMetadata(2, 'u1'), + }, + ], + destType, + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://sin-data.adsrvr.org/data/advertiser', + headers: {}, + params: {}, + body: { + JSON: { + DataProviderId: dataProviderId, + AdvertiserId: advertiserId, + Items: [ + { + DAID: 'test-daid-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + ], + metadata: [generateMetadata(1, 'u1')], + batched: true, + statusCode: 200, + destination: sampleDestination, + }, + { + batched: false, + metadata: [generateMetadata(2, 'u1')], + statusCode: 400, + error: 'Event type identify is not supported', + statTags: { + errorCategory: 'dataValidation', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + errorType: 'instrumentation', + destType: destTypeInUpperCase, + module: 'destination', + implementation: 'cdkV2', + feature: 'router', + }, + destination: sampleDestination, + }, + ], + }, + }, + }, + }, + { + id: 'trade_desk-validation-test-8', + name: destType, + description: 'TTL is out of range', + scenario: 'Framework', + successCriteria: 'Configurations= Error', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'record', + action: 'insert', + fields: { + DAID: 'test-daid-1', + UID2: 'test-uid2-1', + }, + channel: 'sources', + context: sampleContext, + recordId: '1', + }, + destination: overrideDestination(sampleDestination, { ttlInDays: 190 }), + metadata: { + jobId: 1, + userId: 'u1', + }, + }, + ], + destType, + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batched: false, + metadata: [{ jobId: 1, userId: 'u1' }], + statusCode: 400, + error: 'TTL is out of range. Allowed values are 0 to 180 days', + statTags: { + destType: destTypeInUpperCase, + implementation: 'cdkV2', + feature: 'router', + module: 'destination', + errorCategory: 'dataValidation', + errorType: 'configuration', + }, + }, + ], + }, + }, + }, + }, +]; diff --git a/test/integrations/sources/shopify/constants.ts b/test/integrations/sources/shopify/constants.ts new file mode 100644 index 0000000000..f9df305841 --- /dev/null +++ b/test/integrations/sources/shopify/constants.ts @@ -0,0 +1,155 @@ +export const dummySourceConfig = { + ID: 'dummy-source-id', + OriginalID: '', + Name: 'Shopify v2', + Config: { + disableClientSideIdentifier: false, + eventUpload: false, + version: 'pixel', + }, + Enabled: true, + WorkspaceID: 'dummy-workspace-id', + Destinations: null, + WriteKey: 'dummy-write-key', +}; + +export const dummyBillingAddresses = [ + { + address1: null, + address2: null, + city: null, + country: 'US', + countryCode: 'US', + firstName: null, + lastName: null, + phone: null, + province: null, + provinceCode: null, + zip: null, + }, +]; + +export const dummyContext = { + document: { + location: { + href: 'https://store.myshopify.com/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + hash: '', + host: 'store.myshopify.com', + hostname: 'store.myshopify.com', + origin: 'https://store.myshopify.com', + pathname: '/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + port: '', + protocol: 'https:', + search: '', + }, + referrer: 'https://store.myshopify.com/cart', + characterSet: 'UTF-8', + title: 'Checkout - pixel-testing-rs', + }, + navigator: { + language: 'en-US', + cookieEnabled: true, + languages: ['en-US', 'en'], + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36', + }, + window: { + innerHeight: 1028, + innerWidth: 1362, + outerHeight: 1080, + outerWidth: 1728, + pageXOffset: 0, + pageYOffset: 0, + location: { + href: 'https://store.myshopify.com/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + hash: '', + host: 'store.myshopify.com', + hostname: 'store.myshopify.com', + origin: 'https://store.myshopify.com', + pathname: '/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + port: '', + protocol: 'https:', + search: '', + }, + origin: 'https://store.myshopify.com', + screen: { + height: 1117, + width: 1728, + }, + screenX: 0, + screenY: 37, + scrollX: 0, + scrollY: 0, + }, +}; + +export const responseDummyContext = { + document: { + location: { + href: 'https://store.myshopify.com/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + hash: '', + host: 'store.myshopify.com', + hostname: 'store.myshopify.com', + origin: 'https://store.myshopify.com', + pathname: '/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + port: '', + protocol: 'https:', + search: '', + }, + referrer: 'https://store.myshopify.com/cart', + characterSet: 'UTF-8', + title: 'Checkout - pixel-testing-rs', + }, + navigator: { + language: 'en-US', + cookieEnabled: true, + languages: ['en-US', 'en'], + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36', + }, + window: { + innerHeight: 1028, + innerWidth: 1362, + outerHeight: 1080, + outerWidth: 1728, + pageXOffset: 0, + pageYOffset: 0, + location: { + href: 'https://store.myshopify.com/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + hash: '', + host: 'store.myshopify.com', + hostname: 'store.myshopify.com', + origin: 'https://store.myshopify.com', + pathname: '/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + port: '', + protocol: 'https:', + search: '', + }, + origin: 'https://store.myshopify.com', + screen: { + height: 1117, + width: 1728, + }, + screenX: 0, + screenY: 37, + scrollX: 0, + scrollY: 0, + }, + page: { + title: 'Checkout - pixel-testing-rs', + url: 'https://store.myshopify.com/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + path: '/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + search: '', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36', + screen: { + height: 1117, + width: 1728, + }, + library: { + name: 'RudderStack Shopify Cloud', + eventOrigin: 'client', + version: '2.0.0', + }, +}; diff --git a/test/integrations/sources/shopify/data.ts b/test/integrations/sources/shopify/data.ts index f5eb3c148b..a2b27cbbcc 100644 --- a/test/integrations/sources/shopify/data.ts +++ b/test/integrations/sources/shopify/data.ts @@ -1,6 +1,10 @@ import { skip } from 'node:test'; +import { pixelCheckoutEventsTestScenarios } from './pixelTestScenarios/CheckoutEventsTests'; +import { pixelCheckoutStepsScenarios } from './pixelTestScenarios/CheckoutStepsTests'; +import { pixelEventsTestScenarios } from './pixelTestScenarios/ProductEventsTests'; +import { v1ServerSideEventsScenarios } from './v1ServerSideEventsTests'; -export const data = [ +const serverSideEventsScenarios = [ { name: 'shopify', description: 'Track Call -> carts_create ', @@ -606,4 +610,833 @@ export const data = [ }, }, }, + { + name: 'shopify', + description: 'Track Call -> Order Partially Fulfilled event', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + query_parameters: { + topic: ['orders_partially_fulfilled'], + writeKey: ['sample-write-key'], + signature: ['rudderstack'], + }, + id: 820982911946154508, + admin_graphql_api_id: 'gid://shopify/Order/820982911946154508', + app_id: null, + browser_ip: null, + buyer_accepts_marketing: true, + cancel_reason: 'customer', + cancelled_at: '2021-12-31T19:00:00-05:00', + cart_token: null, + checkout_id: null, + checkout_token: null, + client_details: null, + closed_at: null, + confirmation_number: null, + confirmed: false, + contact_email: 'jon@example.com', + created_at: '2021-12-31T19:00:00-05:00', + currency: 'USD', + current_subtotal_price: '398.00', + current_subtotal_price_set: { + shop_money: { + amount: '398.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '398.00', + currency_code: 'USD', + }, + }, + current_total_additional_fees_set: null, + current_total_discounts: '0.00', + current_total_discounts_set: { + shop_money: { + amount: '0.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '0.00', + currency_code: 'USD', + }, + }, + current_total_duties_set: null, + current_total_price: '398.00', + current_total_price_set: { + shop_money: { + amount: '398.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '398.00', + currency_code: 'USD', + }, + }, + current_total_tax: '0.00', + current_total_tax_set: { + shop_money: { + amount: '0.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '0.00', + currency_code: 'USD', + }, + }, + customer_locale: 'en', + device_id: null, + discount_codes: [], + email: 'jon@example.com', + estimated_taxes: false, + financial_status: 'voided', + fulfillment_status: 'pending', + landing_site: null, + landing_site_ref: null, + location_id: null, + merchant_of_record_app_id: null, + name: '#9999', + note: null, + note_attributes: [], + number: 234, + order_number: 1234, + order_status_url: + 'https://jsmith.myshopify.com/548380009/orders/123456abcd/authenticate?key=abcdefg', + original_total_additional_fees_set: null, + original_total_duties_set: null, + payment_gateway_names: ['visa', 'bogus'], + phone: null, + po_number: null, + presentment_currency: 'USD', + processed_at: '2021-12-31T19:00:00-05:00', + reference: null, + referring_site: null, + source_identifier: null, + source_name: 'web', + source_url: null, + subtotal_price: '388.00', + subtotal_price_set: { + shop_money: { + amount: '388.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '388.00', + currency_code: 'USD', + }, + }, + tags: 'tag1, tag2', + tax_exempt: false, + tax_lines: [], + taxes_included: false, + test: true, + token: '123456abcd', + total_discounts: '20.00', + total_discounts_set: { + shop_money: { + amount: '20.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '20.00', + currency_code: 'USD', + }, + }, + total_line_items_price: '398.00', + total_line_items_price_set: { + shop_money: { + amount: '398.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '398.00', + currency_code: 'USD', + }, + }, + total_outstanding: '398.00', + total_price: '388.00', + total_price_set: { + shop_money: { + amount: '388.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '388.00', + currency_code: 'USD', + }, + }, + total_shipping_price_set: { + shop_money: { + amount: '10.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '10.00', + currency_code: 'USD', + }, + }, + total_tax: '0.00', + total_tax_set: { + shop_money: { + amount: '0.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '0.00', + currency_code: 'USD', + }, + }, + total_tip_received: '0.00', + total_weight: 0, + updated_at: '2021-12-31T19:00:00-05:00', + user_id: null, + billing_address: { + first_name: 'Steve', + address1: '123 Shipping Street', + phone: '555-555-SHIP', + city: 'Shippington', + zip: '40003', + province: 'Kentucky', + country: 'United States', + last_name: 'Shipper', + address2: null, + company: 'Shipping Company', + latitude: null, + longitude: null, + name: 'Steve Shipper', + country_code: 'US', + province_code: 'KY', + }, + customer: { + id: 115310627314723954, + email: 'john@example.com', + created_at: null, + updated_at: null, + first_name: 'John', + last_name: 'Smith', + state: 'disabled', + note: null, + verified_email: true, + multipass_identifier: null, + tax_exempt: false, + phone: null, + email_marketing_consent: { + state: 'not_subscribed', + opt_in_level: null, + consent_updated_at: null, + }, + sms_marketing_consent: null, + tags: '', + currency: 'USD', + tax_exemptions: [], + admin_graphql_api_id: 'gid://shopify/Customer/115310627314723954', + default_address: { + id: 715243470612851245, + customer_id: 115310627314723954, + first_name: null, + last_name: null, + company: null, + address1: '123 Elm St.', + address2: null, + city: 'Ottawa', + province: 'Ontario', + country: 'Canada', + zip: 'K2H7A8', + phone: '123-123-1234', + name: '', + province_code: 'ON', + country_code: 'CA', + country_name: 'Canada', + default: true, + }, + }, + discount_applications: [], + fulfillments: [], + line_items: [ + { + id: 866550311766439020, + admin_graphql_api_id: 'gid://shopify/LineItem/866550311766439020', + attributed_staffs: [ + { + id: 'gid://shopify/StaffMember/902541635', + quantity: 1, + }, + ], + current_quantity: 1, + fulfillable_quantity: 1, + fulfillment_service: 'manual', + fulfillment_status: null, + gift_card: false, + grams: 567, + name: 'IPod Nano - 8GB', + price: '199.00', + price_set: { + shop_money: { + amount: '199.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '199.00', + currency_code: 'USD', + }, + }, + product_exists: true, + product_id: 632910392, + properties: [], + quantity: 1, + requires_shipping: true, + sku: 'IPOD2008PINK', + taxable: true, + title: 'IPod Nano - 8GB', + total_discount: '0.00', + total_discount_set: { + shop_money: { + amount: '0.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '0.00', + currency_code: 'USD', + }, + }, + variant_id: 808950810, + variant_inventory_management: 'shopify', + variant_title: null, + vendor: null, + tax_lines: [], + duties: [], + discount_allocations: [], + }, + { + id: 141249953214522974, + admin_graphql_api_id: 'gid://shopify/LineItem/141249953214522974', + attributed_staffs: [], + current_quantity: 1, + fulfillable_quantity: 1, + fulfillment_service: 'manual', + fulfillment_status: null, + gift_card: false, + grams: 567, + name: 'IPod Nano - 8GB', + price: '199.00', + price_set: { + shop_money: { + amount: '199.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '199.00', + currency_code: 'USD', + }, + }, + product_exists: true, + product_id: 632910392, + properties: [], + quantity: 1, + requires_shipping: true, + sku: 'IPOD2008PINK', + taxable: true, + title: 'IPod Nano - 8GB', + total_discount: '0.00', + total_discount_set: { + shop_money: { + amount: '0.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '0.00', + currency_code: 'USD', + }, + }, + variant_id: 808950810, + variant_inventory_management: 'shopify', + variant_title: null, + vendor: null, + tax_lines: [], + duties: [], + discount_allocations: [], + }, + ], + payment_terms: null, + refunds: [], + shipping_address: { + first_name: 'Steve', + address1: '123 Shipping Street', + phone: '555-555-SHIP', + city: 'Shippington', + zip: '40003', + province: 'Kentucky', + country: 'United States', + last_name: 'Shipper', + address2: null, + company: 'Shipping Company', + latitude: null, + longitude: null, + name: 'Steve Shipper', + country_code: 'US', + province_code: 'KY', + }, + shipping_lines: [ + { + id: 271878346596884015, + carrier_identifier: null, + code: null, + discounted_price: '10.00', + discounted_price_set: { + shop_money: { + amount: '10.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '10.00', + currency_code: 'USD', + }, + }, + is_removed: false, + phone: null, + price: '10.00', + price_set: { + shop_money: { + amount: '10.00', + currency_code: 'USD', + }, + presentment_money: { + amount: '10.00', + currency_code: 'USD', + }, + }, + requested_fulfillment_service_id: null, + source: 'shopify', + title: 'Generic Shipping', + tax_lines: [], + discount_allocations: [], + }, + ], + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + cart_token: null, + checkout_token: null, + integration: { + name: 'SHOPIFY', + }, + library: { + name: 'RudderStack Shopify Cloud', + version: '1.0.0', + }, + topic: 'orders_partially_fulfilled', + }, + event: 'Order Partially Fulfilled', + integrations: { + SHOPIFY: true, + }, + properties: { + admin_graphql_api_id: 'gid://shopify/Order/820982911946154508', + app_id: null, + browser_ip: null, + buyer_accepts_marketing: true, + cancel_reason: 'customer', + cancelled_at: '2021-12-31T19:00:00-05:00', + cart_token: null, + checkout_id: null, + checkout_token: null, + client_details: null, + closed_at: null, + confirmation_number: null, + confirmed: false, + contact_email: 'jon@example.com', + created_at: '2021-12-31T19:00:00-05:00', + currency: 'USD', + current_subtotal_price: '398.00', + current_subtotal_price_set: { + presentment_money: { + amount: '398.00', + currency_code: 'USD', + }, + shop_money: { + amount: '398.00', + currency_code: 'USD', + }, + }, + current_total_additional_fees_set: null, + current_total_discounts: '0.00', + current_total_discounts_set: { + presentment_money: { + amount: '0.00', + currency_code: 'USD', + }, + shop_money: { + amount: '0.00', + currency_code: 'USD', + }, + }, + current_total_duties_set: null, + current_total_price: '398.00', + current_total_price_set: { + presentment_money: { + amount: '398.00', + currency_code: 'USD', + }, + shop_money: { + amount: '398.00', + currency_code: 'USD', + }, + }, + current_total_tax: '0.00', + current_total_tax_set: { + presentment_money: { + amount: '0.00', + currency_code: 'USD', + }, + shop_money: { + amount: '0.00', + currency_code: 'USD', + }, + }, + customer_locale: 'en', + device_id: null, + discount_applications: [], + discount_codes: [], + email: 'jon@example.com', + estimated_taxes: false, + financial_status: 'voided', + fulfillment_status: 'pending', + fulfillments: [], + id: 820982911946154500, + landing_site: null, + landing_site_ref: null, + location_id: null, + merchant_of_record_app_id: null, + name: '#9999', + note: null, + note_attributes: [], + number: 234, + order_number: 1234, + order_status_url: + 'https://jsmith.myshopify.com/548380009/orders/123456abcd/authenticate?key=abcdefg', + original_total_additional_fees_set: null, + original_total_duties_set: null, + payment_gateway_names: ['visa', 'bogus'], + payment_terms: null, + phone: null, + po_number: null, + presentment_currency: 'USD', + processed_at: '2021-12-31T19:00:00-05:00', + products: [ + { + admin_graphql_api_id: 'gid://shopify/LineItem/866550311766439020', + attributed_staffs: [ + { + id: 'gid://shopify/StaffMember/902541635', + quantity: 1, + }, + ], + current_quantity: 1, + discount_allocations: [], + duties: [], + fulfillable_quantity: 1, + fulfillment_service: 'manual', + fulfillment_status: null, + gift_card: false, + grams: 567, + id: 866550311766439000, + price: '199.00', + price_set: { + presentment_money: { + amount: '199.00', + currency_code: 'USD', + }, + shop_money: { + amount: '199.00', + currency_code: 'USD', + }, + }, + product_exists: true, + product_id: 632910392, + properties: [], + quantity: 1, + requires_shipping: true, + sku: 'IPOD2008PINK', + tax_lines: [], + taxable: true, + title: 'IPod Nano - 8GB', + total_discount: '0.00', + total_discount_set: { + presentment_money: { + amount: '0.00', + currency_code: 'USD', + }, + shop_money: { + amount: '0.00', + currency_code: 'USD', + }, + }, + variant: '808950810 ', + variant_inventory_management: 'shopify', + }, + { + admin_graphql_api_id: 'gid://shopify/LineItem/141249953214522974', + attributed_staffs: [], + current_quantity: 1, + discount_allocations: [], + duties: [], + fulfillable_quantity: 1, + fulfillment_service: 'manual', + fulfillment_status: null, + gift_card: false, + grams: 567, + id: 141249953214522980, + price: '199.00', + price_set: { + presentment_money: { + amount: '199.00', + currency_code: 'USD', + }, + shop_money: { + amount: '199.00', + currency_code: 'USD', + }, + }, + product_exists: true, + product_id: 632910392, + properties: [], + quantity: 1, + requires_shipping: true, + sku: 'IPOD2008PINK', + tax_lines: [], + taxable: true, + title: 'IPod Nano - 8GB', + total_discount: '0.00', + total_discount_set: { + presentment_money: { + amount: '0.00', + currency_code: 'USD', + }, + shop_money: { + amount: '0.00', + currency_code: 'USD', + }, + }, + variant: '808950810 ', + variant_inventory_management: 'shopify', + }, + ], + reference: null, + referring_site: null, + refunds: [], + shipping_lines: [ + { + carrier_identifier: null, + code: null, + discount_allocations: [], + discounted_price: '10.00', + discounted_price_set: { + presentment_money: { + amount: '10.00', + currency_code: 'USD', + }, + shop_money: { + amount: '10.00', + currency_code: 'USD', + }, + }, + id: 271878346596884000, + is_removed: false, + phone: null, + price: '10.00', + price_set: { + presentment_money: { + amount: '10.00', + currency_code: 'USD', + }, + shop_money: { + amount: '10.00', + currency_code: 'USD', + }, + }, + requested_fulfillment_service_id: null, + source: 'shopify', + tax_lines: [], + title: 'Generic Shipping', + }, + ], + source_identifier: null, + source_name: 'web', + source_url: null, + subtotal_price: '388.00', + subtotal_price_set: { + presentment_money: { + amount: '388.00', + currency_code: 'USD', + }, + shop_money: { + amount: '388.00', + currency_code: 'USD', + }, + }, + tags: 'tag1, tag2', + tax_exempt: false, + tax_lines: [], + taxes_included: false, + test: true, + token: '123456abcd', + total_discounts: '20.00', + total_discounts_set: { + presentment_money: { + amount: '20.00', + currency_code: 'USD', + }, + shop_money: { + amount: '20.00', + currency_code: 'USD', + }, + }, + total_line_items_price: '398.00', + total_line_items_price_set: { + presentment_money: { + amount: '398.00', + currency_code: 'USD', + }, + shop_money: { + amount: '398.00', + currency_code: 'USD', + }, + }, + total_outstanding: '398.00', + total_price: '388.00', + total_price_set: { + presentment_money: { + amount: '388.00', + currency_code: 'USD', + }, + shop_money: { + amount: '388.00', + currency_code: 'USD', + }, + }, + total_shipping_price_set: { + presentment_money: { + amount: '10.00', + currency_code: 'USD', + }, + shop_money: { + amount: '10.00', + currency_code: 'USD', + }, + }, + total_tax: '0.00', + total_tax_set: { + presentment_money: { + amount: '0.00', + currency_code: 'USD', + }, + shop_money: { + amount: '0.00', + currency_code: 'USD', + }, + }, + total_tip_received: '0.00', + total_weight: 0, + updated_at: '2021-12-31T19:00:00-05:00', + user_id: null, + }, + traits: { + address: { + address1: '123 Elm St.', + address2: null, + city: 'Ottawa', + company: null, + country: 'Canada', + country_code: 'CA', + country_name: 'Canada', + customer_id: 115310627314723950, + default: true, + first_name: null, + id: 715243470612851200, + last_name: null, + name: '', + phone: '123-123-1234', + province: 'Ontario', + province_code: 'ON', + zip: 'K2H7A8', + }, + adminGraphqlApiId: 'gid://shopify/Customer/115310627314723954', + billingAddress: { + address1: '123 Shipping Street', + address2: null, + city: 'Shippington', + company: 'Shipping Company', + country: 'United States', + country_code: 'US', + first_name: 'Steve', + last_name: 'Shipper', + latitude: null, + longitude: null, + name: 'Steve Shipper', + phone: '555-555-SHIP', + province: 'Kentucky', + province_code: 'KY', + zip: '40003', + }, + currency: 'USD', + email: 'john@example.com', + firstName: 'John', + lastName: 'Smith', + shippingAddress: { + address1: '123 Shipping Street', + address2: null, + city: 'Shippington', + company: 'Shipping Company', + country: 'United States', + country_code: 'US', + first_name: 'Steve', + last_name: 'Shipper', + latitude: null, + longitude: null, + name: 'Steve Shipper', + phone: '555-555-SHIP', + province: 'Kentucky', + province_code: 'KY', + zip: '40003', + }, + state: 'disabled', + tags: '', + taxExempt: false, + taxExemptions: [], + verifiedEmail: true, + }, + type: 'track', + userId: '115310627314723950', + }, + ], + }, + }, + ], + }, + }, + }, +]; + +export const data = [ + ...pixelCheckoutEventsTestScenarios, + ...pixelCheckoutStepsScenarios, + ...pixelEventsTestScenarios, + ...serverSideEventsScenarios, + ...v1ServerSideEventsScenarios, ]; diff --git a/test/integrations/sources/shopify/pixelTestScenarios/CheckoutEventsTests.ts b/test/integrations/sources/shopify/pixelTestScenarios/CheckoutEventsTests.ts new file mode 100644 index 0000000000..1009fa2d04 --- /dev/null +++ b/test/integrations/sources/shopify/pixelTestScenarios/CheckoutEventsTests.ts @@ -0,0 +1,570 @@ +// This file contains the test scenarios for the pixel checkout events +import { dummySourceConfig, dummyBillingAddresses, dummyContext } from '../constants'; + +export const pixelCheckoutEventsTestScenarios = [ + { + name: 'shopify', + description: 'Track Call -> checkout_started event from web pixel', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 'sh-f77a78f1-C1D8-4ED4-9C9B-0D352CF6F3BF', + name: 'checkout_started', + data: { + checkout: { + buyerAcceptsEmailMarketing: false, + buyerAcceptsSmsMarketing: false, + attributes: [], + billingAddress: dummyBillingAddresses[0], + token: '5f7028e0bd5225c17b24bdaa0c09f914', + currencyCode: 'USD', + discountApplications: [], + discountsAmount: { + amount: 0, + currencyCode: 'USD', + }, + email: '', + phone: '', + lineItems: [ + { + discountAllocations: [], + id: '41327143321713', + quantity: 2, + title: 'The Collection Snowboard: Liquid', + variant: { + id: '41327143321713', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6_64x64.jpg?v=1724736600', + }, + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + id: '7234590834801', + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + type: 'snowboard', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid', + }, + sku: null, + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1499.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + ], + localization: { + country: { + isoCode: 'US', + }, + language: { + isoCode: 'en-US', + }, + market: { + id: 'gid://shopify/Market/23505895537', + handle: 'us', + }, + }, + order: { + id: null, + customer: { + id: null, + isFirstOrder: null, + }, + }, + delivery: { + selectedDeliveryOptions: [], + }, + shippingAddress: dummyBillingAddresses[0], + subtotalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + shippingLine: { + price: { + amount: 0, + currencyCode: 'USD', + }, + }, + smsMarketingPhone: null, + totalTax: { + amount: 0, + currencyCode: 'USD', + }, + totalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + transactions: [], + }, + }, + type: 'standard', + clientId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + timestamp: '2024-09-15T20:57:59.674Z', + context: dummyContext, + pixelEventLabel: true, + query_parameters: { + topic: ['checkouts_update'], + writeKey: ['dummy-write-key'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + document: { + location: { + href: 'https://store.myshopify.com/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + hash: '', + host: 'store.myshopify.com', + hostname: 'store.myshopify.com', + origin: 'https://store.myshopify.com', + pathname: + '/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + port: '', + protocol: 'https:', + search: '', + }, + referrer: 'https://store.myshopify.com/cart', + characterSet: 'UTF-8', + title: 'Checkout - pixel-testing-rs', + }, + navigator: { + language: 'en-US', + cookieEnabled: true, + languages: ['en-US', 'en'], + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36', + }, + window: { + innerHeight: 1028, + innerWidth: 1362, + outerHeight: 1080, + outerWidth: 1728, + pageXOffset: 0, + pageYOffset: 0, + location: { + href: 'https://store.myshopify.com/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + hash: '', + host: 'store.myshopify.com', + hostname: 'store.myshopify.com', + origin: 'https://store.myshopify.com', + pathname: + '/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + port: '', + protocol: 'https:', + search: '', + }, + origin: 'https://store.myshopify.com', + screen: { + height: 1117, + width: 1728, + }, + screenX: 0, + screenY: 37, + scrollX: 0, + scrollY: 0, + }, + page: { + title: 'Checkout - pixel-testing-rs', + url: 'https://store.myshopify.com/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + path: '/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + search: '', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36', + screen: { + height: 1117, + width: 1728, + }, + library: { + name: 'RudderStack Shopify Cloud', + eventOrigin: 'client', + version: '2.0.0', + }, + topic: 'checkout_started', + }, + integrations: { + SHOPIFY: true, + }, + type: 'track', + event: 'Checkout Started', + properties: { + products: [ + { + discountAllocations: [], + id: '41327143321713', + quantity: 2, + title: 'The Collection Snowboard: Liquid', + variant: 'The Collection Snowboard: Liquid', + finalLinePrice: { + amount: 1499.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + name: 'The Collection Snowboard: Liquid', + image_url: + 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6_64x64.jpg?v=1724736600', + price: 749.95, + sku: null, + product_id: '7234590834801', + category: 'snowboard', + url: '/products/the-collection-snowboard-liquid', + brand: 'Hydrogen Vendor', + }, + ], + order_id: 'sh-f77a78f1-C1D8-4ED4-9C9B-0D352CF6F3BF', + checkout_id: '5f7028e0bd5225c17b24bdaa0c09f914', + total: 2759.8, + currency: 'USD', + discount: 0, + shipping: 0, + revenue: 2759.8, + value: 2759.8, + tax: 0, + }, + anonymousId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + }, + ], + }, + }, + ], + }, + }, + }, + { + name: 'shopify', + description: 'Track Call -> checkout_completed event from web pixel', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 'sh-f77a78f1-C1D8-4ED4-9C9B-0D352CF6F3BF', + name: 'checkout_completed', + data: { + checkout: { + buyerAcceptsEmailMarketing: false, + buyerAcceptsSmsMarketing: false, + attributes: [], + billingAddress: dummyBillingAddresses[0], + token: '5f7028e0bd5225c17b24bdaa0c09f914', + currencyCode: 'USD', + discountApplications: [], + discountsAmount: { + amount: 0, + currencyCode: 'USD', + }, + email: '', + phone: '', + lineItems: [ + { + discountAllocations: [], + id: '41327143321713', + quantity: 2, + title: 'The Collection Snowboard: Liquid', + variant: { + id: '41327143321713', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6_64x64.jpg?v=1724736600', + }, + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + id: '7234590834801', + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + type: 'snowboard', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid', + }, + sku: null, + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1499.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + { + discountAllocations: [], + id: '41327143157873', + quantity: 2, + title: 'The Multi-managed Snowboard', + variant: { + id: '41327143157873', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_9129b69a-0c7b-4f66-b6cf-c4222f18028a_64x64.jpg?v=1724736597', + }, + price: { + amount: 629.95, + currencyCode: 'USD', + }, + product: { + id: '7234590736497', + title: 'The Multi-managed Snowboard', + vendor: 'Multi-managed Vendor', + type: 'snowboard', + untranslatedTitle: 'The Multi-managed Snowboard', + url: '/products/the-multi-managed-snowboard', + }, + sku: 'sku-managed-1', + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1259.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + ], + localization: { + country: { + isoCode: 'US', + }, + language: { + isoCode: 'en-US', + }, + market: { + id: 'gid://shopify/Market/23505895537', + handle: 'us', + }, + }, + order: { + id: null, + customer: { + id: null, + isFirstOrder: null, + }, + }, + delivery: { + selectedDeliveryOptions: [], + }, + shippingAddress: dummyBillingAddresses[0], + subtotalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + shippingLine: { + price: { + amount: 0, + currencyCode: 'USD', + }, + }, + smsMarketingPhone: null, + totalTax: { + amount: 0, + currencyCode: 'USD', + }, + totalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + transactions: [], + }, + }, + type: 'standard', + clientId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + timestamp: '2024-09-15T20:57:59.674Z', + context: dummyContext, + pixelEventLabel: true, + query_parameters: { + topic: ['checkouts_update'], + writeKey: ['dummy-write-key'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + document: { + location: { + href: 'https://store.myshopify.com/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + hash: '', + host: 'store.myshopify.com', + hostname: 'store.myshopify.com', + origin: 'https://store.myshopify.com', + pathname: + '/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + port: '', + protocol: 'https:', + search: '', + }, + referrer: 'https://store.myshopify.com/cart', + characterSet: 'UTF-8', + title: 'Checkout - pixel-testing-rs', + }, + navigator: { + language: 'en-US', + cookieEnabled: true, + languages: ['en-US', 'en'], + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36', + }, + window: { + innerHeight: 1028, + innerWidth: 1362, + outerHeight: 1080, + outerWidth: 1728, + pageXOffset: 0, + pageYOffset: 0, + location: { + href: 'https://store.myshopify.com/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + hash: '', + host: 'store.myshopify.com', + hostname: 'store.myshopify.com', + origin: 'https://store.myshopify.com', + pathname: + '/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + port: '', + protocol: 'https:', + search: '', + }, + origin: 'https://store.myshopify.com', + screen: { + height: 1117, + width: 1728, + }, + screenX: 0, + screenY: 37, + scrollX: 0, + scrollY: 0, + }, + page: { + title: 'Checkout - pixel-testing-rs', + url: 'https://store.myshopify.com/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + path: '/checkouts/cn/Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + search: '', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36', + screen: { + height: 1117, + width: 1728, + }, + library: { + name: 'RudderStack Shopify Cloud', + eventOrigin: 'client', + version: '2.0.0', + }, + topic: 'checkout_completed', + }, + integrations: { + SHOPIFY: true, + }, + type: 'track', + event: 'Checkout Completed', + properties: { + products: [ + { + discountAllocations: [], + id: '41327143321713', + quantity: 2, + title: 'The Collection Snowboard: Liquid', + variant: 'The Collection Snowboard: Liquid', + finalLinePrice: { + amount: 1499.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + name: 'The Collection Snowboard: Liquid', + image_url: + 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6_64x64.jpg?v=1724736600', + price: 749.95, + sku: null, + product_id: '7234590834801', + category: 'snowboard', + url: '/products/the-collection-snowboard-liquid', + brand: 'Hydrogen Vendor', + }, + { + discountAllocations: [], + id: '41327143157873', + quantity: 2, + title: 'The Multi-managed Snowboard', + variant: 'The Multi-managed Snowboard', + finalLinePrice: { + amount: 1259.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + name: 'The Multi-managed Snowboard', + image_url: + 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_9129b69a-0c7b-4f66-b6cf-c4222f18028a_64x64.jpg?v=1724736597', + price: 629.95, + sku: 'sku-managed-1', + product_id: '7234590736497', + category: 'snowboard', + url: '/products/the-multi-managed-snowboard', + brand: 'Multi-managed Vendor', + }, + ], + order_id: 'sh-f77a78f1-C1D8-4ED4-9C9B-0D352CF6F3BF', + checkout_id: '5f7028e0bd5225c17b24bdaa0c09f914', + total: 2759.8, + currency: 'USD', + discount: 0, + shipping: 0, + revenue: 2759.8, + value: 2759.8, + tax: 0, + }, + anonymousId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + }, + ], + }, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/sources/shopify/pixelTestScenarios/CheckoutStepsTests.ts b/test/integrations/sources/shopify/pixelTestScenarios/CheckoutStepsTests.ts new file mode 100644 index 0000000000..9d845c1dde --- /dev/null +++ b/test/integrations/sources/shopify/pixelTestScenarios/CheckoutStepsTests.ts @@ -0,0 +1,1517 @@ +import { dummySourceConfig, dummyContext, responseDummyContext } from '../constants'; + +export const pixelCheckoutStepsScenarios = [ + { + name: 'shopify', + description: 'Track Call -> address_info_submitted event from web pixel', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 'sh-f7d2154d-7525-47A4-87FA-E54D2322E129', + name: 'checkout_address_info_submitted', + data: { + checkout: { + buyerAcceptsEmailMarketing: false, + buyerAcceptsSmsMarketing: false, + attributes: [], + billingAddress: { + address1: null, + address2: null, + city: null, + country: 'US', + countryCode: 'US', + firstName: null, + lastName: null, + phone: null, + province: null, + provinceCode: null, + zip: null, + }, + token: '5f7028e0bd5225c17b24bdaa0c09f914', + currencyCode: 'USD', + discountApplications: [], + discountsAmount: { + amount: 0, + currencyCode: 'USD', + }, + email: 'test-user@sampleemail.com', + phone: '', + lineItems: [ + { + discountAllocations: [], + id: '41327143321713', + quantity: 2, + title: 'The Collection Snowboard: Liquid', + variant: { + id: '41327143321713', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6_64x64.jpg?v=1724736600', + }, + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + id: '7234590834801', + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + type: 'snowboard', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid', + }, + sku: null, + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1499.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + { + discountAllocations: [], + id: '41327143157873', + quantity: 2, + title: 'The Multi-managed Snowboard', + variant: { + id: '41327143157873', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_9129b69a-0c7b-4f66-b6cf-c4222f18028a_64x64.jpg?v=1724736597', + }, + price: { + amount: 629.95, + currencyCode: 'USD', + }, + product: { + id: '7234590736497', + title: 'The Multi-managed Snowboard', + vendor: 'Multi-managed Vendor', + type: 'snowboard', + untranslatedTitle: 'The Multi-managed Snowboard', + url: '/products/the-multi-managed-snowboard', + }, + sku: 'sku-managed-1', + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1259.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + ], + localization: { + country: { + isoCode: 'US', + }, + language: { + isoCode: 'en-US', + }, + market: { + id: 'gid://shopify/Market/23505895537', + handle: 'us', + }, + }, + order: { + id: null, + customer: { + id: null, + isFirstOrder: null, + }, + }, + delivery: { + selectedDeliveryOptions: [ + { + cost: { + amount: 0, + currencyCode: 'USD', + }, + costAfterDiscounts: { + amount: 0, + currencyCode: 'USD', + }, + description: null, + handle: '5f7028e0bd5225c17b24bdaa0c09f914-8388085074acab7e91de633521be86f0', + title: 'Economy', + type: 'shipping', + }, + ], + }, + shippingAddress: { + address1: 'Queens Center', + address2: null, + city: 'Elmhurst', + country: 'US', + countryCode: 'US', + firstName: 'test', + lastName: 'user', + phone: null, + province: 'NY', + provinceCode: 'NY', + zip: '11373', + }, + subtotalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + shippingLine: { + price: { + amount: 0, + currencyCode: 'USD', + }, + }, + smsMarketingPhone: null, + totalTax: { + amount: 0, + currencyCode: 'USD', + }, + totalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + transactions: [], + }, + }, + type: 'standard', + clientId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + timestamp: '2024-09-15T21:45:50.523Z', + context: dummyContext, + pixelEventLabel: true, + query_parameters: { + topic: ['checkouts_update'], + writeKey: ['dummy-write-key'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { ...responseDummyContext, topic: 'checkout_address_info_submitted' }, + integrations: { + SHOPIFY: true, + }, + type: 'track', + event: 'Checkout Address Info Submitted', + properties: { + buyerAcceptsEmailMarketing: false, + buyerAcceptsSmsMarketing: false, + attributes: [], + billingAddress: { + address1: null, + address2: null, + city: null, + country: 'US', + countryCode: 'US', + firstName: null, + lastName: null, + phone: null, + province: null, + provinceCode: null, + zip: null, + }, + token: '5f7028e0bd5225c17b24bdaa0c09f914', + currencyCode: 'USD', + discountApplications: [], + discountsAmount: { + amount: 0, + currencyCode: 'USD', + }, + email: 'test-user@sampleemail.com', + phone: '', + lineItems: [ + { + discountAllocations: [], + id: '41327143321713', + quantity: 2, + title: 'The Collection Snowboard: Liquid', + variant: { + id: '41327143321713', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6_64x64.jpg?v=1724736600', + }, + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + id: '7234590834801', + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + type: 'snowboard', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid', + }, + sku: null, + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1499.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + { + discountAllocations: [], + id: '41327143157873', + quantity: 2, + title: 'The Multi-managed Snowboard', + variant: { + id: '41327143157873', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_9129b69a-0c7b-4f66-b6cf-c4222f18028a_64x64.jpg?v=1724736597', + }, + price: { + amount: 629.95, + currencyCode: 'USD', + }, + product: { + id: '7234590736497', + title: 'The Multi-managed Snowboard', + vendor: 'Multi-managed Vendor', + type: 'snowboard', + untranslatedTitle: 'The Multi-managed Snowboard', + url: '/products/the-multi-managed-snowboard', + }, + sku: 'sku-managed-1', + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1259.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + ], + localization: { + country: { + isoCode: 'US', + }, + language: { + isoCode: 'en-US', + }, + market: { + id: 'gid://shopify/Market/23505895537', + handle: 'us', + }, + }, + order: { + id: null, + customer: { + id: null, + isFirstOrder: null, + }, + }, + delivery: { + selectedDeliveryOptions: [ + { + cost: { + amount: 0, + currencyCode: 'USD', + }, + costAfterDiscounts: { + amount: 0, + currencyCode: 'USD', + }, + description: null, + handle: + '5f7028e0bd5225c17b24bdaa0c09f914-8388085074acab7e91de633521be86f0', + title: 'Economy', + type: 'shipping', + }, + ], + }, + shippingAddress: { + address1: 'Queens Center', + address2: null, + city: 'Elmhurst', + country: 'US', + countryCode: 'US', + firstName: 'test', + lastName: 'user', + phone: null, + province: 'NY', + provinceCode: 'NY', + zip: '11373', + }, + subtotalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + shippingLine: { + price: { + amount: 0, + currencyCode: 'USD', + }, + }, + smsMarketingPhone: null, + totalTax: { + amount: 0, + currencyCode: 'USD', + }, + totalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + transactions: [], + }, + anonymousId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + }, + ], + }, + }, + ], + }, + }, + }, + { + name: 'shopify', + description: 'Track Call -> contact_info_submitted event from web pixel', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 'sh-f7c8416f-1D35-4304-EF29-78666678C4E9', + name: 'checkout_contact_info_submitted', + data: { + checkout: { + buyerAcceptsEmailMarketing: false, + buyerAcceptsSmsMarketing: false, + attributes: [], + billingAddress: { + address1: null, + address2: null, + city: null, + country: 'US', + countryCode: 'US', + firstName: null, + lastName: null, + phone: null, + province: null, + provinceCode: null, + zip: null, + }, + token: '5f7028e0bd5225c17b24bdaa0c09f914', + currencyCode: 'USD', + discountApplications: [], + discountsAmount: { + amount: 0, + currencyCode: 'USD', + }, + email: 'test-user@sampleemail.com', + phone: '', + lineItems: [ + { + discountAllocations: [], + id: '41327143321713', + quantity: 2, + title: 'The Collection Snowboard: Liquid', + variant: { + id: '41327143321713', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6_64x64.jpg?v=1724736600', + }, + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + id: '7234590834801', + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + type: 'snowboard', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid', + }, + sku: null, + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1499.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + { + discountAllocations: [], + id: '41327143157873', + quantity: 2, + title: 'The Multi-managed Snowboard', + variant: { + id: '41327143157873', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_9129b69a-0c7b-4f66-b6cf-c4222f18028a_64x64.jpg?v=1724736597', + }, + price: { + amount: 629.95, + currencyCode: 'USD', + }, + product: { + id: '7234590736497', + title: 'The Multi-managed Snowboard', + vendor: 'Multi-managed Vendor', + type: 'snowboard', + untranslatedTitle: 'The Multi-managed Snowboard', + url: '/products/the-multi-managed-snowboard', + }, + sku: 'sku-managed-1', + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1259.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + ], + localization: { + country: { + isoCode: 'US', + }, + language: { + isoCode: 'en-US', + }, + market: { + id: 'gid://shopify/Market/23505895537', + handle: 'us', + }, + }, + order: { + id: null, + customer: { + id: null, + isFirstOrder: null, + }, + }, + delivery: { + selectedDeliveryOptions: [], + }, + shippingAddress: { + address1: null, + address2: null, + city: null, + country: 'US', + countryCode: 'US', + firstName: null, + lastName: null, + phone: null, + province: null, + provinceCode: null, + zip: null, + }, + subtotalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + shippingLine: { + price: { + amount: 0, + currencyCode: 'USD', + }, + }, + smsMarketingPhone: null, + totalTax: { + amount: 0, + currencyCode: 'USD', + }, + totalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + transactions: [], + }, + }, + type: 'standard', + clientId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + timestamp: '2024-09-15T21:40:28.498Z', + context: dummyContext, + pixelEventLabel: true, + query_parameters: { + topic: ['checkouts_update'], + writeKey: ['dummy-write-key'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { ...responseDummyContext, topic: 'checkout_contact_info_submitted' }, + integrations: { + SHOPIFY: true, + }, + type: 'track', + event: 'Checkout Contact Info Submitted', + properties: { + buyerAcceptsEmailMarketing: false, + buyerAcceptsSmsMarketing: false, + attributes: [], + billingAddress: { + address1: null, + address2: null, + city: null, + country: 'US', + countryCode: 'US', + firstName: null, + lastName: null, + phone: null, + province: null, + provinceCode: null, + zip: null, + }, + token: '5f7028e0bd5225c17b24bdaa0c09f914', + currencyCode: 'USD', + discountApplications: [], + discountsAmount: { + amount: 0, + currencyCode: 'USD', + }, + email: 'test-user@sampleemail.com', + phone: '', + lineItems: [ + { + discountAllocations: [], + id: '41327143321713', + quantity: 2, + title: 'The Collection Snowboard: Liquid', + variant: { + id: '41327143321713', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6_64x64.jpg?v=1724736600', + }, + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + id: '7234590834801', + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + type: 'snowboard', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid', + }, + sku: null, + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1499.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + { + discountAllocations: [], + id: '41327143157873', + quantity: 2, + title: 'The Multi-managed Snowboard', + variant: { + id: '41327143157873', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_9129b69a-0c7b-4f66-b6cf-c4222f18028a_64x64.jpg?v=1724736597', + }, + price: { + amount: 629.95, + currencyCode: 'USD', + }, + product: { + id: '7234590736497', + title: 'The Multi-managed Snowboard', + vendor: 'Multi-managed Vendor', + type: 'snowboard', + untranslatedTitle: 'The Multi-managed Snowboard', + url: '/products/the-multi-managed-snowboard', + }, + sku: 'sku-managed-1', + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1259.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + ], + localization: { + country: { + isoCode: 'US', + }, + language: { + isoCode: 'en-US', + }, + market: { + id: 'gid://shopify/Market/23505895537', + handle: 'us', + }, + }, + order: { + id: null, + customer: { + id: null, + isFirstOrder: null, + }, + }, + delivery: { + selectedDeliveryOptions: [], + }, + shippingAddress: { + address1: null, + address2: null, + city: null, + country: 'US', + countryCode: 'US', + firstName: null, + lastName: null, + phone: null, + province: null, + provinceCode: null, + zip: null, + }, + subtotalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + shippingLine: { + price: { + amount: 0, + currencyCode: 'USD', + }, + }, + smsMarketingPhone: null, + totalTax: { + amount: 0, + currencyCode: 'USD', + }, + totalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + transactions: [], + }, + anonymousId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + }, + ], + }, + }, + ], + }, + }, + }, + { + name: 'shopify', + description: 'Track Call -> shipping_info_submitted event from web pixel', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 'sh-f7d5618e-404A-4E6D-4662-599A4BCC9E7C', + name: 'checkout_shipping_info_submitted', + data: { + checkout: { + buyerAcceptsEmailMarketing: false, + buyerAcceptsSmsMarketing: false, + attributes: [], + billingAddress: { + address1: null, + address2: null, + city: null, + country: 'US', + countryCode: 'US', + firstName: null, + lastName: null, + phone: null, + province: null, + provinceCode: null, + zip: null, + }, + token: '5f7028e0bd5225c17b24bdaa0c09f914', + currencyCode: 'USD', + discountApplications: [], + discountsAmount: { + amount: 0, + currencyCode: 'USD', + }, + email: 'test-user@sampleemail.com', + phone: '', + lineItems: [ + { + discountAllocations: [], + id: '41327143321713', + quantity: 2, + title: 'The Collection Snowboard: Liquid', + variant: { + id: '41327143321713', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6_64x64.jpg?v=1724736600', + }, + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + id: '7234590834801', + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + type: 'snowboard', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid', + }, + sku: null, + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1499.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + { + discountAllocations: [], + id: '41327143157873', + quantity: 2, + title: 'The Multi-managed Snowboard', + variant: { + id: '41327143157873', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_9129b69a-0c7b-4f66-b6cf-c4222f18028a_64x64.jpg?v=1724736597', + }, + price: { + amount: 629.95, + currencyCode: 'USD', + }, + product: { + id: '7234590736497', + title: 'The Multi-managed Snowboard', + vendor: 'Multi-managed Vendor', + type: 'snowboard', + untranslatedTitle: 'The Multi-managed Snowboard', + url: '/products/the-multi-managed-snowboard', + }, + sku: 'sku-managed-1', + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1259.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + ], + localization: { + country: { + isoCode: 'US', + }, + language: { + isoCode: 'en-US', + }, + market: { + id: 'gid://shopify/Market/23505895537', + handle: 'us', + }, + }, + order: { + id: null, + customer: { + id: null, + isFirstOrder: null, + }, + }, + delivery: { + selectedDeliveryOptions: [ + { + cost: { + amount: 0, + currencyCode: 'USD', + }, + costAfterDiscounts: { + amount: 0, + currencyCode: 'USD', + }, + description: null, + handle: '5f7028e0bd5225c17b24bdaa0c09f914-8388085074acab7e91de633521be86f0', + title: 'Economy', + type: 'shipping', + }, + ], + }, + shippingAddress: { + address1: 'Queens Center', + address2: null, + city: 'Elmhurst', + country: 'US', + countryCode: 'US', + firstName: 'test', + lastName: 'user', + phone: null, + province: 'NY', + provinceCode: 'NY', + zip: '11373', + }, + subtotalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + shippingLine: { + price: { + amount: 0, + currencyCode: 'USD', + }, + }, + smsMarketingPhone: null, + totalTax: { + amount: 0, + currencyCode: 'USD', + }, + totalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + transactions: [], + }, + }, + type: 'standard', + clientId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + timestamp: '2024-09-15T21:47:38.576Z', + context: dummyContext, + pixelEventLabel: true, + query_parameters: { + topic: ['checkouts_update'], + writeKey: ['dummy-write-key'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { ...responseDummyContext, topic: 'checkout_shipping_info_submitted' }, + integrations: { + SHOPIFY: true, + }, + type: 'track', + event: 'Checkout Shipping Info Submitted', + properties: { + buyerAcceptsEmailMarketing: false, + buyerAcceptsSmsMarketing: false, + attributes: [], + billingAddress: { + address1: null, + address2: null, + city: null, + country: 'US', + countryCode: 'US', + firstName: null, + lastName: null, + phone: null, + province: null, + provinceCode: null, + zip: null, + }, + token: '5f7028e0bd5225c17b24bdaa0c09f914', + currencyCode: 'USD', + discountApplications: [], + discountsAmount: { + amount: 0, + currencyCode: 'USD', + }, + email: 'test-user@sampleemail.com', + phone: '', + lineItems: [ + { + discountAllocations: [], + id: '41327143321713', + quantity: 2, + title: 'The Collection Snowboard: Liquid', + variant: { + id: '41327143321713', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6_64x64.jpg?v=1724736600', + }, + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + id: '7234590834801', + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + type: 'snowboard', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid', + }, + sku: null, + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1499.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + { + discountAllocations: [], + id: '41327143157873', + quantity: 2, + title: 'The Multi-managed Snowboard', + variant: { + id: '41327143157873', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_9129b69a-0c7b-4f66-b6cf-c4222f18028a_64x64.jpg?v=1724736597', + }, + price: { + amount: 629.95, + currencyCode: 'USD', + }, + product: { + id: '7234590736497', + title: 'The Multi-managed Snowboard', + vendor: 'Multi-managed Vendor', + type: 'snowboard', + untranslatedTitle: 'The Multi-managed Snowboard', + url: '/products/the-multi-managed-snowboard', + }, + sku: 'sku-managed-1', + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1259.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + ], + localization: { + country: { + isoCode: 'US', + }, + language: { + isoCode: 'en-US', + }, + market: { + id: 'gid://shopify/Market/23505895537', + handle: 'us', + }, + }, + order: { + id: null, + customer: { + id: null, + isFirstOrder: null, + }, + }, + delivery: { + selectedDeliveryOptions: [ + { + cost: { + amount: 0, + currencyCode: 'USD', + }, + costAfterDiscounts: { + amount: 0, + currencyCode: 'USD', + }, + description: null, + handle: + '5f7028e0bd5225c17b24bdaa0c09f914-8388085074acab7e91de633521be86f0', + title: 'Economy', + type: 'shipping', + }, + ], + }, + shippingAddress: { + address1: 'Queens Center', + address2: null, + city: 'Elmhurst', + country: 'US', + countryCode: 'US', + firstName: 'test', + lastName: 'user', + phone: null, + province: 'NY', + provinceCode: 'NY', + zip: '11373', + }, + subtotalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + shippingLine: { + price: { + amount: 0, + currencyCode: 'USD', + }, + }, + smsMarketingPhone: null, + totalTax: { + amount: 0, + currencyCode: 'USD', + }, + totalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + transactions: [], + }, + anonymousId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + }, + ], + }, + }, + ], + }, + }, + }, + { + name: 'shopify', + description: 'Track Call -> payment_info_submitted event from web pixel', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 'sh-f7d843ea-ED11-4A12-F32F-C5A45BED0413', + name: 'payment_info_submitted', + data: { + checkout: { + buyerAcceptsEmailMarketing: false, + buyerAcceptsSmsMarketing: false, + attributes: [], + billingAddress: { + address1: null, + address2: null, + city: null, + country: 'US', + countryCode: 'US', + firstName: null, + lastName: null, + phone: null, + province: null, + provinceCode: null, + zip: null, + }, + token: '5f7028e0bd5225c17b24bdaa0c09f914', + currencyCode: 'USD', + discountApplications: [], + discountsAmount: { + amount: 0, + currencyCode: 'USD', + }, + email: 'test-user@sampleemail.com', + phone: '', + lineItems: [ + { + discountAllocations: [], + id: '41327143321713', + quantity: 2, + title: 'The Collection Snowboard: Liquid', + variant: { + id: '41327143321713', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6_64x64.jpg?v=1724736600', + }, + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + id: '7234590834801', + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + type: 'snowboard', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid', + }, + sku: null, + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1499.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + { + discountAllocations: [], + id: '41327143157873', + quantity: 2, + title: 'The Multi-managed Snowboard', + variant: { + id: '41327143157873', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_9129b69a-0c7b-4f66-b6cf-c4222f18028a_64x64.jpg?v=1724736597', + }, + price: { + amount: 629.95, + currencyCode: 'USD', + }, + product: { + id: '7234590736497', + title: 'The Multi-managed Snowboard', + vendor: 'Multi-managed Vendor', + type: 'snowboard', + untranslatedTitle: 'The Multi-managed Snowboard', + url: '/products/the-multi-managed-snowboard', + }, + sku: 'sku-managed-1', + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1259.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + ], + localization: { + country: { + isoCode: 'US', + }, + language: { + isoCode: 'en-US', + }, + market: { + id: 'gid://shopify/Market/23505895537', + handle: 'us', + }, + }, + order: { + id: null, + customer: { + id: null, + isFirstOrder: null, + }, + }, + delivery: { + selectedDeliveryOptions: [ + { + cost: { + amount: 0, + currencyCode: 'USD', + }, + costAfterDiscounts: { + amount: 0, + currencyCode: 'USD', + }, + description: null, + handle: '5f7028e0bd5225c17b24bdaa0c09f914-8388085074acab7e91de633521be86f0', + title: 'Economy', + type: 'shipping', + }, + ], + }, + shippingAddress: { + address1: 'Queens Center', + address2: null, + city: 'Elmhurst', + country: 'US', + countryCode: 'US', + firstName: 'test', + lastName: 'user', + phone: null, + province: 'NY', + provinceCode: 'NY', + zip: '11373', + }, + subtotalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + shippingLine: { + price: { + amount: 0, + currencyCode: 'USD', + }, + }, + smsMarketingPhone: null, + totalTax: { + amount: 0, + currencyCode: 'USD', + }, + totalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + transactions: [], + }, + }, + type: 'standard', + clientId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + timestamp: '2024-09-15T21:49:13.092Z', + context: dummyContext, + pixelEventLabel: true, + query_parameters: { + topic: ['checkouts_update'], + writeKey: ['dummy-write-key'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { ...responseDummyContext, topic: 'payment_info_submitted' }, + integrations: { + SHOPIFY: true, + }, + type: 'track', + event: 'Payment Info Submitted', + properties: { + buyerAcceptsEmailMarketing: false, + buyerAcceptsSmsMarketing: false, + attributes: [], + billingAddress: { + address1: null, + address2: null, + city: null, + country: 'US', + countryCode: 'US', + firstName: null, + lastName: null, + phone: null, + province: null, + provinceCode: null, + zip: null, + }, + token: '5f7028e0bd5225c17b24bdaa0c09f914', + currencyCode: 'USD', + discountApplications: [], + discountsAmount: { + amount: 0, + currencyCode: 'USD', + }, + email: 'test-user@sampleemail.com', + phone: '', + lineItems: [ + { + discountAllocations: [], + id: '41327143321713', + quantity: 2, + title: 'The Collection Snowboard: Liquid', + variant: { + id: '41327143321713', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6_64x64.jpg?v=1724736600', + }, + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + id: '7234590834801', + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + type: 'snowboard', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid', + }, + sku: null, + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1499.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + { + discountAllocations: [], + id: '41327143157873', + quantity: 2, + title: 'The Multi-managed Snowboard', + variant: { + id: '41327143157873', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_9129b69a-0c7b-4f66-b6cf-c4222f18028a_64x64.jpg?v=1724736597', + }, + price: { + amount: 629.95, + currencyCode: 'USD', + }, + product: { + id: '7234590736497', + title: 'The Multi-managed Snowboard', + vendor: 'Multi-managed Vendor', + type: 'snowboard', + untranslatedTitle: 'The Multi-managed Snowboard', + url: '/products/the-multi-managed-snowboard', + }, + sku: 'sku-managed-1', + title: null, + untranslatedTitle: null, + }, + finalLinePrice: { + amount: 1259.9, + currencyCode: 'USD', + }, + sellingPlanAllocation: null, + properties: [], + }, + ], + localization: { + country: { + isoCode: 'US', + }, + language: { + isoCode: 'en-US', + }, + market: { + id: 'gid://shopify/Market/23505895537', + handle: 'us', + }, + }, + order: { + id: null, + customer: { + id: null, + isFirstOrder: null, + }, + }, + delivery: { + selectedDeliveryOptions: [ + { + cost: { + amount: 0, + currencyCode: 'USD', + }, + costAfterDiscounts: { + amount: 0, + currencyCode: 'USD', + }, + description: null, + handle: + '5f7028e0bd5225c17b24bdaa0c09f914-8388085074acab7e91de633521be86f0', + title: 'Economy', + type: 'shipping', + }, + ], + }, + shippingAddress: { + address1: 'Queens Center', + address2: null, + city: 'Elmhurst', + country: 'US', + countryCode: 'US', + firstName: 'test', + lastName: 'user', + phone: null, + province: 'NY', + provinceCode: 'NY', + zip: '11373', + }, + subtotalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + shippingLine: { + price: { + amount: 0, + currencyCode: 'USD', + }, + }, + smsMarketingPhone: null, + totalTax: { + amount: 0, + currencyCode: 'USD', + }, + totalPrice: { + amount: 2759.8, + currencyCode: 'USD', + }, + transactions: [], + }, + anonymousId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + }, + ], + }, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/sources/shopify/pixelTestScenarios/ProductEventsTests.ts b/test/integrations/sources/shopify/pixelTestScenarios/ProductEventsTests.ts new file mode 100644 index 0000000000..0b8f8c3c1a --- /dev/null +++ b/test/integrations/sources/shopify/pixelTestScenarios/ProductEventsTests.ts @@ -0,0 +1,888 @@ +// This file contains the test scenarios related to Shopify pixel events, emitted from web pixel on the browser. +import { dummyContext, dummySourceConfig, responseDummyContext } from '../constants'; + +export const pixelEventsTestScenarios = [ + { + name: 'shopify', + description: 'Page Call -> page_view event from web pixel', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 'sh-f6b6f548-5FEF-4DAE-9CAB-39EE6F94E09B', + name: 'page_viewed', + data: {}, + type: 'standard', + clientId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + timestamp: '2024-09-15T17:24:30.373Z', + context: dummyContext, + pixelEventLabel: true, + query_parameters: { + topic: ['page_viewed'], + writeKey: ['dummy-write-key'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { ...responseDummyContext, topic: 'page_viewed' }, + integrations: { + SHOPIFY: true, + }, + name: 'Page View', + type: 'page', + properties: {}, + anonymousId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + }, + ], + }, + }, + ], + }, + }, + }, + { + name: 'shopify', + description: 'Track Call -> product_viewed event from web pixel', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 'sh-f6c07b5a-D20A-4E5F-812E-337299B56C34', + name: 'product_viewed', + data: { + productVariant: { + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + id: '7234590834801', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid', + type: 'snowboard', + }, + id: '41327143321713', + image: { + src: '//store.myshopify.com/cdn/shop/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6.jpg?v=1724736600', + }, + sku: '', + title: 'Default Title', + untranslatedTitle: 'Default Title', + }, + }, + type: 'standard', + clientId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + timestamp: '2024-09-15T17:34:54.889Z', + context: dummyContext, + pixelEventLabel: true, + query_parameters: { + topic: ['product_viewed'], + writeKey: ['dummy-write-key'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { ...responseDummyContext, topic: 'product_viewed' }, + integrations: { + SHOPIFY: true, + }, + type: 'track', + event: 'Product Viewed', + properties: { + productVariant: { + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + id: '7234590834801', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid', + type: 'snowboard', + }, + id: '41327143321713', + image: { + src: '//store.myshopify.com/cdn/shop/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6.jpg?v=1724736600', + }, + sku: '', + title: 'Default Title', + untranslatedTitle: 'Default Title', + }, + product_id: '7234590834801', + variant: 'The Collection Snowboard: Liquid', + brand: 'Hydrogen Vendor', + category: 'snowboard', + price: 749.95, + currency: 'USD', + url: '/products/the-collection-snowboard-liquid', + name: 'The Collection Snowboard: Liquid', + }, + anonymousId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + }, + ], + }, + }, + ], + }, + }, + }, + { + name: 'shopify', + description: 'Track Call -> cart_viewed event from web pixel', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 'shu-f6eecef1-4132-459F-CDB5-681DA3DD61CD', + name: 'cart_viewed', + data: { + cart: { + cost: { + totalAmount: { + amount: 1259.9, + currencyCode: 'USD', + }, + }, + lines: [ + { + cost: { + totalAmount: { + amount: 1259.9, + currencyCode: 'USD', + }, + }, + merchandise: { + price: { + amount: 629.95, + currencyCode: 'USD', + }, + product: { + title: 'The Multi-managed Snowboard', + vendor: 'Multi-managed Vendor', + id: '7234590736497', + untranslatedTitle: 'The Multi-managed Snowboard', + url: '/products/the-multi-managed-snowboard', + type: 'snowboard', + }, + id: '41327143157873', + image: { + src: '//store.myshopify.com/cdn/shop/files/Main_9129b69a-0c7b-4f66-b6cf-c4222f18028a.jpg?v=1724736597', + }, + sku: 'sku-managed-1', + title: 'Default Title', + untranslatedTitle: 'Default Title', + }, + quantity: 2, + }, + ], + totalQuantity: 2, + attributes: [], + id: 'Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + }, + }, + type: 'standard', + clientId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + timestamp: '2024-09-15T18:25:30.125Z', + context: dummyContext, + pixelEventLabel: true, + query_parameters: { + topic: ['cart_viewed'], + writeKey: ['dummy-write-key'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { ...responseDummyContext, topic: 'cart_viewed' }, + integrations: { + SHOPIFY: true, + }, + type: 'track', + event: 'Cart Viewed', + properties: { + products: [ + { + cost: { + totalAmount: { + amount: 1259.9, + currencyCode: 'USD', + }, + }, + merchandise: { + price: { + amount: 629.95, + currencyCode: 'USD', + }, + product: { + title: 'The Multi-managed Snowboard', + vendor: 'Multi-managed Vendor', + id: '7234590736497', + untranslatedTitle: 'The Multi-managed Snowboard', + url: '/products/the-multi-managed-snowboard', + type: 'snowboard', + }, + id: '41327143157873', + image: { + src: '//store.myshopify.com/cdn/shop/files/Main_9129b69a-0c7b-4f66-b6cf-c4222f18028a.jpg?v=1724736597', + }, + sku: 'sku-managed-1', + title: 'Default Title', + untranslatedTitle: 'Default Title', + }, + quantity: 2, + product_id: '7234590736497', + variant: 'The Multi-managed Snowboard', + image_url: + '//store.myshopify.com/cdn/shop/files/Main_9129b69a-0c7b-4f66-b6cf-c4222f18028a.jpg?v=1724736597', + price: 629.95, + category: 'snowboard', + url: '/products/the-multi-managed-snowboard', + brand: 'Multi-managed Vendor', + sku: 'sku-managed-1', + name: 'Default Title', + }, + ], + cart_id: 'Z2NwLXVzLWVhc3QxOjAxSjY5OVpIRURQNERFMDBKUTVaRkI4UzdU', + total: 1259.9, + }, + anonymousId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + }, + ], + }, + }, + ], + }, + }, + }, + { + name: 'shopify', + description: 'Track Call -> collection_viewed event from web pixel', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 'sh-f6f0c6be-43F8-47D2-5F94-C22AD5ED3E79', + name: 'collection_viewed', + data: { + collection: { + id: '', + title: 'Products', + productVariants: [ + { + price: { + amount: 10, + currencyCode: 'USD', + }, + product: { + title: 'Gift Card', + vendor: 'Snowboard Vendor', + id: '7234590605425', + untranslatedTitle: 'Gift Card', + url: '/products/gift-card', + type: 'giftcard', + }, + id: '41327142895729', + image: { + src: '//store.myshopify.com/cdn/shop/files/gift_card.png?v=1724736596', + }, + sku: '', + title: '$10', + untranslatedTitle: '$10', + }, + { + price: { + amount: 24.95, + currencyCode: 'USD', + }, + product: { + title: 'Selling Plans Ski Wax', + vendor: 'pixel-testing-rs', + id: '7234590802033', + untranslatedTitle: 'Selling Plans Ski Wax', + url: '/products/selling-plans-ski-wax', + type: 'accessories', + }, + id: '41327143223409', + image: { + src: '//store.myshopify.com/cdn/shop/files/snowboard_wax.png?v=1724736599', + }, + sku: '', + title: 'Selling Plans Ski Wax', + untranslatedTitle: 'Selling Plans Ski Wax', + }, + { + price: { + amount: 2629.95, + currencyCode: 'USD', + }, + product: { + title: 'The 3p Fulfilled Snowboard', + vendor: 'pixel-testing-rs', + id: '7234590703729', + untranslatedTitle: 'The 3p Fulfilled Snowboard', + url: '/products/the-3p-fulfilled-snowboard', + type: 'snowboard', + }, + id: '41327143125105', + image: { + src: '//store.myshopify.com/cdn/shop/files/Main_b9e0da7f-db89-4d41-83f0-7f417b02831d.jpg?v=1724736597', + }, + sku: 'sku-hosted-1', + title: 'Default Title', + untranslatedTitle: 'Default Title', + }, + ], + }, + }, + type: 'standard', + clientId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + timestamp: '2024-09-15T18:27:39.197Z', + context: dummyContext, + pixelEventLabel: true, + query_parameters: { + topic: ['collection_viewed'], + writeKey: ['dummy-write-key'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { ...responseDummyContext, topic: 'collection_viewed' }, + integrations: { + SHOPIFY: true, + }, + type: 'track', + event: 'Product List Viewed', + properties: { + cart_id: 'c7b3f99b-4d34-463b-835f-c879482a7750', + list_id: 'sh-f6f0c6be-43F8-47D2-5F94-C22AD5ED3E79', + products: [ + { + price: 10, + product: { + title: 'Gift Card', + vendor: 'Snowboard Vendor', + id: '7234590605425', + untranslatedTitle: 'Gift Card', + url: '/products/gift-card', + type: 'giftcard', + }, + id: '41327142895729', + image: { + src: '//store.myshopify.com/cdn/shop/files/gift_card.png?v=1724736596', + }, + sku: '', + title: '$10', + untranslatedTitle: '$10', + image_url: + '//store.myshopify.com/cdn/shop/files/gift_card.png?v=1724736596', + product_id: '7234590605425', + variant: 'Gift Card', + category: 'giftcard', + url: '/products/gift-card', + brand: 'Snowboard Vendor', + name: '$10', + }, + { + price: 24.95, + product: { + title: 'Selling Plans Ski Wax', + vendor: 'pixel-testing-rs', + id: '7234590802033', + untranslatedTitle: 'Selling Plans Ski Wax', + url: '/products/selling-plans-ski-wax', + type: 'accessories', + }, + id: '41327143223409', + image: { + src: '//store.myshopify.com/cdn/shop/files/snowboard_wax.png?v=1724736599', + }, + sku: '', + title: 'Selling Plans Ski Wax', + untranslatedTitle: 'Selling Plans Ski Wax', + image_url: + '//store.myshopify.com/cdn/shop/files/snowboard_wax.png?v=1724736599', + product_id: '7234590802033', + variant: 'Selling Plans Ski Wax', + category: 'accessories', + url: '/products/selling-plans-ski-wax', + brand: 'pixel-testing-rs', + name: 'Selling Plans Ski Wax', + }, + { + price: 2629.95, + product: { + title: 'The 3p Fulfilled Snowboard', + vendor: 'pixel-testing-rs', + id: '7234590703729', + untranslatedTitle: 'The 3p Fulfilled Snowboard', + url: '/products/the-3p-fulfilled-snowboard', + type: 'snowboard', + }, + id: '41327143125105', + image: { + src: '//store.myshopify.com/cdn/shop/files/Main_b9e0da7f-db89-4d41-83f0-7f417b02831d.jpg?v=1724736597', + }, + sku: 'sku-hosted-1', + title: 'Default Title', + untranslatedTitle: 'Default Title', + image_url: + '//store.myshopify.com/cdn/shop/files/Main_b9e0da7f-db89-4d41-83f0-7f417b02831d.jpg?v=1724736597', + product_id: '7234590703729', + variant: 'The 3p Fulfilled Snowboard', + category: 'snowboard', + url: '/products/the-3p-fulfilled-snowboard', + brand: 'pixel-testing-rs', + name: 'Default Title', + }, + ], + }, + anonymousId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + }, + ], + }, + }, + ], + }, + }, + }, + { + name: 'shopify', + description: 'Track Call -> product_added_to_cart event from web pixel', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 'sh-f6f828db-F77B-43E8-96C4-1D51DACD52A3', + name: 'product_added_to_cart', + data: { + cartLine: { + cost: { + totalAmount: { + amount: 749.95, + currencyCode: 'USD', + }, + }, + merchandise: { + id: '41327143321713', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6.jpg?v=1724736600', + }, + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + id: '7234590834801', + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + type: 'snowboard', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid?variant=41327143321713', + }, + sku: '', + title: null, + untranslatedTitle: null, + }, + quantity: 1, + }, + }, + type: 'standard', + clientId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + timestamp: '2024-09-15T18:34:42.625Z', + context: dummyContext, + pixelEventLabel: true, + query_parameters: { + topic: ['carts_update'], + writeKey: ['dummy-write-key'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { ...responseDummyContext, topic: 'product_added_to_cart' }, + integrations: { + SHOPIFY: true, + }, + type: 'track', + event: 'Product Added', + properties: { + cartLine: { + cost: { + totalAmount: { + amount: 749.95, + currencyCode: 'USD', + }, + }, + merchandise: { + id: '41327143321713', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6.jpg?v=1724736600', + }, + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + id: '7234590834801', + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + type: 'snowboard', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid?variant=41327143321713', + }, + sku: '', + title: null, + untranslatedTitle: null, + }, + quantity: 1, + }, + image_url: + 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6.jpg?v=1724736600', + price: 749.95, + product_id: '7234590834801', + variant: 'The Collection Snowboard: Liquid', + category: 'snowboard', + brand: 'Hydrogen Vendor', + url: '/products/the-collection-snowboard-liquid?variant=41327143321713', + sku: '', + name: null, + quantity: 1, + }, + anonymousId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + }, + ], + }, + }, + ], + }, + }, + }, + { + name: 'shopify', + description: 'Track Call -> product_removed_from_cart event from web pixel', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 'shu-f778d1eb-9B83-4832-9DC0-5C3B33A809F0', + name: 'product_removed_from_cart', + data: { + cartLine: { + cost: { + totalAmount: { + amount: 749.95, + currencyCode: 'USD', + }, + }, + merchandise: { + id: '41327143321713', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6.jpg?v=1724736600', + }, + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + id: '7234590834801', + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + type: 'snowboard', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid?variant=41327143321713', + }, + sku: '', + title: null, + untranslatedTitle: null, + }, + quantity: 1, + }, + }, + type: 'standard', + clientId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + timestamp: '2024-09-15T20:56:00.125Z', + context: dummyContext, + pixelEventLabel: true, + query_parameters: { + topic: ['carts_update'], + writeKey: ['dummy-write-key'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { ...responseDummyContext, topic: 'product_removed_from_cart' }, + integrations: { + SHOPIFY: true, + }, + type: 'track', + event: 'Product Removed', + properties: { + cartLine: { + cost: { + totalAmount: { + amount: 749.95, + currencyCode: 'USD', + }, + }, + merchandise: { + id: '41327143321713', + image: { + src: 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6.jpg?v=1724736600', + }, + price: { + amount: 749.95, + currencyCode: 'USD', + }, + product: { + id: '7234590834801', + title: 'The Collection Snowboard: Liquid', + vendor: 'Hydrogen Vendor', + type: 'snowboard', + untranslatedTitle: 'The Collection Snowboard: Liquid', + url: '/products/the-collection-snowboard-liquid?variant=41327143321713', + }, + sku: '', + title: null, + untranslatedTitle: null, + }, + quantity: 1, + }, + image_url: + 'https://cdn.shopify.com/s/files/1/0590/2696/4593/files/Main_b13ad453-477c-4ed1-9b43-81f3345adfd6.jpg?v=1724736600', + price: 749.95, + product_id: '7234590834801', + variant: 'The Collection Snowboard: Liquid', + category: 'snowboard', + brand: 'Hydrogen Vendor', + url: '/products/the-collection-snowboard-liquid?variant=41327143321713', + sku: '', + name: null, + quantity: 1, + }, + anonymousId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + }, + ], + }, + }, + ], + }, + }, + }, + { + name: 'shopify', + description: 'Track Call -> search_submitted event from web pixel', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 'sh-f7d599b4-D80F-4D05-C4CE-B980D5444596', + name: 'search_submitted', + data: { + searchResult: { + query: 'skate', + productVariants: [], + }, + }, + type: 'standard', + clientId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + timestamp: '2024-09-15T22:37:35.869Z', + context: dummyContext, + pixelEventLabel: true, + query_parameters: { + topic: ['search_submitted'], + writeKey: ['dummy-write-key'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { ...responseDummyContext, topic: 'search_submitted' }, + integrations: { + SHOPIFY: true, + }, + type: 'track', + event: 'Search Submitted', + properties: { + query: 'skate', + }, + anonymousId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + }, + ], + }, + }, + ], + }, + }, + }, + { + name: 'shopify', + description: 'Track Call -> unknown event from web pixel, should not be sent to Shopify', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 'sh-f7d599b4-D80F-4D05-C4CE-B980D5444596', + name: 'unknown_event', + data: { + searchResult: { + query: 'skate', + productVariants: [], + }, + }, + type: 'standard', + clientId: 'c7b3f99b-4d34-463b-835f-c879482a7750', + timestamp: '2024-09-15T22:37:35.869Z', + context: dummyContext, + pixelEventLabel: true, + query_parameters: { + topic: ['search_submitted'], + writeKey: ['dummy-write-key'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + outputToSource: { + body: 'T0s=', + contentType: 'text/plain', + }, + statusCode: 200, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/sources/shopify/v1ServerSideEventsTests.ts b/test/integrations/sources/shopify/v1ServerSideEventsTests.ts new file mode 100644 index 0000000000..2c323cb370 --- /dev/null +++ b/test/integrations/sources/shopify/v1ServerSideEventsTests.ts @@ -0,0 +1,596 @@ +// This file contains the test scenarios for the server-side events from the Shopify GraphQL API for +// the v1 transformation flow +import utils from '../../../../src/v0/util'; +const defaultMockFns = () => { + jest.spyOn(utils, 'generateUUID').mockReturnValue('5d3e2cb6-4011-5c9c-b7ee-11bc1e905097'); +}; +import { dummySourceConfig } from './constants'; + +export const v1ServerSideEventsScenarios = [ + { + name: 'shopify', + description: 'Track Call -> Checkout Updated event', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 35374569160817, + token: 'e89d4437003b6b8480f8bc7f8036a659', + cart_token: 'Z2NwLXVzLWVhc3QxOjAxSjdXRjdOQjY0NlFFNFdQVEg0MTRFM1E2', + email: 'testuser101@gmail.com', + gateway: null, + buyer_accepts_marketing: false, + buyer_accepts_sms_marketing: false, + sms_marketing_phone: null, + created_at: '2024-09-16T03:50:15+00:00', + updated_at: '2024-09-17T03:29:02-04:00', + landing_site: '/', + note: '', + note_attributes: [], + referring_site: '', + shipping_lines: [ + { + code: 'Standard', + price: '6.90', + original_shop_price: '6.90', + original_shop_markup: '0.00', + source: 'shopify', + title: 'Standard', + presentment_title: 'Standard', + phone: null, + tax_lines: [], + custom_tax_lines: null, + markup: '0.00', + carrier_identifier: null, + carrier_service_id: null, + api_client_id: '580111', + delivery_option_group: { + token: '26492692a443ee35c30eb82073bacaa8', + type: 'one_time_purchase', + }, + delivery_expectation_range: null, + delivery_expectation_type: null, + id: null, + requested_fulfillment_service_id: null, + delivery_category: null, + validation_context: null, + applied_discounts: [], + }, + ], + shipping_address: { + first_name: 'testuser', + address1: 'oakwood bridge', + phone: null, + city: 'KLF', + zip: '85003', + province: 'Arizona', + country: 'United States', + last_name: 'dummy', + address2: 'Hedgetown', + company: null, + latitude: null, + longitude: null, + name: 'testuser dummy', + country_code: 'US', + province_code: 'AZ', + }, + taxes_included: false, + total_weight: 0, + currency: 'USD', + completed_at: null, + phone: null, + customer_locale: 'en-US', + line_items: [ + { + key: '41327143059569', + fulfillment_service: 'manual', + gift_card: false, + grams: 0, + presentment_title: 'The Multi-location Snowboard', + presentment_variant_title: '', + product_id: 7234590638193, + quantity: 1, + requires_shipping: true, + sku: '', + tax_lines: [], + taxable: true, + title: 'The Multi-location Snowboard', + variant_id: 41327143059569, + variant_title: '', + variant_price: '729.95', + vendor: 'pixel-testing-rs', + unit_price_measurement: { + measured_type: null, + quantity_value: null, + quantity_unit: null, + reference_value: null, + reference_unit: null, + }, + compare_at_price: null, + line_price: '729.95', + price: '729.95', + applied_discounts: [], + destination_location_id: null, + user_id: null, + rank: null, + origin_location_id: null, + properties: {}, + }, + ], + name: '#35374569160817', + abandoned_checkout_url: + 'https://pixel-testing-rs.myshopify.com/59026964593/checkouts/ac/Z2NwLXVzLWVhc3QxOjAxSjdXRjdOQjY0NlFFNFdQVEg0MTRFM1E2/recover?key=8195f56ee0de230b3a0469cc692f3436', + discount_codes: [], + tax_lines: [], + presentment_currency: 'USD', + source_name: 'web', + total_line_items_price: '729.95', + total_tax: '0.00', + total_discounts: '0.00', + subtotal_price: '729.95', + total_price: '736.85', + total_duties: '0.00', + device_id: null, + user_id: null, + location_id: null, + source_identifier: null, + source_url: null, + source: null, + closed_at: null, + customer: { + id: 7188389789809, + email: 'testuser101@gmail.com', + accepts_marketing: false, + created_at: null, + updated_at: null, + first_name: 'testuser', + last_name: 'dummy', + orders_count: 0, + state: 'disabled', + total_spent: '0.00', + last_order_id: null, + note: null, + verified_email: true, + multipass_identifier: null, + tax_exempt: false, + phone: null, + tags: '', + currency: 'USD', + accepts_marketing_updated_at: null, + admin_graphql_api_id: 'gid://shopify/Customer/7188389789809', + default_address: { + id: null, + customer_id: 7188389789809, + first_name: 'testuser', + last_name: 'dummy', + company: null, + address1: 'oakwood bridge', + address2: 'Hedgetown', + city: 'KLF', + province: 'Arizona', + country: 'United States', + zip: '85003', + phone: null, + name: 'testuser dummy', + province_code: 'AZ', + country_code: 'US', + country_name: 'United States', + default: true, + }, + last_order_name: null, + marketing_opt_in_level: null, + }, + query_parameters: { + topic: ['checkouts_update'], + writeKey: ['2l9QoM7KRMJLMcYhXNUVDT0Mqbd'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + library: { + name: 'RudderStack Shopify Cloud', + version: '1.0.0', + }, + integration: { + name: 'SHOPIFY', + }, + topic: 'checkouts_update', + cart_token: 'Z2NwLXVzLWVhc3QxOjAxSjdXRjdOQjY0NlFFNFdQVEg0MTRFM1E2', + }, + integrations: { + SHOPIFY: true, + }, + type: 'track', + event: 'Checkout Updated', + properties: { + order_id: 35374569160817, + value: '736.85', + tax: '0.00', + currency: 'USD', + token: 'e89d4437003b6b8480f8bc7f8036a659', + cart_token: 'Z2NwLXVzLWVhc3QxOjAxSjdXRjdOQjY0NlFFNFdQVEg0MTRFM1E2', + email: 'testuser101@gmail.com', + buyer_accepts_marketing: false, + buyer_accepts_sms_marketing: false, + created_at: '2024-09-16T03:50:15+00:00', + updated_at: '2024-09-17T03:29:02-04:00', + landing_site: '/', + note: '', + note_attributes: [], + referring_site: '', + shipping_lines: [ + { + code: 'Standard', + price: '6.90', + original_shop_price: '6.90', + original_shop_markup: '0.00', + source: 'shopify', + title: 'Standard', + presentment_title: 'Standard', + phone: null, + tax_lines: [], + custom_tax_lines: null, + markup: '0.00', + carrier_identifier: null, + carrier_service_id: null, + api_client_id: '580111', + delivery_option_group: { + token: '26492692a443ee35c30eb82073bacaa8', + type: 'one_time_purchase', + }, + delivery_expectation_range: null, + delivery_expectation_type: null, + id: null, + requested_fulfillment_service_id: null, + delivery_category: null, + validation_context: null, + applied_discounts: [], + }, + ], + taxes_included: false, + total_weight: 0, + customer_locale: 'en-US', + name: '#35374569160817', + abandoned_checkout_url: + 'https://pixel-testing-rs.myshopify.com/59026964593/checkouts/ac/Z2NwLXVzLWVhc3QxOjAxSjdXRjdOQjY0NlFFNFdQVEg0MTRFM1E2/recover?key=8195f56ee0de230b3a0469cc692f3436', + discount_codes: [], + tax_lines: [], + presentment_currency: 'USD', + source_name: 'web', + total_line_items_price: '729.95', + total_discounts: '0.00', + subtotal_price: '729.95', + total_duties: '0.00', + products: [ + { + product_id: 7234590638193, + price: '729.95', + brand: 'pixel-testing-rs', + quantity: 1, + key: '41327143059569', + fulfillment_service: 'manual', + gift_card: false, + grams: 0, + presentment_title: 'The Multi-location Snowboard', + presentment_variant_title: '', + requires_shipping: true, + tax_lines: [], + taxable: true, + title: 'The Multi-location Snowboard', + unit_price_measurement: { + measured_type: null, + quantity_value: null, + quantity_unit: null, + reference_value: null, + reference_unit: null, + }, + compare_at_price: null, + line_price: '729.95', + applied_discounts: [], + destination_location_id: null, + user_id: null, + rank: null, + origin_location_id: null, + properties: {}, + variant: '41327143059569 729.95 ', + }, + ], + }, + userId: '7188389789809', + traits: { + email: 'testuser101@gmail.com', + firstName: 'testuser', + lastName: 'dummy', + address: { + id: null, + customer_id: 7188389789809, + first_name: 'testuser', + last_name: 'dummy', + company: null, + address1: 'oakwood bridge', + address2: 'Hedgetown', + city: 'KLF', + province: 'Arizona', + country: 'United States', + zip: '85003', + phone: null, + name: 'testuser dummy', + province_code: 'AZ', + country_code: 'US', + country_name: 'United States', + default: true, + }, + acceptsMarketing: false, + orderCount: 0, + state: 'disabled', + totalSpent: '0.00', + verifiedEmail: true, + taxExempt: false, + tags: '', + currency: 'USD', + adminGraphqlApiId: 'gid://shopify/Customer/7188389789809', + shippingAddress: { + first_name: 'testuser', + address1: 'oakwood bridge', + phone: null, + city: 'KLF', + zip: '85003', + province: 'Arizona', + country: 'United States', + last_name: 'dummy', + address2: 'Hedgetown', + company: null, + latitude: null, + longitude: null, + name: 'testuser dummy', + country_code: 'US', + province_code: 'AZ', + }, + }, + timestamp: '2024-09-17T07:29:02.000Z', + anonymousId: '5d3e2cb6-4011-5c9c-b7ee-11bc1e905097', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'shopify', + description: 'Track Call -> Cart Update event', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + id: 'Z2NwLXVzLWVhc3QxOjAxSjdXRjdOQjY0NlFFNFdQVEg0MTRFM1E2', + token: 'Z2NwLXVzLWVhc3QxOjAxSjdXRjdOQjY0NlFFNFdQVEg0MTRFM1E2', + line_items: [ + { + id: 41327143059569, + properties: null, + quantity: 3, + variant_id: 41327143059569, + key: '41327143059569:90562f18109e0e6484b0c297e7981b30', + discounted_price: '729.95', + discounts: [], + gift_card: false, + grams: 0, + line_price: '2189.85', + original_line_price: '2189.85', + original_price: '729.95', + price: '729.95', + product_id: 7234590638193, + sku: '', + taxable: true, + title: 'The Multi-location Snowboard', + total_discount: '0.00', + vendor: 'pixel-testing-rs', + discounted_price_set: { + shop_money: { + amount: '729.95', + currency_code: 'USD', + }, + presentment_money: { + amount: '729.95', + currency_code: 'USD', + }, + }, + line_price_set: { + shop_money: { + amount: '2189.85', + currency_code: 'USD', + }, + presentment_money: { + amount: '2189.85', + currency_code: 'USD', + }, + }, + original_line_price_set: { + shop_money: { + amount: '2189.85', + currency_code: 'USD', + }, + presentment_money: { + amount: '2189.85', + currency_code: 'USD', + }, + }, + price_set: { + shop_money: { + amount: '729.95', + currency_code: 'USD', + }, + presentment_money: { + amount: '729.95', + currency_code: 'USD', + }, + }, + total_discount_set: { + shop_money: { + amount: '0.0', + currency_code: 'USD', + }, + presentment_money: { + amount: '0.0', + currency_code: 'USD', + }, + }, + }, + ], + note: '', + updated_at: '2024-09-17T08:15:13.280Z', + created_at: '2024-09-16T03:50:15.478Z', + query_parameters: { + topic: ['carts_update'], + writeKey: ['2l9QoM7KRMJLMcYhXNUVDT0Mqbd'], + }, + }, + source: dummySourceConfig, + }, + ], + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + library: { + name: 'RudderStack Shopify Cloud', + version: '1.0.0', + }, + integration: { + name: 'SHOPIFY', + }, + topic: 'carts_update', + }, + integrations: { + SHOPIFY: true, + }, + type: 'track', + event: 'Cart Update', + properties: { + id: 'Z2NwLXVzLWVhc3QxOjAxSjdXRjdOQjY0NlFFNFdQVEg0MTRFM1E2', + token: 'Z2NwLXVzLWVhc3QxOjAxSjdXRjdOQjY0NlFFNFdQVEg0MTRFM1E2', + note: '', + updated_at: '2024-09-17T08:15:13.280Z', + created_at: '2024-09-16T03:50:15.478Z', + products: [ + { + product_id: 7234590638193, + price: '729.95', + brand: 'pixel-testing-rs', + quantity: 3, + id: 41327143059569, + properties: null, + key: '41327143059569:90562f18109e0e6484b0c297e7981b30', + discounted_price: '729.95', + discounts: [], + gift_card: false, + grams: 0, + line_price: '2189.85', + original_line_price: '2189.85', + original_price: '729.95', + taxable: true, + title: 'The Multi-location Snowboard', + total_discount: '0.00', + discounted_price_set: { + shop_money: { + amount: '729.95', + currency_code: 'USD', + }, + presentment_money: { + amount: '729.95', + currency_code: 'USD', + }, + }, + line_price_set: { + shop_money: { + amount: '2189.85', + currency_code: 'USD', + }, + presentment_money: { + amount: '2189.85', + currency_code: 'USD', + }, + }, + original_line_price_set: { + shop_money: { + amount: '2189.85', + currency_code: 'USD', + }, + presentment_money: { + amount: '2189.85', + currency_code: 'USD', + }, + }, + price_set: { + shop_money: { + amount: '729.95', + currency_code: 'USD', + }, + presentment_money: { + amount: '729.95', + currency_code: 'USD', + }, + }, + total_discount_set: { + shop_money: { + amount: '0.0', + currency_code: 'USD', + }, + presentment_money: { + amount: '0.0', + currency_code: 'USD', + }, + }, + variant: '41327143059569 ', + }, + ], + }, + anonymousId: '5d3e2cb6-4011-5c9c-b7ee-11bc1e905097', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, +];