From abbdca07374773952331c1674489db0c9c53eca4 Mon Sep 17 00:00:00 2001 From: ItsSudip Date: Sat, 24 Feb 2024 23:49:31 +0530 Subject: [PATCH 01/35] chore: updated test cases according to new flow for clevertap --- .../clevertap/dataDelivery/business.ts | 219 ++++++++++++++++++ .../clevertap/dataDelivery/data.ts | 5 +- 2 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 test/integrations/destinations/clevertap/dataDelivery/business.ts diff --git a/test/integrations/destinations/clevertap/dataDelivery/business.ts b/test/integrations/destinations/clevertap/dataDelivery/business.ts new file mode 100644 index 0000000000..edab4ee6d7 --- /dev/null +++ b/test/integrations/destinations/clevertap/dataDelivery/business.ts @@ -0,0 +1,219 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; + +const params = { + destination: 'clevertap', +}; +const headers = { + 'X-CleverTap-Account-Id': '476550467', + 'X-CleverTap-Passcode': + 'fbee74a147828e2932c701d19dc1f2dcfa4ac0048be3aa3a88d427090a59dc1c0fa002f1', + 'Content-Type': 'application/json', +}; +export const V1BusinessTestScenarion: ProxyV1TestData[] = [ + { + id: 'clevertap_business_0', + scenario: 'business', + successCriteria: 'should return 200 status code with success message', + name: 'clevertap', + description: '[business]:: create an user through identify call', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + params, + headers, + JSON: { + d: [ + { + type: 'profile', + profileData: { + Email: 'jamesDoe@gmail.com', + Name: 'James Doe', + Phone: '92374162212', + Gender: 'M', + Employed: true, + DOB: '1614775793', + Education: 'Science', + Married: 'Y', + 'Customer Type': 'Prime', + graduate: true, + msg_push: true, + msgSms: true, + msgemail: true, + msgwhatsapp: false, + custom_tags: '["Test_User","Interested_User","DIY_Hobby"]', + custom_mappings: '{"Office":"Trastkiv","Country":"Russia"}', + address: + '{"city":"kolkata","country":"India","postalCode":789223,"state":"WB","street":""}', + }, + identity: 'anon_id', + }, + ], + }, + endpoint: 'https://api.clevertap.com/1/upload/test1', + }, + [generateMetadata(123)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + metadata: generateMetadata(123), + error: '{"status":"success","processed":1,"unprocessed":[]}', + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'clevertap_business_1', + scenario: 'business', + successCriteria: 'should return 401 status code with error message', + name: 'clevertap', + description: '[business]:: event failed due to invalid credentials', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + params, + headers: { + 'X-CleverTap-Account-Id': 'fakeId123', + 'X-CleverTap-Passcode': 'fakePasscode123', + 'Content-Type': 'application/json', + }, + JSON: { + d: [ + { + identity: 'anon-id-new', + type: 'event', + evtName: 'Web Page Viewed: Rudder', + evtData: { + title: 'Home', + path: '/', + }, + }, + ], + }, + endpoint: 'https://api.clevertap.com/1/upload/test2', + }, + [generateMetadata(123)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 401, + message: 'Request failed with status: 401', + response: [ + { + metadata: generateMetadata(123), + error: '{"status":"fail","error":"Invalid Credentials","code":401}', + statusCode: 401, + }, + ], + statTags: { + destType: 'CLEVERTAP', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'clevertap_business_2', + scenario: 'business', + successCriteria: 'should return 401 status code with error message', + name: 'clevertap', + description: '[business]:: event failed due to invalid credentials', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + params, + headers: { + 'X-CleverTap-Account-Id': '476550467', + 'X-CleverTap-Passcode': + 'fbee74a147828e2932c701d19dc1f2dcfa4ac0048be3aa3a88d427090a59dc1c0fa002f1', + 'Content-Type': 'application/json', + }, + JSON: { + d: [ + { + identity: 'anon-id-new', + type: 'event', + evtData: { + title: 'Home', + path: '/', + }, + }, + ], + }, + endpoint: 'https://api.clevertap.com/1/upload/test3', + }, + [generateMetadata(123)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + message: 'Request failed with status: 200', + response: [ + { + metadata: generateMetadata(123), + error: '{"status":"fail","processed":0,"unprocessed":[]}', + statusCode: 400, + }, + ], + statTags: { + destType: 'CLEVERTAP', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/clevertap/dataDelivery/data.ts b/test/integrations/destinations/clevertap/dataDelivery/data.ts index 8032dd50c8..57e0d0ceea 100644 --- a/test/integrations/destinations/clevertap/dataDelivery/data.ts +++ b/test/integrations/destinations/clevertap/dataDelivery/data.ts @@ -1,4 +1,5 @@ -export const data = [ +import { V1BusinessTestScenarion } from './business'; +const oldV0TestCases = [ { name: 'clevertap', description: 'Test 0', @@ -228,3 +229,5 @@ export const data = [ }, }, ]; + +export const data = [...oldV0TestCases, ...V1BusinessTestScenarion]; From c1b3736ab60c9582bdf1c4b07a761976de0da16f Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Fri, 8 Mar 2024 11:46:43 +0530 Subject: [PATCH 02/35] fix: email mapping for clevertap --- .../clevertap/data/CleverTapIdentify.json | 2 +- .../destinations/clevertap/processor/data.ts | 124 ++++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/src/v0/destinations/clevertap/data/CleverTapIdentify.json b/src/v0/destinations/clevertap/data/CleverTapIdentify.json index 577e13c339..cdc4b28d93 100644 --- a/src/v0/destinations/clevertap/data/CleverTapIdentify.json +++ b/src/v0/destinations/clevertap/data/CleverTapIdentify.json @@ -1,7 +1,7 @@ [ { "destKey": "Email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/test/integrations/destinations/clevertap/processor/data.ts b/test/integrations/destinations/clevertap/processor/data.ts index 6309c5ec8a..1d7bdd7e78 100644 --- a/test/integrations/destinations/clevertap/processor/data.ts +++ b/test/integrations/destinations/clevertap/processor/data.ts @@ -122,6 +122,130 @@ export const data = [ }, }, }, + { + name: 'clevertap', + description: 'Should not load email from externalId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + passcode: 'sample_passcode', + accountId: '476550467', + trackAnonymous: true, + enableObjectIdMapping: false, + }, + }, + message: { + channel: 'web', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: 'anon_id', + type: 'identify', + traits: { + anonymousId: 'anon_id', + name: 'James Doe', + phone: '92374162212', + gender: 'M', + employed: true, + birthday: '1614775793', + education: 'Science', + graduate: true, + married: true, + customerType: 'Prime', + msg_push: true, + msgSms: true, + msgemail: true, + msgwhatsapp: false, + custom_tags: ['Test_User', 'Interested_User', 'DIY_Hobby'], + custom_mappings: { + Office: 'Trastkiv', + Country: 'Russia', + }, + address: { + city: 'kolkata', + country: 'India', + postalCode: 789223, + state: 'WB', + street: '', + }, + 'category-unsubscribe': { email: ['Marketing', 'Transactional'] }, + }, + context: { + externalId: [{ type: 'someId', id: 'someID' }], + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.clevertap.com/1/upload', + headers: { + 'X-CleverTap-Account-Id': '476550467', + 'X-CleverTap-Passcode': 'sample_passcode', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + d: [ + { + type: 'profile', + profileData: { + Name: 'James Doe', + Phone: '92374162212', + Gender: 'M', + Employed: true, + DOB: '1614775793', + Education: 'Science', + Married: true, + 'Customer Type': 'Prime', + graduate: true, + msg_push: true, + msgSms: true, + msgemail: true, + msgwhatsapp: false, + custom_mappings: '{"Office":"Trastkiv","Country":"Russia"}', + custom_tags: '["Test_User","Interested_User","DIY_Hobby"]', + address: + '{"city":"kolkata","country":"India","postalCode":789223,"state":"WB","street":""}', + 'category-unsubscribe': { email: ['Marketing', 'Transactional'] }, + }, + identity: 'anon_id', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, { name: 'clevertap', description: 'Test 1', From 01d460c3edaf39b35c4686516c9e9140be46aa5e Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Mon, 11 Mar 2024 12:48:09 +0530 Subject: [PATCH 03/35] fix: label not present in prometheus metrics (#3176) * fix: label not present in prometheus metrics Signed-off-by: Sai Sankeerth * fix: remove error logging Signed-off-by: Sai Sankeerth --- src/util/prometheus.js | 2 +- src/util/redis/redisConnector.test.js | 2 +- src/util/redis/testData/shopify_source.json | 15 ++++++++---- .../shopify/shopify_redis.util.test.js | 14 +++++++---- src/v0/sources/shopify/transform.js | 14 +++++++---- src/v0/sources/shopify/util.js | 23 +++++++++++++------ 6 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 0fa17dc9bd..89e5424c0c 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -710,7 +710,7 @@ class Prometheus { name: 'get_libraries_code_time', help: 'get_libraries_code_time', type: 'histogram', - labelNames: ['libraryVersionId', 'versionId', 'type'], + labelNames: ['libraryVersionId', 'versionId', 'type', 'version'], }, { name: 'isolate_cpu_time', diff --git a/src/util/redis/redisConnector.test.js b/src/util/redis/redisConnector.test.js index 840f222e37..e0491132ff 100644 --- a/src/util/redis/redisConnector.test.js +++ b/src/util/redis/redisConnector.test.js @@ -70,7 +70,7 @@ describe(`Redis Class Get Tests`, () => { data.forEach((dataPoint, index) => { it(`${index}. Redis Get- ${dataPoint.description}`, async () => { try { - const output = await RedisDB.getVal(dataPoint.input.value, (isObjExpected = false)); + const output = await RedisDB.getVal(dataPoint.input.value, false); expect(output).toEqual(dataPoint.output); } catch (error) { expect(error.message).toEqual(dataPoint.output.error); diff --git a/src/util/redis/testData/shopify_source.json b/src/util/redis/testData/shopify_source.json index 53c6047298..04b80b8fc9 100644 --- a/src/util/redis/testData/shopify_source.json +++ b/src/util/redis/testData/shopify_source.json @@ -5,7 +5,8 @@ "user_id": "rudder01", "id": "shopify_test_get_items_fail", "query_parameters": { - "topic": ["carts_update"] + "topic": ["carts_update"], + "writeKey": ["wr"] }, "token": "shopify_test_get_items_fail", "email": "test@rudderstack.com", @@ -115,7 +116,8 @@ "input": { "cart_token": "shopifyGetAnonymousId", "query_parameters": { - "topic": ["checkouts_delete"] + "topic": ["checkouts_delete"], + "writeKey": ["wr"] }, "line_items": [], "note": null, @@ -154,7 +156,8 @@ "input": { "id": "shopify_test3", "query_parameters": { - "topic": ["carts_update"] + "topic": ["carts_update"], + "writeKey": ["wr"] }, "token": "shopify_test3", "line_items": [], @@ -252,7 +255,8 @@ "user_id": "rudder01", "id": "shopify_test_cart", "query_parameters": { - "topic": ["carts_update"] + "topic": ["carts_update"], + "writeKey": ["wr"] }, "token": "shopify_test_cart", "email": "test@rudderstack.com", @@ -1256,7 +1260,8 @@ "input": { "id": "shopify_test4", "query_parameters": { - "topic": ["carts_update"] + "topic": ["carts_update"], + "writeKey": ["wr"] }, "token": "shopify_test4", "line_items": [], diff --git a/src/v0/sources/shopify/shopify_redis.util.test.js b/src/v0/sources/shopify/shopify_redis.util.test.js index db596e1dfb..fb99837932 100644 --- a/src/v0/sources/shopify/shopify_redis.util.test.js +++ b/src/v0/sources/shopify/shopify_redis.util.test.js @@ -1,5 +1,9 @@ const { getAnonymousIdAndSessionId, checkAndUpdateCartItems } = require('./util'); jest.mock('ioredis', () => require('../../../../test/__mocks__/redis')); +const metricMetadata = { + writeKey: 'dummyKey', + source: 'src', +}; describe('Shopify Utils Test', () => { describe('Check for valid cart update event test cases', () => { it('Event containing token and nothing is retreived from redis and less than req. time difference between created_at and uadated_at', async () => { @@ -14,7 +18,7 @@ describe('Shopify Utils Test', () => { created_at: '2023-02-10T12:05:04.402Z', }; const expectedOutput = false; - const output = await checkAndUpdateCartItems(input); + const output = await checkAndUpdateCartItems(input, null, metricMetadata); expect(output).toEqual(expectedOutput); }); it('Event containing token and nothing is retreived from redis', async () => { @@ -28,7 +32,7 @@ describe('Shopify Utils Test', () => { ], }; const expectedOutput = true; - const output = await checkAndUpdateCartItems(input); + const output = await checkAndUpdateCartItems(input, null, metricMetadata); expect(output).toEqual(expectedOutput); }); @@ -44,7 +48,7 @@ describe('Shopify Utils Test', () => { }; const expectedOutput = true; - const output = await checkAndUpdateCartItems(input); + const output = await checkAndUpdateCartItems(input, null, metricMetadata); expect(output).toEqual(expectedOutput); }); @@ -60,7 +64,7 @@ describe('Shopify Utils Test', () => { }; const expectedOutput = false; - const output = await checkAndUpdateCartItems(input); + const output = await checkAndUpdateCartItems(input, null, metricMetadata); expect(output).toEqual(expectedOutput); }); @@ -76,7 +80,7 @@ describe('Shopify Utils Test', () => { }; const expectedOutput = true; - const output = await checkAndUpdateCartItems(input); + const output = await checkAndUpdateCartItems(input, null, metricMetadata); expect(output).toEqual(expectedOutput); }); }); diff --git a/src/v0/sources/shopify/transform.js b/src/v0/sources/shopify/transform.js index 013580d7a3..4f09984054 100644 --- a/src/v0/sources/shopify/transform.js +++ b/src/v0/sources/shopify/transform.js @@ -143,7 +143,7 @@ const processEvent = async (inputEvent, metricMetadata) => { break; case 'carts_update': if (useRedisDatabase) { - redisData = await getDataFromRedis(event.id || event.token); + redisData = await getDataFromRedis(event.id || event.token, metricMetadata); const isValidEvent = await checkAndUpdateCartItems(inputEvent, redisData, metricMetadata); if (!isValidEvent) { return NO_OPERATION_SUCCESS; @@ -155,7 +155,8 @@ const processEvent = async (inputEvent, metricMetadata) => { if (!SUPPORTED_TRACK_EVENTS.includes(shopifyTopic)) { stats.increment('invalid_shopify_event', { event: shopifyTopic, - ...metricMetadata, + source: metricMetadata.source, + shopifyTopic: metricMetadata.shopifyTopic, }); return NO_OPERATION_SUCCESS; } @@ -215,7 +216,8 @@ const processIdentifierEvent = async (event, metricMetadata) => { stats.increment('shopify_redis_calls', { type: 'set', field: 'itemsHash', - ...metricMetadata, + source: metricMetadata.source, + writeKey: metricMetadata.writeKey, }); /* cart_token: { anonymousId: 'anon_id1', @@ -236,14 +238,16 @@ const processIdentifierEvent = async (event, metricMetadata) => { stats.increment('shopify_redis_calls', { type: 'set', field, - ...metricMetadata, + source: metricMetadata.source, + writeKey: metricMetadata.writeKey, }); await RedisDB.setVal(`${event.cartToken}`, value); } catch (e) { logger.debug(`{{SHOPIFY::}} cartToken map set call Failed due redis error ${e}`); stats.increment('shopify_redis_failures', { type: 'set', - ...metricMetadata, + source: metricMetadata.source, + writeKey: metricMetadata.writeKey, }); } } diff --git a/src/v0/sources/shopify/util.js b/src/v0/sources/shopify/util.js index 6f31ade4a7..c4bbb61b9c 100644 --- a/src/v0/sources/shopify/util.js +++ b/src/v0/sources/shopify/util.js @@ -29,7 +29,8 @@ const getDataFromRedis = async (key, metricMetadata) => { stats.increment('shopify_redis_calls', { type: 'get', field: 'all', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, }); const redisData = await RedisDB.getVal(key); if ( @@ -37,7 +38,8 @@ const getDataFromRedis = async (key, metricMetadata) => { (typeof redisData === 'object' && Object.keys(redisData).length === 0) ) { stats.increment('shopify_redis_no_val', { - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, }); } return redisData; @@ -45,7 +47,8 @@ const getDataFromRedis = async (key, metricMetadata) => { logger.debug(`{{SHOPIFY::}} Get call Failed due redis error ${e}`); stats.increment('shopify_redis_failures', { type: 'get', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, }); } return null; @@ -166,7 +169,9 @@ const getAnonymousIdAndSessionId = async (message, metricMetadata, redisData = n if (isDefinedAndNotNull(anonymousId) && isDefinedAndNotNull(sessionId)) { stats.increment('shopify_anon_id_resolve', { method: 'note_attributes', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, + shopifyTopic: metricMetadata.shopifyTopic, }); return { anonymousId, sessionId }; } @@ -198,7 +203,9 @@ const getAnonymousIdAndSessionId = async (message, metricMetadata, redisData = n // and for how many stats.increment('shopify_anon_id_resolve', { method: 'database', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, + shopifyTopic: metricMetadata.shopifyTopic, }); } return { anonymousId, sessionId }; @@ -215,14 +222,16 @@ const updateCartItemsInRedis = async (cartToken, newCartItemsHash, metricMetadat stats.increment('shopify_redis_calls', { type: 'set', field: 'itemsHash', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, }); await RedisDB.setVal(`${cartToken}`, value); } catch (e) { logger.debug(`{{SHOPIFY::}} itemsHash set call Failed due redis error ${e}`); stats.increment('shopify_redis_failures', { type: 'set', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, }); } }; From a0ca61bfd4fdb0197e40e39f9d21d49a97d726da Mon Sep 17 00:00:00 2001 From: ItsSudip Date: Mon, 11 Mar 2024 19:35:18 +0530 Subject: [PATCH 04/35] chore: address comments --- .../clevertap/dataDelivery/business.ts | 103 ++++++++++++++---- .../destinations/clevertap/network.ts | 29 +++++ 2 files changed, 113 insertions(+), 19 deletions(-) diff --git a/test/integrations/destinations/clevertap/dataDelivery/business.ts b/test/integrations/destinations/clevertap/dataDelivery/business.ts index edab4ee6d7..d9f83f52f3 100644 --- a/test/integrations/destinations/clevertap/dataDelivery/business.ts +++ b/test/integrations/destinations/clevertap/dataDelivery/business.ts @@ -10,6 +10,18 @@ const headers = { 'fbee74a147828e2932c701d19dc1f2dcfa4ac0048be3aa3a88d427090a59dc1c0fa002f1', 'Content-Type': 'application/json', }; + +const statTags = { + destType: 'CLEVERTAP', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + export const V1BusinessTestScenarion: ProxyV1TestData[] = [ { id: 'clevertap_business_0', @@ -133,16 +145,7 @@ export const V1BusinessTestScenarion: ProxyV1TestData[] = [ statusCode: 401, }, ], - statTags: { - destType: 'CLEVERTAP', - destinationId: 'default-destinationId', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'default-workspaceId', - }, + statTags, }, }, }, @@ -201,16 +204,78 @@ export const V1BusinessTestScenarion: ProxyV1TestData[] = [ statusCode: 400, }, ], - statTags: { - destType: 'CLEVERTAP', - destinationId: 'default-destinationId', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'default-workspaceId', + statTags, + }, + }, + }, + }, + }, + { + id: 'clevertap_business_3', + scenario: 'business', + successCriteria: 'should return 200 status code with success message', + name: 'clevertap', + description: '[business]:: create an user through identify call', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + params, + headers, + JSON: { + d: [ + { + identity: 'testUser1', + type: 'profile', + profileData: { + Name: 'Test User1', + Email: 'test1@testMail.com', + }, + }, + { + evtData: { + name: 1234, + revenue: 4.99, + }, + type: 'event', + identity: 'user123', + }, + { + identity: 'testUser2', + type: 'profile', + profileData: { + Name: 'Test User2', + Email: 'test2@testMail.com', + }, + }, + ], }, + endpoint: 'https://api.clevertap.com/1/upload/test4', + }, + [generateMetadata(123)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + statTags, + status: 400, + message: 'Request failed with status: 200', + response: [ + { + metadata: generateMetadata(123), + error: + '{"status":"partial","processed":2,"unprocessed":[{"status":"fail","code":509,"error":"Event Name is incorrect. ErrorCode: 509 - Event name is mandatory. Skipped record number : 2","record":{"evtData":{"name":1234,"revenue":4.99},"type":"event","identity":"user123"}}]}', + statusCode: 400, + }, + ], }, }, }, diff --git a/test/integrations/destinations/clevertap/network.ts b/test/integrations/destinations/clevertap/network.ts index 57a647e684..9122ba1129 100644 --- a/test/integrations/destinations/clevertap/network.ts +++ b/test/integrations/destinations/clevertap/network.ts @@ -87,6 +87,35 @@ const dataDeliveryMocksData = [ }, httpRes: { data: { status: 'fail', processed: 0, unprocessed: [] }, status: 200 }, }, + { + httpReq: { + url: 'https://api.clevertap.com/1/upload/test4', + method: 'POST', + }, + httpRes: { + data: { + status: 'partial', + processed: 2, + unprocessed: [ + { + status: 'fail', + code: 509, + error: + 'Event Name is incorrect. ErrorCode: 509 - Event name is mandatory. Skipped record number : 2', + record: { + evtData: { + name: 1234, + revenue: 4.99, + }, + type: 'event', + identity: 'user123', + }, + }, + ], + }, + status: 200, + }, + }, ]; const deleteNwData = [ { From fe72a9db9aee53bcab66b21b2f77a344f7ed61e3 Mon Sep 17 00:00:00 2001 From: Abhimanyu Babbar Date: Mon, 11 Mar 2024 23:46:33 +0530 Subject: [PATCH 05/35] chore: added step to raise PR to dedicated enterprise customers in devops on merge to master (#2802) * chore: added step to raise PR to dedicated enterprise customers as well in devops on merge to master * chore: automatic devops pr raise issue fixed --------- Co-authored-by: anshulrudderstack Co-authored-by: anshulrudderstack <144046982+anshulrudderstack@users.noreply.github.com> Co-authored-by: Jayachand --- .../workflows/prepare-for-prod-dt-deploy.yml | 36 +++++++++++++++++ .../workflows/prepare-for-prod-rollback.yml | 40 ++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/.github/workflows/prepare-for-prod-dt-deploy.yml b/.github/workflows/prepare-for-prod-dt-deploy.yml index 2af853f643..a5ca48e3f8 100644 --- a/.github/workflows/prepare-for-prod-dt-deploy.yml +++ b/.github/workflows/prepare-for-prod-dt-deploy.yml @@ -144,3 +144,39 @@ jobs: git push -u origin hosted-transformer-$TAG_NAME gh pr create --fill + + - name: Update helm charts and raise pull request for enterprise customers on dedicated transformers + env: + GITHUB_TOKEN: ${{ secrets.PAT }} + run: | + cd rudder-devops + git checkout -b dedicated-transformer-$TAG_NAME + + cd customer-objects + + declare -a enabled_ut_customers=() + declare -a sub_directories=('enterprise-us' 'enterprise-eu') + + # identify the customers enabled in sub-directories + for directory in "${sub_directories[@]}"; do + for f in "./$directory"/*; do + [[ -f $f ]] || continue + + enabled="$(yq e '.spec.user_transformer.enabled' $f)" + if [ $enabled == "true" ]; then + enabled_ut_customers+=( $f ) + fi + done + done + + # bump up the customers version and repository information + for customer in "${enabled_ut_customers[@]}"; do + yq eval -i ".spec.user_transformer.image.version=\"$TAG_NAME\"" $customer + yq eval -i ".spec.user_transformer.image.repository=\"$TF_IMAGE_REPOSITORY\"" $customer + git add $customer + done + + git commit -m "chore: upgrade dedicated transformers to $TAG_NAME" + git push -u origin dedicated-transformer-$TAG_NAME + + gh pr create --fill diff --git a/.github/workflows/prepare-for-prod-rollback.yml b/.github/workflows/prepare-for-prod-rollback.yml index 9ac144a21e..825720efe1 100644 --- a/.github/workflows/prepare-for-prod-rollback.yml +++ b/.github/workflows/prepare-for-prod-rollback.yml @@ -27,11 +27,14 @@ jobs: git config --global user.name "GitHub Actions" git config --global user.email "noreply@github.com" + - name: Clone Devops Repo + run: | + git clone https://${{secrets.PAT}}@github.com/rudderlabs/rudder-devops.git + - name: Update Helm Charts and Raise Pull Request env: GITHUB_TOKEN: ${{ secrets.PAT }} run: | - git clone https://${{secrets.PAT}}@github.com/rudderlabs/rudder-devops.git cd rudder-devops git checkout -b shared-transformer-rollback-${{ steps.target-version.outputs.tag_name }} @@ -57,3 +60,38 @@ jobs: git push -u origin shared-transformer-rollback-${{ steps.target-version.outputs.tag_name }} gh pr create --fill + + - name: Update helm charts and raise pull request for enterprise customers on dedicated transformers + env: + GITHUB_TOKEN: ${{ secrets.PAT }} + run: | + cd rudder-devops + git checkout -b dedicated-transformer-rollback-${{ steps.target-version.outputs.tag_name }} + + cd customer-objects + + declare -a enabled_ut_customers=() + declare -a sub_directories=('enterprise-us' 'enterprise-eu') + + # identify the customers enabled in sub-directories + for directory in "${sub_directories[@]}"; do + for f in "./$directory"/*; do + [[ -f $f ]] || continue + + enabled="$(yq e '.spec.user_transformer.enabled' $f)" + if [ $enabled == "true" ]; then + enabled_ut_customers+=( $f ) + fi + done + done + + # bump up the customers version and repository information + for customer in "${enabled_ut_customers[@]}"; do + yq eval -i ".spec.user_transformer.image.version=\"${{ steps.target-version.outputs.tag_name }}\"" $customer + git add $customer + done + + git commit -m "chore: rollback dedicated transformers to ${{ steps.target-version.outputs.tag_name }}" + git push -u origin dedicated-transformer-rollback-${{ steps.target-version.outputs.tag_name }} + + gh pr create --fill From 1ca039d64ebb1a18a0fc6b78ed5ee08528ad6b48 Mon Sep 17 00:00:00 2001 From: Gustavo Warmling Teixeira Date: Tue, 12 Mar 2024 01:21:19 -0300 Subject: [PATCH 06/35] feat: add Koala destination (#3122) * Add koala procWorkflow file Basic steps for koala destination * Add koala canonicalNames * Add Koala integration test processor data * Add User-Agent rudderstack header * Add identity and track steps Basic implementation of payload data per event_type * Add messageId to Track call * Include ip information * Update endpoint url profiles -> projects * Update src/cdk/v2/destinations/koala/procWorkflow.yaml Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> * Update src/cdk/v2/destinations/koala/procWorkflow.yaml Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> * Update src/cdk/v2/destinations/koala/procWorkflow.yaml Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> * Add KOALA as a routerTransform * Fix wrong attr assignment * Add rtWorkflow file * Remove batch_mode step * Conside properties data when collecting ko_profile_id and email * Update src/cdk/v2/destinations/koala/procWorkflow.yaml Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> * Update src/cdk/v2/destinations/koala/procWorkflow.yaml Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> * Remove tool-versions, added by mistake * Use context variables * remove event attr from identity call * Update tests data * Update test/integrations/destinations/koala/processor/data.ts Co-authored-by: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> * Update test/integrations/destinations/koala/processor/data.ts Co-authored-by: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> * Update test/integrations/destinations/koala/processor/data.ts Co-authored-by: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> * Update src/cdk/v2/destinations/koala/procWorkflow.yaml Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> * Do not remove email from properties lets keep email in properties --------- Co-authored-by: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Co-authored-by: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> --- .../v2/destinations/koala/procWorkflow.yaml | 65 ++++ src/cdk/v2/destinations/koala/rtWorkflow.yaml | 31 ++ src/constants/destinationCanonicalNames.js | 1 + src/features.json | 3 +- .../destinations/koala/processor/data.ts | 319 ++++++++++++++++++ .../destinations/koala/router/data.ts | 200 +++++++++++ 6 files changed, 618 insertions(+), 1 deletion(-) create mode 100644 src/cdk/v2/destinations/koala/procWorkflow.yaml create mode 100644 src/cdk/v2/destinations/koala/rtWorkflow.yaml create mode 100644 test/integrations/destinations/koala/processor/data.ts create mode 100644 test/integrations/destinations/koala/router/data.ts diff --git a/src/cdk/v2/destinations/koala/procWorkflow.yaml b/src/cdk/v2/destinations/koala/procWorkflow.yaml new file mode 100644 index 0000000000..9ec0202b13 --- /dev/null +++ b/src/cdk/v2/destinations/koala/procWorkflow.yaml @@ -0,0 +1,65 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + - name: defaultRequestConfig + path: ../../../../v0/util + +steps: + - name: validateInput + template: | + $.assert(.message.type, "message Type is not present. Aborting message"); + $.assert(.message.type in {{$.EventType.([.IDENTIFY, .TRACK])}}, + "message type " + .message.type + " is not supported"); + $.assertConfig(.destination.Config.publicKey, "publicKey is not present. Aborting message"); + $.context.email = .message.().({{{{$.getGenericPaths("emailOnly")}}}}); + $.context.ko_profile_id = .message.traits.ko_profile_id ?? .message.context.traits.ko_profile_id ?? .message.properties.ko_profile_id; + $.assert($.context.email || $.context.ko_profile_id, "Neither email or ko_profile_id are present on traits. Aborting message"); + - name: setMessageType + template: | + $.context.messageType = .message.type.toLowerCase(); + - name: preparePayloadForIdentify + condition: $.context.messageType === {{$.EventType.IDENTIFY}} + template: | + const traits = .message.traits ?? .message.context.traits ?? {}; + const koTraits = traits{~['ko_profile_id']} + const basePayload = { + email: $.context.email, + profile_id: $.context.ko_profile_id, + identifies: [{ + type: $.context.messageType, + sent_at: .message.().({{{{$.getGenericPaths("timestamp")}}}}), + traits: koTraits + }] + }; + + $.context.payload = basePayload + - name: preparePayloadForTrack + condition: $.context.messageType === {{$.EventType.TRACK}} + template: | + const properties = .message.properties ?? {}; + const koProperties = properties{~['ko_profile_id']} + const basePayload = { + ip: .message.context.ip ?? .message.request_ip, + email: $.context.email, + profile_id: $.context.ko_profile_id, + events: [{ + type: $.context.messageType, + event: .message.event, + message_id: .message.messageId, + sent_at: .message.().({{{{$.getGenericPaths("timestamp")}}}}), + properties: koProperties, + context: .message.context + }] + }; + + $.context.payload = basePayload + - name: buildResponseForProcessTransformation + template: | + const response = $.defaultRequestConfig(); + response.body.JSON = $.context.payload; + response.endpoint = "https://api2.getkoala.com/web/projects/" + .destination.Config.publicKey + "/batch"; + response.headers = { + "content-type": "application/json" + }; + response diff --git a/src/cdk/v2/destinations/koala/rtWorkflow.yaml b/src/cdk/v2/destinations/koala/rtWorkflow.yaml new file mode 100644 index 0000000000..335293b6db --- /dev/null +++ b/src/cdk/v2/destinations/koala/rtWorkflow.yaml @@ -0,0 +1,31 @@ +bindings: + - name: handleRtTfSingleEventError + path: ../../../../v0/util/index + +steps: + - name: validateInput + template: | + $.assert(Array.isArray(^) && ^.length > 0, "Invalid event array") + + - name: transform + externalWorkflow: + path: ./procWorkflow.yaml + loopOverInput: true + + - name: successfulEvents + template: | + $.outputs.transform#idx.output.({ + "batchedRequest": ., + "batched": false, + "destination": ^[idx].destination, + "metadata": ^[idx].metadata[], + "statusCode": 200 + })[] + - name: failedEvents + template: | + $.outputs.transform#idx.error.( + $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) + )[] + - name: finalPayload + template: | + [...$.outputs.failedEvents, ...$.outputs.successfulEvents] diff --git a/src/constants/destinationCanonicalNames.js b/src/constants/destinationCanonicalNames.js index d1b2b24de0..17848e6b94 100644 --- a/src/constants/destinationCanonicalNames.js +++ b/src/constants/destinationCanonicalNames.js @@ -152,6 +152,7 @@ const DestCanonicalNames = { 'the trade desk', ], INTERCOM: ['INTERCOM', 'intercom', 'Intercom'], + koala: ['Koala', 'koala', 'KOALA'], }; module.exports = { DestHandlerMap, DestCanonicalNames }; diff --git a/src/features.json b/src/features.json index 5460111a22..dc52044048 100644 --- a/src/features.json +++ b/src/features.json @@ -66,7 +66,8 @@ "REDDIT": true, "THE_TRADE_DESK": true, "INTERCOM": true, - "NINETAILED": true + "NINETAILED": true, + "KOALA": true }, "regulations": [ "BRAZE", diff --git a/test/integrations/destinations/koala/processor/data.ts b/test/integrations/destinations/koala/processor/data.ts new file mode 100644 index 0000000000..9c1ea97a77 --- /dev/null +++ b/test/integrations/destinations/koala/processor/data.ts @@ -0,0 +1,319 @@ +export const data = [ + { + name: 'koala', + description: 'Sucessful track event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + annonymousId: 'annonymous-uuid', + event: 'User Signed Up', + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + properties: { + email: 'johndoe@somemail.com', + label: 'test', + value: 10, + }, + context: { + network: 'wifi' + }, + originalTimestamp: '2024-01-23T08:35:17.562Z', + sentAt: '2024-01-23T08:35:17.562Z', + request_ip: '192.11.22.33', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + FORM: {}, + JSON_ARRAY: {}, + XML: {}, + JSON: { + ip: '192.11.22.33', + email: 'johndoe@somemail.com', + events: [{ + type: 'track', + event: 'User Signed Up', + sent_at: '2024-01-23T08:35:17.562Z', + message_id: '84e26acc-56a5-4835-8233-591137fca468', + properties: { + email: 'johndoe@somemail.com', + label: 'test', + value: 10, + }, + context: { + network: 'wifi' + }, + }] + }, + }, + endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', + files: {}, + params: {}, + type: 'REST', + version: '1', + method: 'POST', + userId: '', + headers: { + 'content-type': 'application/json', + }, + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'koala', + description: 'Successful identify event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + type: 'identify', + traits: { + FirstName: 'John', + LastName: 'Doe', + address: { + city: 'San Francisco', + state: 'CA', + postalCode: '94107', + }, + email: 'johndoe@somemail.com', + ko_profile_id: 'xxxx-2222-xxxx-xxxx' + }, + originalTimestamp: '2024-01-23T08:35:17.342Z', + sentAt: '2024-01-23T08:35:35.234Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + FORM: {}, + JSON_ARRAY: {}, + XML: {}, + JSON: { + email: 'johndoe@somemail.com', + profile_id: 'xxxx-2222-xxxx-xxxx', + identifies: [{ + type: 'identify', + sent_at: '2024-01-23T08:35:17.342Z', + traits: { + FirstName: 'John', + LastName: 'Doe', + address: { + city: 'San Francisco', + state: 'CA', + postalCode: '94107', + }, + email: 'johndoe@somemail.com', + }, + }], + }, + }, + endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', + files: {}, + params: {}, + type: 'REST', + version: '1', + method: 'POST', + userId: '', + headers: { + 'content-type': 'application/json', + }, + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'koala', + description: 'Missing required email or ko_profile_id fields in traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + type: 'track', + traits: { + name: 'John Doe', + }, + originalTimestamp: '2024-01-23T08:35:17.342Z', + sentAt: '2024-01-23T08:35:35.234Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'Neither email or ko_profile_id are present on traits. Aborting message: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Neither email or ko_profile_id are present on traits. Aborting message', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'KOALA', + module: 'destination', + implementation: 'cdkV2', + destinationId: 'destId', + workspaceId: 'wspId', + feature: 'processor', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'koala', + description: 'Invalid message type page', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + type: 'page', + groupId: 'group-uuid', + originalTimestamp: '2024-01-23T08:35:17.342Z', + sentAt: '2024-01-23T08:35:35.234Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'message type page is not supported: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message type page is not supported', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'KOALA', + module: 'destination', + implementation: 'cdkV2', + destinationId: 'destId', + workspaceId: 'wspId', + feature: 'processor', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/koala/router/data.ts b/test/integrations/destinations/koala/router/data.ts new file mode 100644 index 0000000000..fb0db3e3fb --- /dev/null +++ b/test/integrations/destinations/koala/router/data.ts @@ -0,0 +1,200 @@ +export const data = [ + { + name: 'koala', + description: 'Router batch request', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + annonymousId: 'annonymous-uuid', + event: 'User Signed Up', + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + traits: { + email: 'johndoe@somemail.com' + }, + properties: { + label: 'test', + value: 10, + }, + context: { + network: 'wifi' + }, + originalTimestamp: '2024-01-23T08:35:17.562Z', + sentAt: '2024-01-23T08:35:17.562Z', + request_ip: '192.11.22.33', + }, + metadata: { + jobId: 1, + userId: 'u1', + destinationId: 'destId', + workspaceId: 'wspId' + } + }, + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + annonymousId: 'annonymous-uuid', + event: 'User Deleted account', + type: 'track', + messageId: '8bc79b03-2a5c-4615-b2da-54c0aaaaaae8', + traits: { + ko_profile_id: '123456' + }, + properties: { + attr1: 'foo', + attr2: 'bar' + }, + context: { + network: 'wifi' + }, + originalTimestamp: '2024-01-23T08:35:17.562Z', + sentAt: '2024-01-23T08:35:17.562Z', + request_ip: '192.11.55.1', + }, + metadata: { + jobId: 2, + userId: 'u1', + destinationId: 'destId', + workspaceId: 'wspId' + } + }, + ], + destType: 'koala', + }, + method: 'POST' + } + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + body: { + FORM: {}, + JSON_ARRAY: {}, + XML: {}, + JSON: { + ip: '192.11.22.33', + email: 'johndoe@somemail.com', + events: [{ + type: 'track', + event: 'User Signed Up', + sent_at: '2024-01-23T08:35:17.562Z', + message_id: '84e26acc-56a5-4835-8233-591137fca468', + properties: { + label: 'test', + value: 10, + }, + context: { + network: 'wifi' + }, + }] + }, + }, + endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', + files: {}, + params: {}, + type: 'REST', + version: '1', + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + }, + batched: false, + metadata: [{ jobId: 1, userId: 'u1', workspaceId: 'wspId', destinationId: 'destId' }], + statusCode: 200, + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + }, + { + batchedRequest: { + body: { + FORM: {}, + JSON_ARRAY: {}, + XML: {}, + JSON: { + ip: '192.11.55.1', + profile_id: '123456', + events: [{ + type: 'track', + event: 'User Deleted account', + sent_at: '2024-01-23T08:35:17.562Z', + message_id: '8bc79b03-2a5c-4615-b2da-54c0aaaaaae8', + properties: { + attr1: 'foo', + attr2: 'bar' + }, + context: { + network: 'wifi' + }, + }] + }, + }, + endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', + files: {}, + params: {}, + type: 'REST', + version: '1', + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + }, + batched: false, + metadata: [{ jobId: 2, userId: 'u1', workspaceId: 'wspId', destinationId: 'destId' }], + statusCode: 200, + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + } + ] + } + } + } + } +] From 69afa97396b1933fc11ae962dc93c0fe30dd1f03 Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Tue, 12 Mar 2024 10:23:17 +0530 Subject: [PATCH 07/35] chore: add v1 proxy tests and mocks for Marketo (#3116) --- test/integrations/common/network.ts | 11 + .../marketo/dataDelivery/business.ts | 352 ++++++ .../destinations/marketo/dataDelivery/data.ts | 7 +- .../marketo/dataDelivery/other.ts | 266 ++++ .../destinations/marketo/network.ts | 1100 +++++++++++++++-- 5 files changed, 1663 insertions(+), 73 deletions(-) create mode 100644 test/integrations/destinations/marketo/dataDelivery/business.ts create mode 100644 test/integrations/destinations/marketo/dataDelivery/other.ts diff --git a/test/integrations/common/network.ts b/test/integrations/common/network.ts index 8b0ed16c72..a6ab202a4e 100644 --- a/test/integrations/common/network.ts +++ b/test/integrations/common/network.ts @@ -81,4 +81,15 @@ export const networkCallsData = [ status: 429, }, }, + { + description: 'Mock response depicting DNS lookup failure error', + httpReq: { + method: 'post', + url: 'https://random_test_url/dns_lookup_failure', + }, + httpRes: { + data: {}, + status: 400, + }, + }, ]; diff --git a/test/integrations/destinations/marketo/dataDelivery/business.ts b/test/integrations/destinations/marketo/dataDelivery/business.ts new file mode 100644 index 0000000000..ca4e05afa9 --- /dev/null +++ b/test/integrations/destinations/marketo/dataDelivery/business.ts @@ -0,0 +1,352 @@ +import { ProxyMetdata } from '../../../../../src/types'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const statTags = { + aborted: { + destType: 'MARKETO', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, + retryable: { + destType: 'MARKETO', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, + throttled: { + destType: 'MARKETO', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'throttled', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, +}; + +export const proxyMetdata: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; + +export const reqMetadataArray = [proxyMetdata]; +const params = { + destination: 'marketo', +}; + +const commonRequestParameters = { + JSON: { + action: 'createOrUpdate', + input: [ + { + City: 'Tokyo', + Country: 'JP', + Email: 'gabi29@gmail.com', + PostalCode: '100-0001', + Title: 'Owner', + id: 1328328, + userId: 'gabi_userId_45', + }, + ], + lookupField: 'id', + }, + params, +}; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'marketo_v1_scenario_1', + name: 'marketo', + description: '[Proxy v1 API] :: Test for a successful update request', + successCriteria: 'Should return a 200 status code with status updated and record id', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer test_token_1', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test1', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + error: + '{"requestId":"664#17dae8c3d48","result":[{"id":1328328,"status":"updated"}],"success":true}', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_2', + name: 'marketo', + description: '[Proxy v1 API] :: Test for Access token invalid scenario', + successCriteria: 'Should return a 500 status code with message Access token invalid', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer test_token_2', + 'Content-Type': 'application/json', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test2', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + statTags: statTags.retryable, + message: + 'Request Failed for marketo, Access token invalid (Retryable).during Marketo Response Handling', + response: [ + { + error: + '{"requestId":"a61c#17daea5968a","success":false,"errors":[{"code":"601","message":"Access token invalid"}]}', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_3', + name: 'marketo', + description: '[Proxy v1 API] :: Test for Requested resource not found scenario', + successCriteria: 'Should return a 400 status code with message Requested resource not found', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer test_token_3', + 'Content-Type': 'application/json', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test3', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: + 'Request Failed for marketo, Requested resource not found (Aborted).during Marketo Response Handling', + response: [ + { + error: + '{"requestId":"a61c#17daea5968a","success":false,"errors":[{"code":"610","message":"Requested resource not found"}]}', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_4', + name: 'marketo', + description: '[Proxy v1 API] :: Test for Unknown error with empty response', + successCriteria: 'Should return a 500 status code with empty response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer test_token_4', + 'Content-Type': 'application/json', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test4', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + statTags: statTags.retryable, + message: 'Request failed with status: 500', + response: [ + { + error: '""', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_5', + name: 'marketo', + description: '[Proxy v1 API] :: Test for missing content type header scenario', + successCriteria: 'Should return a 612 status code with Invalid Content Type ', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'invalid', + 'User-Agent': 'RudderLabs', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test_invalid_header', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: + 'Request Failed for marketo, Invalid Content Type (Aborted).during Marketo Response Handling', + response: [ + { + error: + '{"success":false,"errors":[{"code":"612","message":"Invalid Content Type"}]}', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_6', + name: 'marketo', + description: '[Proxy v1 API] :: Test for a passed field exceeding max length', + successCriteria: 'Should return a 1077 status code with Value for field exceeds max length', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test_exceeded_length', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: 'Request failed with status: 400', + response: [ + { + error: + '{"success":false,"errors":[{"code":"1077","message":"Value for field exceeds max length"}]}', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/marketo/dataDelivery/data.ts b/test/integrations/destinations/marketo/dataDelivery/data.ts index 47dd8e9236..db379c9e95 100644 --- a/test/integrations/destinations/marketo/dataDelivery/data.ts +++ b/test/integrations/destinations/marketo/dataDelivery/data.ts @@ -1,4 +1,7 @@ -export const data = [ +import { testScenariosForV1API } from './business'; +import { otheMarketoScenariosV1 } from './other'; + +const legacyTests = [ { name: 'marketo', description: 'Test 0', @@ -488,3 +491,5 @@ export const data = [ }, }, ]; + +export const data = [...legacyTests, ...testScenariosForV1API, ...otheMarketoScenariosV1]; diff --git a/test/integrations/destinations/marketo/dataDelivery/other.ts b/test/integrations/destinations/marketo/dataDelivery/other.ts new file mode 100644 index 0000000000..5d4e3b1f17 --- /dev/null +++ b/test/integrations/destinations/marketo/dataDelivery/other.ts @@ -0,0 +1,266 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const statTags = { + aborted: { + destType: 'MARKETO', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', + }, + retryable: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'MARKETO', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, +}; + +const metadata = { + jobId: 1, + secret: { + accessToken: 'default-accessToken', + }, + attemptNum: 1, + userId: 'default-userId', + sourceId: 'default-sourceId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + dontBatch: false, +}; + +export const otheMarketoScenariosV1: ProxyV1TestData[] = [ + { + id: 'marketo_v1_other_scenario_1', + name: 'marketo', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 503 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 503, + metadata, + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 503', + status: 503, + }, + }, + }, + }, + }, + { + id: 'marketo_v1_other_scenario_2', + name: 'marketo', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata, + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, + { + id: 'marketo_v1_other_scenario_3', + name: 'marketo', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout"', + statusCode: 504, + metadata, + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 504', + status: 504, + }, + }, + }, + }, + }, + { + id: 'marketo_v1_other_scenario_4', + name: 'marketo', + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with empty error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_response', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata, + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, + { + id: 'marketo_v1_other_scenario_5', + name: 'marketo', + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with empty error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata, + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_6', + name: 'marketo', + description: '[Proxy v1 API] :: Test for DNS lookup failed scenario', + successCriteria: 'Should return a 400 status code with empty response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/dns_lookup_failure', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: 'Request failed with status: 400', + response: [ + { + error: '{}', + metadata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/marketo/network.ts b/test/integrations/destinations/marketo/network.ts index 9c28a9aef1..1606e78c51 100644 --- a/test/integrations/destinations/marketo/network.ts +++ b/test/integrations/destinations/marketo/network.ts @@ -1,20 +1,26 @@ -export const networkCallsData = [ +const userObject = { + City: 'Tokyo', + Country: 'JP', + Email: 'gabi29@gmail.com', + PostalCode: '100-0001', + Title: 'Owner', + id: 1328328, + userId: 'gabi_userId_45', +}; + +const headerObject = { + Authorization: 'Bearer test_token_2', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', +}; + +const tfProxyMocksData = [ { httpReq: { url: 'https://mktId.mktorest.com/rest/v1/leads.json/test1', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, headers: { @@ -44,24 +50,10 @@ export const networkCallsData = [ url: 'https://mktId.mktorest.com/rest/v1/leads.json/test2', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, - headers: { - Authorization: 'Bearer test_token_2', - 'Content-Type': 'application/json', - 'User-Agent': 'RudderLabs', - }, + headers: headerObject, method: 'POST', }, httpRes: { @@ -84,17 +76,7 @@ export const networkCallsData = [ url: 'https://mktId.mktorest.com/rest/v1/leads.json/test3', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, headers: { @@ -124,17 +106,7 @@ export const networkCallsData = [ url: 'https://mktId.mktorest.com/rest/v1/leads.json/test4', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, headers: { @@ -151,17 +123,7 @@ export const networkCallsData = [ url: 'https://mktId.mktorest.com/rest/v1/leads.json/test5', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, headers: { @@ -178,17 +140,7 @@ export const networkCallsData = [ url: 'https://mktId.mktorest.com/rest/v1/leads.json/test6', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, headers: { @@ -970,3 +922,1007 @@ export const networkCallsData = [ }, }, ]; + +const businessMockData = [ + { + description: 'Mock response for a successful update request', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test1', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer test_token_1', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + requestId: '664#17dae8c3d48', + result: [ + { + id: 1328328, + status: 'updated', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed update request due to invalid access token', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test2', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: headerObject, + method: 'POST', + }, + httpRes: { + data: { + requestId: 'a61c#17daea5968a', + success: false, + errors: [ + { + code: '601', + message: 'Access token invalid', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed update request due to requested resource not found', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test3', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer test_token_3', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + requestId: 'a61c#17daea5968a', + success: false, + errors: [ + { + code: '610', + message: 'Requested resource not found', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful create/update request', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test4', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer test_token_4', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: {}, + }, + { + description: 'Mock response for a successful create/update request', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test5', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer test_token_5', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: '', + }, + { + description: 'Mock response for a failed create/update request due to DNS lookup failure', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test6', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + code: '[ENOTFOUND] :: DNS lookup failed', + status: 400, + }, + }, + { + description: + 'Mock response for a failed create/update request due to unhandled exception in proxy request', + httpReq: { + url: 'https://unhandled_exception_in_proxy_req.mktorest.com/rest/v1/leads.json', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer access_token_success', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + requestId: '142e4#1835b117b76', + success: false, + errors: [ + { + code: 'random_marketo_code', + message: 'problem', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful access token request', + httpReq: { + url: 'https://marketo_acct_id_success.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed access token request due to expired token', + httpReq: { + url: 'https://marketo_acct_id_token_failure.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_expired', + expires_in: 0, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request', + httpReq: { + url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/leads.json', + method: 'get', + }, + httpRes: { + data: { + requestId: '7ab2#17672a46a99', + result: [ + { + id: 4, + status: 'created', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request with filterType=email with no results', + httpReq: { + url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/leads.json?filterType=email&filterValues=arnab.compsc%40gmail.com', + method: 'GET', + }, + httpRes: { + data: { + requestId: '107#17672aeadba', + result: [], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request with filterType=userId with results', + httpReq: { + url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/leads.json?filterType=userId&filterValues=test-user-6j55yr', + method: 'GET', + }, + httpRes: { + data: { + requestId: '12093#17672aeaee6', + result: [ + { + createdAt: '2020-12-17T21:39:07Z', + email: null, + firstName: null, + id: 4, + lastName: null, + updatedAt: '2020-12-17T21:39:07Z', + userId: 'test-user-6j55yr', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed access token request due to expired token', + httpReq: { + url: 'https://marketo_acct_id_failed.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + success: false, + errors: [ + { + code: '601', + message: 'Access Token Expired', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful access token request', + httpReq: { + url: 'https://munchkinId.mktorest.com/identity/oauth/token?client_id=b&client_secret=clientSecret&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_acess', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed access token request due to invalid client id', + httpReq: { + url: 'https://munchkinId.mktorest.com/identity/oauth/token?client_id=wrongClientId&client_secret=clientSecret&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request to get list of fields', + httpReq: { + url: 'https://munchkinId.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bulk request with queued status', + httpReq: { + url: 'https://munchkinId.mktorest.com/bulk/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '5bdd#17fd1ff88cd', + result: [ + { + batchId: 2977, + importId: '2977', + status: 'Queued', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for bulk request', + httpReq: { + url: 'https://a.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for bulk request for throttling error', + httpReq: { + url: 'https://a.mktorest.com/identity/oauth/token?client_id=b&client_secret=forThrottle&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request to get list of fields', + httpReq: { + url: 'https://a.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a succesful oauth token request', + httpReq: { + url: 'https://testMunchkin4.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed bulk request with 400 error', + httpReq: { + url: 'https://testMunchkin4.mktorest.com/bulk/v1/leads/batch/1234.json', + method: 'GET', + }, + httpRes: { + data: { + errors: [ + { + message: 'Any 400 error', + code: 1000, + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful oauth token request', + httpReq: { + url: 'https://testMunchkin3.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful oauth token request', + httpReq: { + url: 'https://testMunchkin500.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed bulk request with 500 error', + httpReq: { + url: 'https://testMunchkin500.mktorest.com/bulk/v1/leads/batch/1234.json', + method: 'GET', + }, + httpRes: { + data: { + errors: [ + { + message: 'Any 500 error', + code: 502, + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bulk request with warnings', + httpReq: { + url: 'https://a.mktorest.com/bulk/v1/leads/batch/12345/warnings.json', + method: 'GET', + }, + httpRes: { + data: 'data \n data', + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bulk request with failures', + httpReq: { + url: 'https://a.mktorest.com/bulk/v1/leads/batch/12345/failures.json', + method: 'GET', + }, + httpRes: { + data: 'data \n data', + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bearer token request', + httpReq: { + url: 'https://testMunchkin1.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request to get list of fields', + httpReq: { + url: 'https://testMunchkin1.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed bulk request with 603 error code', + httpReq: { + url: 'https://testMunchkin1.mktorest.com/bulk/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + success: false, + errors: [ + { + code: 603, + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bearer token request', + httpReq: { + url: 'https://testMunchkin2.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request to get list of fields', + httpReq: { + url: 'https://testMunchkin2.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'Email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed leads query request with pending import error', + httpReq: { + url: 'https://testMunchkin2.mktorest.com/bulk/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + success: false, + errors: [ + { + message: 'There are 10 imports currently being processed. Please try again later', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a succesful leads query request', + httpReq: { + url: 'https://testMunchkin3.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'Email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed leads query request with empty file error', + httpReq: { + url: 'https://testMunchkin3.mktorest.com/bulk/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + success: false, + errors: [ + { + message: 'Empty file', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful leads query request', + httpReq: { + url: 'https://testMunchkin4.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'Email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed leads query request with any other error', + httpReq: { + url: 'https://testMunchkin4.mktorest.com/bulk/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + success: false, + errors: [ + { + message: 'Any other error', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a succesful bearer token request', + httpReq: { + url: 'https://valid_account_broken_event.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response for a successful get request with filterType=email and filterValues specified with no results', + httpReq: { + url: 'https://valid_account_broken_event.mktorest.com/rest/v1/leads.json?filterType=email&filterValues=0c7b8b80-9c43-4f8e-b2d2-5e2448a25040@j.mail', + method: 'GET', + }, + httpRes: { + data: { + requestId: '12093#17672aeaee6', + result: [], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed get request due to missing lookup field', + httpReq: { + url: 'https://valid_account_broken_event.mktorest.com/rest/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '142e4#1835b117b76', + success: false, + errors: [ + { + code: '1006', + message: "Lookup field 'userId' not found", + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bearer token request', + httpReq: { + url: 'https://unhandled_status_code.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response for a successful get request with filterType=email and filterValues specified with no results', + httpReq: { + url: 'https://unhandled_status_code.mktorest.com/rest/v1/leads.json?filterType=email&filterValues=0c7b8b80-9c43-4f8e-b2d2-5e2448a25040@j.mail', + method: 'GET', + }, + httpRes: { + data: { + requestId: '12093#17672aeaee6', + result: [], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed get request due to random marketo error code', + httpReq: { + url: 'https://unhandled_status_code.mktorest.com/rest/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '142e4#1835b117b76', + success: false, + errors: [ + { + code: 'random_marketo_code', + message: 'some other problem', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bearer token request', + httpReq: { + url: 'https://successful_identify_transformation.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response for a successful get request with no filterType and filterValues specified', + httpReq: { + url: 'https://successful_identify_transformation.mktorest.com/rest/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7ab2#17672a46a99', + result: [ + { + id: 4, + status: 'created', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response for a successful get request with filterType=email and filterValues specified with results', + httpReq: { + url: 'https://successful_identify_transformation.mktorest.com/rest/v1/leads.json?filterType=email&filterValues=0c7b8b80-9c43-4f8e-b2d2-5e2448a25040@j.mail', + method: 'GET', + }, + httpRes: { + data: { + requestId: '12093#17672aeaee6', + result: [ + { + createdAt: '2022-09-17T21:39:07Z', + email: '0c7b8b80-9c43-4f8e-b2d2-5e2448a25040@j.mail', + firstName: 'random_first', + id: 4, + lastName: 'random_last', + updatedAt: '2022-09-20T21:48:07Z', + userId: 'test-user-957ue', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed lead request due to invalid header', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test_invalid_header', + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'invalid', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + success: false, + errors: [ + { + code: '612', + message: 'Invalid Content Type', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed lead request due to length exceeded', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test_exceeded_length', + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + success: false, + errors: [ + { + code: '1077', + message: 'Value for field exceeds max length', + }, + ], + }, + status: 400, + statusText: 'OK', + }, + }, +]; + +export const networkCallsData = [...businessMockData, ...tfProxyMocksData]; From 25b042cd9e47b6eb3cb5cc9f15d27f1a2d9605cc Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Tue, 12 Mar 2024 10:23:33 +0530 Subject: [PATCH 08/35] chore: add v1 proxy tests for MSL (#3130) --- .../dataDelivery/business.ts | 255 ++++++++++++++++++ .../marketo_static_list/dataDelivery/data.ts | 73 +---- .../marketo_static_list/dataDelivery/other.ts | 194 +++++++++++++ .../marketo_static_list/network.ts | 63 ----- 4 files changed, 458 insertions(+), 127 deletions(-) create mode 100644 test/integrations/destinations/marketo_static_list/dataDelivery/business.ts create mode 100644 test/integrations/destinations/marketo_static_list/dataDelivery/other.ts diff --git a/test/integrations/destinations/marketo_static_list/dataDelivery/business.ts b/test/integrations/destinations/marketo_static_list/dataDelivery/business.ts new file mode 100644 index 0000000000..08be877ba8 --- /dev/null +++ b/test/integrations/destinations/marketo_static_list/dataDelivery/business.ts @@ -0,0 +1,255 @@ +import { ProxyMetdata } from '../../../../../src/types'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +export const statTags = { + aborted: { + destType: 'MARKETO_STATIC_LIST', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, + retryable: { + destType: 'MARKETO_STATIC_LIST', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', + }, + throttled: { + destType: 'MARKETO_STATIC_LIST', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'throttled', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, +}; + +export const proxyMetdata: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + secret: {}, + dontBatch: false, +}; + +export const reqMetadataArray = [proxyMetdata]; +const params = { + destination: 'marketo_static_list', +}; + +const commonRequestParameters = { + params, + userId: '', + body: { + FORM: {}, + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + }, + files: {}, +}; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'msl_v1_scenario_1', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Test for a partial successful request with multiple ids', + successCriteria: 'Should return a 200 status code with respective status for each record id', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer Incorrect_token', + 'Content-Type': 'application/json', + }, + endpoint: + 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=110&id=111&id=112', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + error: + '{"requestId":"b6d1#18a8d2c10e7","result":[{"id":110,"status":"skipped","reasons":[{"code":"1015","message":"Lead not in list"}]},{"id":111,"status":"removed"},{"id":112,"status":"removed"}],"success":true}', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'msl_v1_scenario_2', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Test for Access token invalid scenario', + successCriteria: 'Should return a 500 status code with message Access token invalid', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer Incorrect_token', + 'Content-Type': 'application/json', + }, + endpoint: + 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=1&id=2&id=3', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + statTags: statTags.retryable, + message: + 'Request Failed for Marketo Static List, Access token invalid (Retryable).during Marketo Static List Response Handling', + response: [ + { + error: + '{"requestId":"68d8#1846058ee27","success":false,"errors":[{"code":"601","message":"Access token invalid"}]}', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + }, + }, + }, + }, + }, + { + id: 'msl_v1_scenario_3', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Test for a complete successful request with multiple ids', + successCriteria: + 'Should return a 200 status code with respective added status for each record id', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer token', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + endpoint: + 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=1&id=2', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + error: + '{"requestId":"12d3c#1846057dce2","result":[{"id":1,"status":"added"},{"id":2,"status":"added"}],"success":true}', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'msl_v1_scenario_4', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Test for DNS lookup failed scenario', + successCriteria: 'Should return a 400 status code with empty response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test6', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + statTags: statTags.retryable, + message: 'Request failed with status: 500', + response: [ + { + error: '""', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/marketo_static_list/dataDelivery/data.ts b/test/integrations/destinations/marketo_static_list/dataDelivery/data.ts index f0275e329e..c0398f6d2b 100644 --- a/test/integrations/destinations/marketo_static_list/dataDelivery/data.ts +++ b/test/integrations/destinations/marketo_static_list/dataDelivery/data.ts @@ -1,4 +1,7 @@ -export const data = [ +import { testScenariosForV1API } from './business'; +import { otherScenariosV1 } from './other'; + +const legacyTests = [ { name: 'marketo_static_list', description: 'Test 0', @@ -158,21 +161,7 @@ export const data = [ }, body: { FORM: {}, - JSON: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], - lookupField: 'id', - }, + JSON: {}, JSON_ARRAY: {}, XML: {}, }, @@ -234,30 +223,7 @@ export const data = [ params: {}, body: { FORM: {}, - JSON: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - { - City: 'Tokyo', - Country: 'JP', - Email: 'b@s.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328329, - userId: 'ben_userId_45', - }, - ], - lookupField: 'id', - }, + JSON: {}, JSON_ARRAY: {}, XML: {}, }, @@ -311,30 +277,7 @@ export const data = [ params: {}, body: { FORM: {}, - JSON: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - { - City: 'Tokyo', - Country: 'JP', - Email: 'b@s.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328329, - userId: 'ben_userId_45', - }, - ], - lookupField: 'id', - }, + JSON: {}, JSON_ARRAY: {}, XML: {}, }, @@ -373,3 +316,5 @@ export const data = [ }, }, ]; + +export const data = [...legacyTests, ...testScenariosForV1API, ...otherScenariosV1]; diff --git a/test/integrations/destinations/marketo_static_list/dataDelivery/other.ts b/test/integrations/destinations/marketo_static_list/dataDelivery/other.ts new file mode 100644 index 0000000000..b1f3403fa6 --- /dev/null +++ b/test/integrations/destinations/marketo_static_list/dataDelivery/other.ts @@ -0,0 +1,194 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload, generateMetadata } from '../../../testUtils'; +import { reqMetadataArray, statTags } from './business'; + +export const otherScenariosV1: ProxyV1TestData[] = [ + { + id: 'marketo_static_list_v1_other_scenario_1', + name: 'marketo_static_list', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 503, + metadata: generateMetadata(1), + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 503', + status: 503, + }, + }, + }, + }, + }, + { + id: 'marketo_static_list_v1_other_scenario_2', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, + { + id: 'marketo_static_list_v1_other_scenario_3', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout"', + statusCode: 504, + metadata: generateMetadata(1), + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 504', + status: 504, + }, + }, + }, + }, + }, + { + id: 'marketo_static_list_v1_other_scenario_4', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_response', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, + { + id: 'marketo_static_list_v1_other_scenario_5', + name: 'marketo_static_list', + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/marketo_static_list/network.ts b/test/integrations/destinations/marketo_static_list/network.ts index 5c13273859..f165291c15 100644 --- a/test/integrations/destinations/marketo_static_list/network.ts +++ b/test/integrations/destinations/marketo_static_list/network.ts @@ -46,21 +46,6 @@ const deliveryCallsData = [ { httpReq: { url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=1&id=2', - data: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], - lookupField: 'id', - }, params: { destination: 'marketo_static_list' }, headers: { Authorization: 'Bearer token', @@ -84,30 +69,6 @@ const deliveryCallsData = [ { httpReq: { url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=3&id=4', - data: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - { - City: 'Tokyo', - Country: 'JP', - Email: 'b@s.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328329, - userId: 'ben_userId_45', - }, - ], - lookupField: 'id', - }, params: {}, headers: { Authorization: 'Bearer token', @@ -131,30 +92,6 @@ const deliveryCallsData = [ { httpReq: { url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=5&id=6', - data: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - { - City: 'Tokyo', - Country: 'JP', - Email: 'b@s.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328329, - userId: 'ben_userId_45', - }, - ], - lookupField: 'id', - }, params: {}, headers: { Authorization: 'Bearer token', From 1ef20d66f87c296b4efd3c6d7129cde87aca9af2 Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Tue, 12 Mar 2024 10:33:07 +0530 Subject: [PATCH 09/35] chore: upgrade node version to v18.19.1 to resolve synk vulnerabilities (#3154) --- .nvmrc | 2 +- Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.nvmrc b/.nvmrc index a9d087399d..3c5535cf60 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.19.0 +18.19.1 diff --git a/Dockerfile b/Dockerfile index 6bd03c9515..8cd4005a7b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1.4 -FROM node:18.19.0-alpine3.18 AS base +FROM node:18.19.1-alpine3.18 AS base ENV HUSKY 0 RUN apk update From 916aaecb1939160620d5fd3c4c0c0e33f2a371b2 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Tue, 12 Mar 2024 12:43:15 +0530 Subject: [PATCH 10/35] feat: use dontBatch directive in algolia (#3169) * feat: algolia v1 proxy --- .../v2/destinations/algolia/rtWorkflow.yaml | 8 +- src/v1/destinations/algolia/networkHandler.js | 84 + .../destinations/algolia/router/data.ts | 2402 +++++++++++++++++ 3 files changed, 2492 insertions(+), 2 deletions(-) create mode 100644 src/v1/destinations/algolia/networkHandler.js diff --git a/src/cdk/v2/destinations/algolia/rtWorkflow.yaml b/src/cdk/v2/destinations/algolia/rtWorkflow.yaml index 758a71bf5b..f5442f3209 100644 --- a/src/cdk/v2/destinations/algolia/rtWorkflow.yaml +++ b/src/cdk/v2/destinations/algolia/rtWorkflow.yaml @@ -2,7 +2,6 @@ bindings: - path: ../../../../v0/destinations/algolia/config - name: handleRtTfSingleEventError path: ../../../../v0/util/index - steps: - name: validateInput template: | @@ -28,10 +27,14 @@ steps: $.outputs.transform#idx.error.( $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) )[] + - name: batchSuccessfulEvents description: Batches the successfulEvents template: | - let batches = $.chunk($.outputs.successfulEvents, $.MAX_BATCH_SIZE); + const dontBatchTrueEvents = $.outputs.successfulEvents{.metadata.dontBatch}[]; + const dontBatchFalseEvents = $.outputs.successfulEvents{!.metadata.dontBatch}[]; + + let batches = [...$.chunk(dontBatchFalseEvents, $.MAX_BATCH_SIZE), ...$.chunk(dontBatchTrueEvents, 1)]; batches@batch.({ "batchedRequest": { "body": { @@ -56,6 +59,7 @@ steps: "statusCode": 200, "destination": batch[0].destination })[]; + - name: finalPayload template: | [...$.outputs.failedEvents, ...$.outputs.batchSuccessfulEvents] diff --git a/src/v1/destinations/algolia/networkHandler.js b/src/v1/destinations/algolia/networkHandler.js new file mode 100644 index 0000000000..d953251050 --- /dev/null +++ b/src/v1/destinations/algolia/networkHandler.js @@ -0,0 +1,84 @@ +/* eslint-disable no-restricted-syntax */ +const { TransformerProxyError } = require('../../../v0/util/errorTypes'); +const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); +const { isHttpStatusSuccess, getAuthErrCategoryFromStCode } = require('../../../v0/util/index'); + +const { + processAxiosResponse, + getDynamicErrorType, +} = require('../../../adapters/utils/networkUtils'); +const tags = require('../../../v0/util/tags'); + +const responseHandler = (responseParams) => { + const { destinationResponse, rudderJobMetadata } = responseParams; + const message = `[ALGOLIA Response V1 Handler] - Request Processed Successfully`; + const responseWithIndividualEvents = []; + // response: + // {status: 200, message: 'OK'} + // {response:'[ENOTFOUND] :: DNS lookup failed', status: 400} + // destinationResponse = { + // response: {"status": 422, "message": "EventType must be one of \"click\", \"conversion\" or \"view\""}, status: 422 + // } + const { response, status } = destinationResponse; + + if (isHttpStatusSuccess(status)) { + for (const mData of rudderJobMetadata) { + const proxyOutputObj = { + statusCode: 200, + metadata: mData, + error: 'success', + }; + responseWithIndividualEvents.push(proxyOutputObj); + } + + return { + status, + message, + destinationResponse, + response: responseWithIndividualEvents, + }; + } + + // in case of non 2xx status sending 500 for every event, populate response and update dontBatch to true + const errorMessage = response?.error?.message || response?.message || 'unknown error format'; + let serverStatus = 400; + for (const metadata of rudderJobMetadata) { + // handling case if dontBatch is true, and again we got invalid from destination + if (metadata.dontBatch && status === 422) { + responseWithIndividualEvents.push({ + statusCode: 400, + metadata, + error: errorMessage, + }); + } else { + serverStatus = 500; + metadata.dontBatch = true; + responseWithIndividualEvents.push({ + statusCode: 500, + metadata, + error: errorMessage, + }); + } + } + + // sending back 500 for retry + throw new TransformerProxyError( + `ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation`, + serverStatus, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + getAuthErrCategoryFromStCode(status), + responseWithIndividualEvents, + ); +}; + +function networkHandler() { + this.prepareProxy = prepareProxyRequest; + this.proxy = proxyRequest; + this.processAxiosResponse = processAxiosResponse; + this.responseHandler = responseHandler; +} + +module.exports = { networkHandler }; diff --git a/test/integrations/destinations/algolia/router/data.ts b/test/integrations/destinations/algolia/router/data.ts index 65c74342dc..dca899693e 100644 --- a/test/integrations/destinations/algolia/router/data.ts +++ b/test/integrations/destinations/algolia/router/data.ts @@ -2220,4 +2220,2406 @@ export const data = [ }, }, }, + { + name: 'algolia', + description: 'dontBatch true for all', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + ], + destType: 'algolia', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + }, + }, + { + name: 'algolia', + description: 'dontBatch Partial true for events in a batch', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 5, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + ], + destType: 'algolia', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 5, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + }, + }, + { + name: 'algolia', + description: 'dontBatch false for all events, all events are batched in 1', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 5, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + ], + destType: 'algolia', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 5, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + }, + }, ]; From 541b596e472a242a454a438e487a8712adace5de Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Tue, 12 Mar 2024 13:06:32 +0530 Subject: [PATCH 11/35] chore: garl proxy test refactor (#3073) * chore: garl proxy test refactor * chore: code review changes --- .../dataDelivery/business.ts | 376 ++++++++++++++++++ .../dataDelivery/data.ts | 264 +----------- 2 files changed, 379 insertions(+), 261 deletions(-) create mode 100644 test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/business.ts diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/business.ts b/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/business.ts new file mode 100644 index 0000000000..2aefb18fdd --- /dev/null +++ b/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/business.ts @@ -0,0 +1,376 @@ +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; + +const commonHeaders = { + Authorization: 'Bearer dummy-access', + 'Content-Type': 'application/json', + 'developer-token': 'dummy-dev-token', +}; + +const commonParams = { + destination: 'google_adwords_remarketing_lists', + listId: '709078448', + customerId: '7693729833', + consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, +}; + +const validRequestPayload1 = { + enablePartialFailure: true, + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + hashedEmail: '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: 'e56d336922eaab3be8c1244dbaa713e134a8eba50ddbd4f50fd2fe18d72595cd', + }, + }, + ], + }, + }, + ], +}; + +const validRequestPayload2 = { + enablePartialFailure: true, + operations: [ + { + remove: { + userIdentifiers: [ + { + hashedEmail: '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + hashedEmail: '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: 'e56d336922eaab3be8c1244dbaa713e134a8eba50ddbd4f50fd2fe18d72595cd', + }, + }, + ], + }, + }, + ], +}; + +const invalidArgumentRequestPayload = { + enablePartialFailure: true, + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: 'abcd@testmail.com', + }, + ], + }, + }, + ], +}; + +const metadataArray = [generateMetadata(1)]; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const testScenariosForV0API = [ + { + id: 'garl_v0_scenario_1', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v0 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: commonHeaders, + params: commonParams, + JSON: validRequestPayload1, + endpoint: 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + destinationResponse: { response: '', status: 200 }, + }, + }, + }, + }, + }, + { + id: 'garl_v0_scenario_2', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v0 API] :: Test for a invalid argument request with a 400 response from the destination', + successCriteria: 'Should return 400 with invalid argument error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: commonHeaders, + params: commonParams, + JSON: invalidArgumentRequestPayload, + endpoint: 'https://googleads.googleapis.com/v15/customers/7693729834/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + status: 400, + message: + 'Request contains an invalid argument. during ga_audience response transformation', + destinationResponse: { + error: { + code: 400, + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v9.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + offlineUserDataJobError: 'INVALID_SHA256_FORMAT', + }, + message: 'The SHA256 encoded value is malformed.', + location: { + fieldPathElements: [ + { fieldName: 'operations', index: 0 }, + { fieldName: 'remove' }, + { fieldName: 'user_identifiers', index: 0 }, + { fieldName: 'hashed_email' }, + ], + }, + }, + ], + }, + ], + message: 'Request contains an invalid argument.', + status: 'INVALID_ARGUMENT', + }, + }, + statTags: expectedStatTags, + }, + }, + }, + }, + }, + { + id: 'garl_v0_scenario_3', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v0 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: commonHeaders, + params: commonParams, + JSON: validRequestPayload2, + endpoint: 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + destinationResponse: { response: '', status: 200 }, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API = [ + { + id: 'garl_v1_scenario_1', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v1 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: commonHeaders, + params: commonParams, + JSON: validRequestPayload1, + endpoint: + 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: '""', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'garl_v1_scenario_2', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v1 API] :: Test for a invalid argument request with a 400 response from the destination', + successCriteria: 'Should return 400 with invalid argument error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: commonHeaders, + params: commonParams, + JSON: invalidArgumentRequestPayload, + endpoint: + 'https://googleads.googleapis.com/v15/customers/7693729834/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + 'Request contains an invalid argument. during ga_audience response transformation', + response: [ + { + error: + 'Request contains an invalid argument. during ga_audience response transformation', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'garl_v1_scenario_3', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v1 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: commonHeaders, + params: commonParams, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: '""', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/data.ts b/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/data.ts index fe16ffef47..51827a38e2 100644 --- a/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/data.ts +++ b/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/data.ts @@ -1,261 +1,3 @@ -export const data = [ - { - name: 'google_adwords_remarketing_lists', - description: 'Test 0', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', - headers: { - Authorization: 'Bearer dummy-access', - 'Content-Type': 'application/json', - 'developer-token': 'dummy-dev-token', - }, - params: { - destination: 'google_adwords_remarketing_lists', - listId: '709078448', - customerId: '7693729833', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, - }, - body: { - JSON: { - enablePartialFailure: true, - operations: [ - { - create: { - userIdentifiers: [ - { - hashedEmail: - '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', - }, - { - hashedPhoneNumber: - '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', - }, - { - hashedEmail: - '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', - }, - { - hashedPhoneNumber: - '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', - }, - { - addressInfo: { - hashedFirstName: - 'e56d336922eaab3be8c1244dbaa713e134a8eba50ddbd4f50fd2fe18d72595cd', - }, - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: { - status: 200, - message: 'Request Processed Successfully', - destinationResponse: { response: '', status: 200 }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_remarketing_lists', - description: 'Test 1', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v15/customers/7693729834/offlineUserDataJobs', - headers: { - Authorization: 'Bearer dummy-access', - 'Content-Type': 'application/json', - 'developer-token': 'dummy-dev-token', - }, - params: { - listId: '709078448', - customerId: '7693729833', - destination: 'google_adwords_remarketing_lists', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, - }, - body: { - JSON: { - enablePartialFailure: true, - operations: [ - { - create: { - userIdentifiers: [ - { - hashedEmail: 'abcd@testmail.com', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - status: 400, - message: - 'Request contains an invalid argument. during ga_audience response transformation', - destinationResponse: { - error: { - code: 400, - details: [ - { - '@type': 'type.googleapis.com/google.ads.googleads.v9.errors.GoogleAdsFailure', - errors: [ - { - errorCode: { - offlineUserDataJobError: 'INVALID_SHA256_FORMAT', - }, - message: 'The SHA256 encoded value is malformed.', - location: { - fieldPathElements: [ - { fieldName: 'operations', index: 0 }, - { fieldName: 'remove' }, - { fieldName: 'user_identifiers', index: 0 }, - { fieldName: 'hashed_email' }, - ], - }, - }, - ], - }, - ], - message: 'Request contains an invalid argument.', - status: 'INVALID_ARGUMENT', - }, - }, - statTags: { - destType: 'GOOGLE_ADWORDS_REMARKETING_LISTS', - errorCategory: 'network', - destinationId: 'Non-determininable', - workspaceId: 'Non-determininable', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_remarketing_lists', - description: 'Test 2', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', - headers: { - Authorization: 'Bearer dummy-access', - 'Content-Type': 'application/json', - 'developer-token': 'dummy-dev-token', - }, - params: { - listId: '709078448', - customerId: '7693729833', - destination: 'google_adwords_remarketing_lists', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, - }, - body: { - JSON: { - enablePartialFailure: true, - operations: [ - { - remove: { - userIdentifiers: [ - { - hashedEmail: - '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', - }, - { - hashedPhoneNumber: - '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', - }, - { - hashedEmail: - '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', - }, - { - hashedPhoneNumber: - '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', - }, - { - addressInfo: { - hashedFirstName: - 'e56d336922eaab3be8c1244dbaa713e134a8eba50ddbd4f50fd2fe18d72595cd', - }, - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: { - status: 200, - message: 'Request Processed Successfully', - destinationResponse: { response: '', status: 200 }, - }, - }, - }, - }, - }, -]; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + +export const data = [...testScenariosForV0API, ...testScenariosForV1API]; From e43b83dfc192c03317d26535f67f64c41eafc709 Mon Sep 17 00:00:00 2001 From: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Date: Tue, 12 Mar 2024 13:18:25 +0530 Subject: [PATCH 12/35] refactor: trade desk proxy testcases (#3175) * refactor: trade desk proxy testcases * test: add network failure testcases * doc: add trade desk error doc * chore: format koala testcases --- .../destinations/koala/processor/data.ts | 58 ++-- .../destinations/koala/router/data.ts | 88 +++--- .../destinations/the_trade_desk/common.ts | 23 +- .../the_trade_desk/delivery/business.ts | 175 +++++++++++ .../the_trade_desk/delivery/data.ts | 251 +-------------- .../the_trade_desk/delivery/other.ts | 290 ++++++++++++++++++ .../destinations/the_trade_desk/mocks.ts | 9 + 7 files changed, 575 insertions(+), 319 deletions(-) create mode 100644 test/integrations/destinations/the_trade_desk/delivery/business.ts create mode 100644 test/integrations/destinations/the_trade_desk/delivery/other.ts diff --git a/test/integrations/destinations/koala/processor/data.ts b/test/integrations/destinations/koala/processor/data.ts index 9c1ea97a77..b866353066 100644 --- a/test/integrations/destinations/koala/processor/data.ts +++ b/test/integrations/destinations/koala/processor/data.ts @@ -31,7 +31,7 @@ export const data = [ value: 10, }, context: { - network: 'wifi' + network: 'wifi', }, originalTimestamp: '2024-01-23T08:35:17.562Z', sentAt: '2024-01-23T08:35:17.562Z', @@ -58,20 +58,22 @@ export const data = [ JSON: { ip: '192.11.22.33', email: 'johndoe@somemail.com', - events: [{ - type: 'track', - event: 'User Signed Up', - sent_at: '2024-01-23T08:35:17.562Z', - message_id: '84e26acc-56a5-4835-8233-591137fca468', - properties: { - email: 'johndoe@somemail.com', - label: 'test', - value: 10, - }, - context: { - network: 'wifi' + events: [ + { + type: 'track', + event: 'User Signed Up', + sent_at: '2024-01-23T08:35:17.562Z', + message_id: '84e26acc-56a5-4835-8233-591137fca468', + properties: { + email: 'johndoe@somemail.com', + label: 'test', + value: 10, + }, + context: { + network: 'wifi', + }, }, - }] + ], }, }, endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', @@ -127,7 +129,7 @@ export const data = [ postalCode: '94107', }, email: 'johndoe@somemail.com', - ko_profile_id: 'xxxx-2222-xxxx-xxxx' + ko_profile_id: 'xxxx-2222-xxxx-xxxx', }, originalTimestamp: '2024-01-23T08:35:17.342Z', sentAt: '2024-01-23T08:35:35.234Z', @@ -153,20 +155,22 @@ export const data = [ JSON: { email: 'johndoe@somemail.com', profile_id: 'xxxx-2222-xxxx-xxxx', - identifies: [{ - type: 'identify', - sent_at: '2024-01-23T08:35:17.342Z', - traits: { - FirstName: 'John', - LastName: 'Doe', - address: { - city: 'San Francisco', - state: 'CA', - postalCode: '94107', + identifies: [ + { + type: 'identify', + sent_at: '2024-01-23T08:35:17.342Z', + traits: { + FirstName: 'John', + LastName: 'Doe', + address: { + city: 'San Francisco', + state: 'CA', + postalCode: '94107', + }, + email: 'johndoe@somemail.com', }, - email: 'johndoe@somemail.com', }, - }], + ], }, }, endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', diff --git a/test/integrations/destinations/koala/router/data.ts b/test/integrations/destinations/koala/router/data.ts index fb0db3e3fb..f998f48dc4 100644 --- a/test/integrations/destinations/koala/router/data.ts +++ b/test/integrations/destinations/koala/router/data.ts @@ -27,14 +27,14 @@ export const data = [ type: 'track', messageId: '84e26acc-56a5-4835-8233-591137fca468', traits: { - email: 'johndoe@somemail.com' + email: 'johndoe@somemail.com', }, properties: { label: 'test', value: 10, }, context: { - network: 'wifi' + network: 'wifi', }, originalTimestamp: '2024-01-23T08:35:17.562Z', sentAt: '2024-01-23T08:35:17.562Z', @@ -44,8 +44,8 @@ export const data = [ jobId: 1, userId: 'u1', destinationId: 'destId', - workspaceId: 'wspId' - } + workspaceId: 'wspId', + }, }, { destination: { @@ -65,14 +65,14 @@ export const data = [ type: 'track', messageId: '8bc79b03-2a5c-4615-b2da-54c0aaaaaae8', traits: { - ko_profile_id: '123456' + ko_profile_id: '123456', }, properties: { attr1: 'foo', - attr2: 'bar' + attr2: 'bar', }, context: { - network: 'wifi' + network: 'wifi', }, originalTimestamp: '2024-01-23T08:35:17.562Z', sentAt: '2024-01-23T08:35:17.562Z', @@ -82,14 +82,14 @@ export const data = [ jobId: 2, userId: 'u1', destinationId: 'destId', - workspaceId: 'wspId' - } + workspaceId: 'wspId', + }, }, ], destType: 'koala', }, - method: 'POST' - } + method: 'POST', + }, }, output: { response: { @@ -105,19 +105,21 @@ export const data = [ JSON: { ip: '192.11.22.33', email: 'johndoe@somemail.com', - events: [{ - type: 'track', - event: 'User Signed Up', - sent_at: '2024-01-23T08:35:17.562Z', - message_id: '84e26acc-56a5-4835-8233-591137fca468', - properties: { - label: 'test', - value: 10, + events: [ + { + type: 'track', + event: 'User Signed Up', + sent_at: '2024-01-23T08:35:17.562Z', + message_id: '84e26acc-56a5-4835-8233-591137fca468', + properties: { + label: 'test', + value: 10, + }, + context: { + network: 'wifi', + }, }, - context: { - network: 'wifi' - }, - }] + ], }, }, endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', @@ -153,19 +155,21 @@ export const data = [ JSON: { ip: '192.11.55.1', profile_id: '123456', - events: [{ - type: 'track', - event: 'User Deleted account', - sent_at: '2024-01-23T08:35:17.562Z', - message_id: '8bc79b03-2a5c-4615-b2da-54c0aaaaaae8', - properties: { - attr1: 'foo', - attr2: 'bar' - }, - context: { - network: 'wifi' + events: [ + { + type: 'track', + event: 'User Deleted account', + sent_at: '2024-01-23T08:35:17.562Z', + message_id: '8bc79b03-2a5c-4615-b2da-54c0aaaaaae8', + properties: { + attr1: 'foo', + attr2: 'bar', + }, + context: { + network: 'wifi', + }, }, - }] + ], }, }, endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', @@ -191,10 +195,10 @@ export const data = [ }, }, }, - } - ] - } - } - } - } -] + }, + ], + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/the_trade_desk/common.ts b/test/integrations/destinations/the_trade_desk/common.ts index 28f46df829..08ba0ee6ea 100644 --- a/test/integrations/destinations/the_trade_desk/common.ts +++ b/test/integrations/destinations/the_trade_desk/common.ts @@ -5,8 +5,7 @@ const destTypeInUpperCase = 'THE_TRADE_DESK'; const advertiserId = 'test-advertiser-id'; const dataProviderId = 'rudderstack'; const segmentName = 'test-segment'; - -const trackerId = 'test-trackerId'; +const firstPartyDataEndpoint = 'https://sin-data.adsrvr.org/data/advertiser'; const sampleDestination: Destination = { Config: { @@ -48,6 +47,22 @@ const sampleContext = { sources: sampleSource, }; +const proxyV1AbortableErrorStatTags = { + destType: destTypeInUpperCase, + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +const { errorType: _, ...proxyV1PlatformErrorStatTags } = proxyV1AbortableErrorStatTags; +proxyV1PlatformErrorStatTags.errorCategory = 'platform'; + +const proxyV1RetryableErrorStatTags = { ...proxyV1AbortableErrorStatTags, errorType: 'retryable' }; + export { destType, destTypeInUpperCase, @@ -56,4 +71,8 @@ export { segmentName, sampleDestination, sampleContext, + proxyV1AbortableErrorStatTags, + proxyV1PlatformErrorStatTags, + proxyV1RetryableErrorStatTags, + firstPartyDataEndpoint, }; diff --git a/test/integrations/destinations/the_trade_desk/delivery/business.ts b/test/integrations/destinations/the_trade_desk/delivery/business.ts new file mode 100644 index 0000000000..0406a5f0bc --- /dev/null +++ b/test/integrations/destinations/the_trade_desk/delivery/business.ts @@ -0,0 +1,175 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload, generateMetadata } from '../../../testUtils'; +import { + destType, + advertiserId, + dataProviderId, + segmentName, + sampleDestination, + proxyV1AbortableErrorStatTags, + firstPartyDataEndpoint, +} from '../common'; + +const validRequestPayload1 = { + AdvertiserId: advertiserId, + DataProviderId: dataProviderId, + Items: [ + { + DAID: 'test-daid-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + { + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + UID2: 'test-uid2-1', + }, + { + DAID: 'test-daid-2', + Data: [ + { + Name: segmentName, + TTLInMinutes: 0, + }, + ], + }, + { + Data: [ + { + Name: segmentName, + TTLInMinutes: 0, + }, + ], + UID2: 'test-uid2-2', + }, + ], +}; + +const invalidRequestPayload1 = { + AdvertiserId: advertiserId, + DataProviderId: dataProviderId, + Items: [ + { + DAID: 'test-daid', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + { + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + UID2: 'test-invalid-uid2', + }, + ], +}; + +const metadataArray = [generateMetadata(1)]; + +export const businessProxyV1: ProxyV1TestData[] = [ + { + id: 'ttd_v1_scenario_1', + name: destType, + description: + '[Proxy v1 API] :: Test for a valid request - Successful delivery of Add/Remove IDs to Trade Desk', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: {}, + params: {}, + JSON: validRequestPayload1, + endpoint: firstPartyDataEndpoint, + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: '{}', + }, + ], + }, + }, + }, + }, + }, + { + id: 'ttd_v1_scenario_2', + name: destType, + description: + '[Proxy v1 API] :: Test for invalid ID - where the destination responds with 200 with invalid ID', + successCriteria: 'Should return 400 with error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: {}, + params: {}, + JSON: invalidRequestPayload1, + endpoint: firstPartyDataEndpoint, + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + 'Request failed with status: 200 due to {"FailedLines":[{"ErrorCode":"MissingUserId","Message":"Invalid UID2, item #2"}]}', + response: [ + { + error: + '{"FailedLines":[{"ErrorCode":"MissingUserId","Message":"Invalid UID2, item #2"}]}', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: proxyV1AbortableErrorStatTags, + status: 400, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/the_trade_desk/delivery/data.ts b/test/integrations/destinations/the_trade_desk/delivery/data.ts index 320eb6dcfe..5099eafce7 100644 --- a/test/integrations/destinations/the_trade_desk/delivery/data.ts +++ b/test/integrations/destinations/the_trade_desk/delivery/data.ts @@ -1,248 +1,3 @@ -import { - destType, - destTypeInUpperCase, - advertiserId, - dataProviderId, - segmentName, - sampleDestination, -} from '../common'; - -beforeAll(() => { - process.env.THE_TRADE_DESK_DATA_PROVIDER_SECRET_KEY = 'mockedDataProviderSecretKey'; -}); - -afterAll(() => { - delete process.env.THE_TRADE_DESK_DATA_PROVIDER_SECRET_KEY; -}); - -export const data = [ - { - name: destType, - description: 'Successful delivery of Add/Remove IDs to/from Trade Desk', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - destinationConfig: sampleDestination.Config, - body: { - JSON: { - AdvertiserId: advertiserId, - DataProviderId: dataProviderId, - Items: [ - { - DAID: 'test-daid-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - { - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - UID2: 'test-uid2-1', - }, - { - DAID: 'test-daid-2', - Data: [ - { - Name: segmentName, - TTLInMinutes: 0, - }, - ], - }, - { - Data: [ - { - Name: segmentName, - TTLInMinutes: 0, - }, - ], - UID2: 'test-uid2-2', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: { - destinationResponse: { - response: {}, - status: 200, - }, - message: 'Request Processed Successfully', - status: 200, - }, - }, - }, - }, - }, - { - name: destType, - description: 'Error response from The Trade Desk due to invalid IDs', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - destinationConfig: sampleDestination.Config, - body: { - JSON: { - AdvertiserId: advertiserId, - DataProviderId: dataProviderId, - Items: [ - { - DAID: 'test-daid', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - { - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - UID2: 'test-invalid-uid2', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - destinationResponse: { - response: { - FailedLines: [{ ErrorCode: 'MissingUserId', Message: 'Invalid UID2, item #2' }], - }, - status: 200, - }, - message: - 'Request failed with status: 200 due to {"FailedLines":[{"ErrorCode":"MissingUserId","Message":"Invalid UID2, item #2"}]}', - statTags: { - destType: destTypeInUpperCase, - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - status: 400, - }, - }, - }, - }, - }, - { - name: destType, - description: - 'Missing advertiser secret key in destination config from proxy request from server', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - body: { - JSON: { - AdvertiserId: advertiserId, - DataProviderId: dataProviderId, - Items: [ - { - DAID: 'test-daid-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - destinationResponse: '', - message: 'Advertiser secret key is missing in destination config. Aborting', - statTags: { - destType: destTypeInUpperCase, - destinationId: 'Non-determininable', - errorCategory: 'platform', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - status: 400, - }, - }, - }, - }, - }, -]; +import { businessProxyV1 } from './business'; +import { otherProxyV1 } from './other'; +export const data = [...businessProxyV1, ...otherProxyV1]; diff --git a/test/integrations/destinations/the_trade_desk/delivery/other.ts b/test/integrations/destinations/the_trade_desk/delivery/other.ts new file mode 100644 index 0000000000..bed10e6ec5 --- /dev/null +++ b/test/integrations/destinations/the_trade_desk/delivery/other.ts @@ -0,0 +1,290 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload, generateMetadata } from '../../../testUtils'; +import { + destType, + advertiserId, + dataProviderId, + segmentName, + proxyV1PlatformErrorStatTags, + proxyV1RetryableErrorStatTags, + firstPartyDataEndpoint, + sampleDestination, +} from '../common'; +import { envMock } from '../mocks'; + +envMock(); + +const validRequestPayload1 = { + AdvertiserId: advertiserId, + DataProviderId: dataProviderId, + Items: [ + { + DAID: 'test-daid-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + ], +}; + +const metadataArray = [generateMetadata(1)]; + +// https://partner.thetradedesk.com/v3/portal/data/doc/post-data-advertiser-external#error-codes-messages +export const otherProxyV1: ProxyV1TestData[] = [ + { + id: 'ttd_v1_other_scenario_1', + name: destType, + description: + '[Proxy v1 API] :: Missing advertiser secret key in destination config from proxy request from server', + successCriteria: 'Should return 400 with platform error', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: {}, + params: {}, + JSON: validRequestPayload1, + endpoint: firstPartyDataEndpoint, + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Advertiser secret key is missing in destination config. Aborting', + response: [ + { + error: 'Advertiser secret key is missing in destination config. Aborting', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: proxyV1PlatformErrorStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'ttd_v1_other_scenario_2', + name: destType, + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_service_not_available', + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 503, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: + 'Request failed with status: 503 due to {"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + status: 503, + }, + }, + }, + }, + }, + { + id: 'ttd_v1_other_scenario_3', + name: destType, + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_internal_server_error', + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'Request failed with status: 500 due to "Internal Server Error"', + status: 500, + }, + }, + }, + }, + }, + { + id: 'ttd_v1_other_scenario_4', + name: destType, + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout"', + statusCode: 504, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'Request failed with status: 504 due to "Gateway Timeout"', + status: 504, + }, + }, + }, + }, + }, + { + id: 'ttd_v1_other_scenario_5', + name: destType, + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_null_response', + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'Request failed with status: 500 due to ""', + status: 500, + }, + }, + }, + }, + }, + { + id: 'ttd_v1_other_scenario_6', + name: destType, + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'Request failed with status: 500 due to ""', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/the_trade_desk/mocks.ts b/test/integrations/destinations/the_trade_desk/mocks.ts index ddcbebae88..bf9d910d42 100644 --- a/test/integrations/destinations/the_trade_desk/mocks.ts +++ b/test/integrations/destinations/the_trade_desk/mocks.ts @@ -3,3 +3,12 @@ import config from '../../../../src/cdk/v2/destinations/the_trade_desk/config'; export const defaultMockFns = () => { jest.replaceProperty(config, 'MAX_REQUEST_SIZE_IN_BYTES', 250); }; + +export const envMock = () => { + process.env.THE_TRADE_DESK_DATA_PROVIDER_SECRET_KEY = 'mockedDataProviderSecretKey'; + + // Clean up after all tests are done + afterAll(() => { + delete process.env.THE_TRADE_DESK_DATA_PROVIDER_SECRET_KEY; + }); +}; From 229ce473af1ddd62d946bea1b018c882b142a5ef Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Tue, 12 Mar 2024 17:53:51 +0530 Subject: [PATCH 13/35] fix: send proper status to server in cm360 (#3127) --- .../campaign_manager/networkHandler.js | 4 ++-- .../campaign_manager/dataDelivery/oauth.ts | 24 +++++++++---------- .../campaign_manager/dataDelivery/other.ts | 8 +++---- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/v1/destinations/campaign_manager/networkHandler.js b/src/v1/destinations/campaign_manager/networkHandler.js index 79f7e7f93b..300b5f9676 100644 --- a/src/v1/destinations/campaign_manager/networkHandler.js +++ b/src/v1/destinations/campaign_manager/networkHandler.js @@ -69,7 +69,7 @@ const responseHandler = (responseParams) => { const errorMessage = response.error?.message || 'unknown error format'; for (const metadata of rudderJobMetadata) { responseWithIndividualEvents.push({ - statusCode: 500, + statusCode: status, metadata, error: errorMessage, }); @@ -77,7 +77,7 @@ const responseHandler = (responseParams) => { throw new TransformerProxyError( `Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation`, - 500, + status, { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), }, diff --git a/test/integrations/destinations/campaign_manager/dataDelivery/oauth.ts b/test/integrations/destinations/campaign_manager/dataDelivery/oauth.ts index 929af485d8..288a06bfe6 100644 --- a/test/integrations/destinations/campaign_manager/dataDelivery/oauth.ts +++ b/test/integrations/destinations/campaign_manager/dataDelivery/oauth.ts @@ -326,14 +326,14 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ }, output: { response: { - status: 500, + status: 401, body: { output: { response: [ { error: '{"error":{"code":401,"message":"Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.","errors":[{"message":"Login Required.","domain":"global","reason":"required","location":"Authorization","locationType":"header"}],"status":"UNAUTHENTICATED","details":[{"@type":"type.googleapis.com/google.rpc.ErrorInfo","reason":"CREDENTIALS_MISSING","domain":"googleapis.com","metadata":{"method":"google.ads.xfa.op.v4.DfareportingConversions.Batchinsert","service":"googleapis.com"}}]}}', - statusCode: 500, + statusCode: 401, metadata: { jobId: 1, attemptNum: 1, @@ -361,7 +361,7 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ authErrorCategory: 'REFRESH_TOKEN', message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 401, }, }, }, @@ -389,14 +389,14 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ }, output: { response: { - status: 500, + status: 403, body: { output: { response: [ { error: '{"error":{"code":403,"message":"Request had insufficient authentication scopes.","errors":[{"message":"Insufficient Permission","domain":"global","reason":"insufficientPermissions"}],"status":"PERMISSION_DENIED","details":[{"@type":"type.googleapis.com/google.rpc.ErrorInfo","reason":"ACCESS_TOKEN_SCOPE_INSUFFICIENT","domain":"googleapis.com","metadata":{"service":"gmail.googleapis.com","method":"caribou.api.proto.MailboxService.GetProfile"}}]}}', - statusCode: 500, + statusCode: 403, metadata: { jobId: 1, attemptNum: 1, @@ -424,7 +424,7 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ authErrorCategory: 'AUTH_STATUS_INACTIVE', message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 403, }, }, }, @@ -452,14 +452,14 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ }, output: { response: { - status: 500, + status: 403, body: { output: { response: [ { error: '{"error":{"code":403,"message":"invalid_grant","error_description":"Bad accesss"}}', - statusCode: 500, + statusCode: 403, metadata: { jobId: 1, attemptNum: 1, @@ -487,7 +487,7 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ authErrorCategory: 'AUTH_STATUS_INACTIVE', message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 403, }, }, }, @@ -514,14 +514,14 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ }, output: { response: { - status: 500, + status: 401, body: { output: { response: [ { error: '{"error":"unauthorized","error_description":"Access token expired: 2020-10-20T12:00:00.000Z"}', - statusCode: 500, + statusCode: 401, metadata: { jobId: 1, attemptNum: 1, @@ -549,7 +549,7 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ authErrorCategory: 'REFRESH_TOKEN', message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 401, }, }, }, diff --git a/test/integrations/destinations/campaign_manager/dataDelivery/other.ts b/test/integrations/destinations/campaign_manager/dataDelivery/other.ts index 709f55a4c0..1c0c45728c 100644 --- a/test/integrations/destinations/campaign_manager/dataDelivery/other.ts +++ b/test/integrations/destinations/campaign_manager/dataDelivery/other.ts @@ -260,7 +260,7 @@ export const otherScenariosV1: ProxyV1TestData[] = [ { error: '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', - statusCode: 500, + statusCode: 503, metadata: { jobId: 1, attemptNum: 1, @@ -287,7 +287,7 @@ export const otherScenariosV1: ProxyV1TestData[] = [ }, message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 503, }, }, }, @@ -376,7 +376,7 @@ export const otherScenariosV1: ProxyV1TestData[] = [ response: [ { error: '"Gateway Timeout"', - statusCode: 500, + statusCode: 504, metadata: { jobId: 1, attemptNum: 1, @@ -403,7 +403,7 @@ export const otherScenariosV1: ProxyV1TestData[] = [ }, message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 504, }, }, }, From 5dd5142a5d55a5eca5517c75d5356df416308afc Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:56:56 +0530 Subject: [PATCH 14/35] chore: adding proxy test cases for salesforce oauth (#3170) * chore: add initial business tests * chore: cleanup * chore: add other scenario test, refactor * chore: address commentsx1 * chore: move test to other * chore: address commentsx2 * chore: adding proxy test cases for salesforce oauth access token refresh * code review suggestion Co-authored-by: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> * fix: review comments address --------- Co-authored-by: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> --- .../salesforce_oauth/dataDelivery/data.ts | 3 + .../salesforce_oauth/dataDelivery/oauth.ts | 174 ++++++++++++++++++ .../destinations/salesforce_oauth/network.ts | 51 +++++ 3 files changed, 228 insertions(+) create mode 100644 test/integrations/destinations/salesforce_oauth/dataDelivery/data.ts create mode 100644 test/integrations/destinations/salesforce_oauth/dataDelivery/oauth.ts create mode 100644 test/integrations/destinations/salesforce_oauth/network.ts diff --git a/test/integrations/destinations/salesforce_oauth/dataDelivery/data.ts b/test/integrations/destinations/salesforce_oauth/dataDelivery/data.ts new file mode 100644 index 0000000000..bed8eec8db --- /dev/null +++ b/test/integrations/destinations/salesforce_oauth/dataDelivery/data.ts @@ -0,0 +1,3 @@ +import { testScenariosForV1API } from './oauth'; + +export const data = [...testScenariosForV1API]; diff --git a/test/integrations/destinations/salesforce_oauth/dataDelivery/oauth.ts b/test/integrations/destinations/salesforce_oauth/dataDelivery/oauth.ts new file mode 100644 index 0000000000..55eaa9cca1 --- /dev/null +++ b/test/integrations/destinations/salesforce_oauth/dataDelivery/oauth.ts @@ -0,0 +1,174 @@ +import { ProxyMetdata } from '../../../../../src/types'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const commonHeadersForWrongToken = { + Authorization: 'Bearer expiredAccessToken', + 'Content-Type': 'application/json', +}; + +const commonHeadersForRightToken = { + Authorization: 'Bearer correctAccessToken', + 'Content-Type': 'application/json', +}; +const params = { destination: 'salesforce_oauth' }; + +const users = [ + { + Email: 'danis.archurav@sbermarket.ru', + Company: 'itus.ru', + LastName: 'Danis', + FirstName: 'Archurav', + LeadSource: 'App Signup', + account_type__c: 'free_trial', + }, +]; + +const statTags = { + retryable: { + destType: 'SALESFORCE_OAUTH', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, +}; + +const commonRequestParametersWithWrongToken = { + headers: commonHeadersForWrongToken, + JSON: users[0], + params, +}; + +const commonRequestParametersWithRightToken = { + headers: commonHeadersForRightToken, + JSON: users[0], + params, +}; + +export const proxyMetdataWithSecretWithWrongAccessToken: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: { + access_token: 'expiredAccessToken', + instanceUrl: 'https://rudderstack.my.salesforce_oauth.com', + }, + destInfo: { authKey: 'dummyDestinationId' }, + dontBatch: false, +}; + +export const proxyMetdataWithSecretWithRightAccessToken: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: { + access_token: 'expiredRightToken', + instanceUrl: 'https://rudderstack.my.salesforce_oauth.com', + }, + destInfo: { authKey: 'dummyDestinationId' }, + dontBatch: false, +}; + +export const reqMetadataArrayWithWrongSecret = [proxyMetdataWithSecretWithWrongAccessToken]; +export const reqMetadataArray = [proxyMetdataWithSecretWithRightAccessToken]; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'salesforce_v1_scenario_1', + name: 'salesforce_oauth', + description: '[Proxy v1 API] :: Test with expired access token scenario', + successCriteria: + 'Should return 5XX with error category REFRESH_TOKEN and Session expired or invalid, INVALID_SESSION_ID', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParametersWithWrongToken, + endpoint: + 'https://rudderstack.my.salesforce_oauth.com/services/data/v50.0/sobjects/Lead/20', + }, + reqMetadataArrayWithWrongSecret, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + authErrorCategory: 'REFRESH_TOKEN', + message: + 'Salesforce Request Failed - due to "INVALID_SESSION_ID", (Retryable) during Salesforce Response Handling', + response: [ + { + error: + '[{"message":"Session expired or invalid","errorCode":"INVALID_SESSION_ID"}]', + metadata: proxyMetdataWithSecretWithWrongAccessToken, + statusCode: 500, + }, + ], + statTags: statTags.retryable, + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_scenario_2', + name: 'salesforce', + description: + '[Proxy v1 API] :: Test for a valid request - Lead creation with existing unchanged leadId and unchanged data', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParametersWithRightToken, + endpoint: + 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/existing_unchanged_leadId', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request for destination: salesforce Processed Successfully', + response: [ + { + error: '{"statusText":"No Content"}', + metadata: proxyMetdataWithSecretWithRightAccessToken, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/salesforce_oauth/network.ts b/test/integrations/destinations/salesforce_oauth/network.ts new file mode 100644 index 0000000000..ae5f9d3fe4 --- /dev/null +++ b/test/integrations/destinations/salesforce_oauth/network.ts @@ -0,0 +1,51 @@ +const headerWithWrongAccessToken = { + Authorization: 'Bearer expiredAccessToken', + 'Content-Type': 'application/json', +}; + +const headerWithRightAccessToken = { + Authorization: 'Bearer correctAccessToken', + 'Content-Type': 'application/json', +}; + +const dataValue = { + Email: 'danis.archurav@sbermarket.ru', + Company: 'itus.ru', + LastName: 'Danis', + FirstName: 'Archurav', + LeadSource: 'App Signup', + account_type__c: 'free_trial', +}; + +const businessMockData = [ + { + description: 'Mock response from destination depicting an expired access token', + httpReq: { + method: 'post', + url: 'https://rudderstack.my.salesforce_oauth.com/services/data/v50.0/sobjects/Lead/20', + headers: headerWithWrongAccessToken, + data: dataValue, + params: { destination: 'salesforce_oauth' }, + }, + httpRes: { + data: [{ message: 'Session expired or invalid', errorCode: 'INVALID_SESSION_ID' }], + status: 401, + }, + }, + { + description: + 'Mock response from destination depicting a valid lead request, with no changed data', + httpReq: { + method: 'post', + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/existing_unchanged_leadId', + data: dataValue, + headers: headerWithRightAccessToken, + }, + httpRes: { + data: { statusText: 'No Content' }, + status: 204, + }, + }, +]; + +export const networkCallsData = [...businessMockData]; From d2eba21191dc4f7b610414158af68e5533016014 Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Tue, 12 Mar 2024 22:26:07 +0530 Subject: [PATCH 15/35] fix: upload js coverage to codecov (#3179) --- .github/workflows/dt-test-and-report-code-coverage.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/dt-test-and-report-code-coverage.yml b/.github/workflows/dt-test-and-report-code-coverage.yml index 51a9f8c9ee..4375b3383e 100644 --- a/.github/workflows/dt-test-and-report-code-coverage.yml +++ b/.github/workflows/dt-test-and-report-code-coverage.yml @@ -41,6 +41,8 @@ jobs: - name: Upload Coverage Reports to Codecov uses: codecov/codecov-action@v4.0.1 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: directory: ./reports/coverage From 67fcdd3f4e8c60b545e7bf7164dde0d1df7b6e2c Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Wed, 13 Mar 2024 07:33:22 +0530 Subject: [PATCH 16/35] chore: resolve code injection vulnerabilities (#3164) --- src/controllers/bulkUpload.ts | 10 +++---- src/util/fetchDestinationHandlers.ts | 42 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 src/util/fetchDestinationHandlers.ts diff --git a/src/controllers/bulkUpload.ts b/src/controllers/bulkUpload.ts index babb8b6db1..dbd77dc07f 100644 --- a/src/controllers/bulkUpload.ts +++ b/src/controllers/bulkUpload.ts @@ -1,14 +1,14 @@ /* eslint-disable global-require, import/no-dynamic-require, @typescript-eslint/no-unused-vars */ import { client as errNotificationClient } from '../util/errorNotifier'; import logger from '../logger'; +import { + getJobStatusHandler, + getPollStatusHandler, + getDestFileUploadHandler, +} from '../util/fetchDestinationHandlers'; import { CatchErr, ContextBodySimple } from '../util/types'; // TODO: To be refactored and redisgned -const getDestFileUploadHandler = (version, dest) => - require(`../${version}/destinations/${dest}/fileUpload`); -const getPollStatusHandler = (version, dest) => require(`../${version}/destinations/${dest}/poll`); -const getJobStatusHandler = (version, dest) => - require(`../${version}/destinations/${dest}/fetchJobStatus`); const ERROR_MESSAGE_PROCESSOR_STRING = 'Error occurred while processing payload.'; const getCommonMetadata = (ctx) => diff --git a/src/util/fetchDestinationHandlers.ts b/src/util/fetchDestinationHandlers.ts new file mode 100644 index 0000000000..2661ef2e68 --- /dev/null +++ b/src/util/fetchDestinationHandlers.ts @@ -0,0 +1,42 @@ +import * as V0MarketoBulkUploadFileUpload from '../v0/destinations/marketo_bulk_upload/fileUpload'; +import * as V0MarketoBulkUploadPollStatus from '../v0/destinations/marketo_bulk_upload/poll'; +import * as V0MarketoBulkUploadJobStatus from '../v0/destinations/marketo_bulk_upload/fetchJobStatus'; + +const fileUploadHandlers = { + v0: { + marketo_bulk_upload: V0MarketoBulkUploadFileUpload, + }, +}; + +const pollStatusHandlers = { + v0: { + marketo_bulk_upload: V0MarketoBulkUploadPollStatus, + }, +}; + +const jobStatusHandlers = { + v0: { + marketo_bulk_upload: V0MarketoBulkUploadJobStatus, + }, +}; + +export const getDestFileUploadHandler = (version, dest) => { + if (fileUploadHandlers[version] && fileUploadHandlers[version][dest]) { + return fileUploadHandlers[version][dest]; + } + return undefined; +}; + +export const getPollStatusHandler = (version, dest) => { + if (pollStatusHandlers[version] && pollStatusHandlers[version][dest]) { + return pollStatusHandlers[version][dest]; + } + return undefined; +}; + +export const getJobStatusHandler = (version, dest) => { + if (jobStatusHandlers[version] && jobStatusHandlers[version][dest]) { + return jobStatusHandlers[version][dest]; + } + return undefined; +}; From ecbe61abef3f02a8ab2bd66f646ae088c4b50d9d Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Wed, 13 Mar 2024 12:33:49 +0530 Subject: [PATCH 17/35] chore: refactor intercom proxy tests to new component structure (#3088) * chore: refactor intercom proxy tests to new component structure * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes --- .../intercom/dataDelivery/business.ts | 615 ++++++++++++++++++ .../intercom/dataDelivery/data.ts | 96 +-- .../intercom/dataDelivery/other.ts | 384 +++++++++++ .../destinations/intercom/network.ts | 358 +++++++--- 4 files changed, 1271 insertions(+), 182 deletions(-) create mode 100644 test/integrations/destinations/intercom/dataDelivery/business.ts create mode 100644 test/integrations/destinations/intercom/dataDelivery/other.ts diff --git a/test/integrations/destinations/intercom/dataDelivery/business.ts b/test/integrations/destinations/intercom/dataDelivery/business.ts new file mode 100644 index 0000000000..2490041832 --- /dev/null +++ b/test/integrations/destinations/intercom/dataDelivery/business.ts @@ -0,0 +1,615 @@ +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; + +const commonHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const unauthorizedResponseHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer invalidApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const createUserPayload = { + email: 'test_1@test.com', + phone: '9876543210', + name: 'Test Name', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const conflictUserPayload = { + email: 'test_1@test.com', + name: 'Rudder Labs', + signed_up_at: 1601496060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_2', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const updateUserPayload = { + email: 'test_1@test.com', + phone: '9876543211', + name: 'Sample Name', + signed_up_at: 1601493060, + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const createCompanyPayload = { + company_id: 'rudderlabs', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', + custom_attributes: { isOpenSource: true }, +}; + +const sendMessagePayload = { + from: { + type: 'user', + id: 'id@1', + }, + body: 'heyy, how are you', + referer: 'https://twitter.com/bob', +}; + +const createUserRequestParameters = { + JSON: createUserPayload, + headers: commonHeaders, +}; + +const updateUserRequestParameters = { + JSON: updateUserPayload, + headers: commonHeaders, +}; + +const createCompanyRequestParameters = { + JSON: createCompanyPayload, + headers: commonHeaders, +}; + +const sendMessageRequestParameters = { + JSON: sendMessagePayload, + headers: commonHeaders, +}; + +const metadataArray = [generateMetadata(1)]; + +export const testScenariosForV0API = [ + { + id: 'intercom_v0_other_scenario_1', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario to test Invalid Credentials Handling during Destination Authentication', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...createUserRequestParameters, + headers: unauthorizedResponseHeaders, + endpoint: 'https://api.intercom.io/users/test1', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'unauthorized', + message: 'Access Token Invalid', + }, + ], + request_id: 'request123', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_2', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario to test Malformed Payload Response Handling from Destination', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...createCompanyRequestParameters, + endpoint: 'https://api.eu.intercom.io/companies', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'parameter_invalid', + message: "Custom attribute 'isOpenSource' does not exist", + }, + ], + request_id: 'request_1', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_3', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario to test Plan-Restricted Response Handling from Destination', + successCriteria: 'Should return 403 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...sendMessageRequestParameters, + endpoint: 'https://api.intercom.io/messages', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'api_plan_restricted', + message: 'Active subscription needed.', + }, + ], + request_id: 'request124', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 403, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_4', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario to test Rate Limit Exceeded Handling from Destination', + successCriteria: 'Should return 429 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...updateUserRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 429, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'rate_limit_exceeded', + message: 'The rate limit for the App has been exceeded', + }, + ], + request_id: 'request125', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 429, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_5', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario to test Conflict User Handling from Destination', + successCriteria: 'Should return 409 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + JSON: conflictUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/contacts', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 409, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'conflict', + message: 'A contact matching those details already exists with id=test1', + }, + ], + request_id: 'request126', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 409, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_6', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario to test Unsupported Media Handling from Destination', + successCriteria: 'Should return 406 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + JSON: createUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/users', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 406, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'media_type_not_acceptable', + message: 'The Accept header should send a media type of application/json', + }, + ], + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 406, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API = [ + { + id: 'intercom_v1_other_scenario_1', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario to test Invalid Credentials Handling during Destination Authentication', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...createUserRequestParameters, + headers: unauthorizedResponseHeaders, + endpoint: 'https://api.intercom.io/users/test1', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request123","errors":[{"code":"unauthorized","message":"Access Token Invalid"}]}', + metadata: generateMetadata(1), + statusCode: 401, + }, + ], + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_2', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario to test Malformed Payload Response Handling from Destination', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...createCompanyRequestParameters, + endpoint: 'https://api.eu.intercom.io/companies', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request_1","errors":[{"code":"parameter_invalid","message":"Custom attribute \'isOpenSource\' does not exist"}]}', + metadata: generateMetadata(1), + statusCode: 401, + }, + ], + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_3', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario to test Plan-Restricted Response Handling from Destination', + successCriteria: 'Should return 403 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...sendMessageRequestParameters, + endpoint: 'https://api.intercom.io/messages', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request124","errors":[{"code":"api_plan_restricted","message":"Active subscription needed."}]}', + metadata: generateMetadata(1), + statusCode: 403, + }, + ], + status: 403, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_4', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario to test Rate Limit Exceeded Handling from Destination', + successCriteria: 'Should return 429 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...updateUserRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request125","errors":[{"code":"rate_limit_exceeded","message":"The rate limit for the App has been exceeded"}]}', + metadata: generateMetadata(1), + statusCode: 429, + }, + ], + status: 429, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_5', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario to test Conflict User Handling from Destination', + successCriteria: 'Should return 409 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: conflictUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/contacts', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request126","errors":[{"code":"conflict","message":"A contact matching those details already exists with id=test1"}]}', + metadata: generateMetadata(1), + statusCode: 409, + }, + ], + status: 409, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_6', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario to test Unsupported Media Handling from Destination', + successCriteria: 'Should return 406 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: createUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/users', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"errors":[{"code":"media_type_not_acceptable","message":"The Accept header should send a media type of application/json"}],"type":"error.list"}', + metadata: generateMetadata(1), + statusCode: 406, + }, + ], + status: 406, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/intercom/dataDelivery/data.ts b/test/integrations/destinations/intercom/dataDelivery/data.ts index db7aafc963..e5d2f4696b 100644 --- a/test/integrations/destinations/intercom/dataDelivery/data.ts +++ b/test/integrations/destinations/intercom/dataDelivery/data.ts @@ -1,91 +1,9 @@ +import { otherScenariosV0, otherScenariosV1 } from './other'; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + export const data = [ - { - name: 'intercom', - description: 'Test 0', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users/test1', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - name: 'Test Name', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - update_last_request_at: true, - user_id: 'test_user_id_1', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - 'address.city': 'Kolkata', - 'address.state': 'West Bengal', - 'originalArray[0].nested_field': 'nested value', - 'originalArray[0].tags[0]': 'tag_1', - 'originalArray[0].tags[1]': 'tag_2', - 'originalArray[0].tags[2]': 'tag_3', - 'originalArray[1].nested_field': 'nested value', - 'originalArray[1].tags[0]': 'tag_1', - 'originalArray[2].nested_field': 'nested value', - }, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 500, - body: { - output: { - status: 500, - message: - '[Intercom Response Handler] Request failed for destination intercom with status: 408', - destinationResponse: { - response: { - type: 'error.list', - request_id: '000on04msi4jpk7d3u60', - errors: [ - { - code: 'Request Timeout', - message: 'The server would not wait any longer for the client', - }, - ], - }, - status: 408, - }, - statTags: { - destType: 'INTERCOM', - errorCategory: 'network', - destinationId: 'Non-determininable', - workspaceId: 'Non-determininable', - errorType: 'retryable', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - }, - }, - }, - }, - }, - }, + ...otherScenariosV0, + ...otherScenariosV1, + ...testScenariosForV0API, + ...testScenariosForV1API, ]; diff --git a/test/integrations/destinations/intercom/dataDelivery/other.ts b/test/integrations/destinations/intercom/dataDelivery/other.ts new file mode 100644 index 0000000000..46cdcac468 --- /dev/null +++ b/test/integrations/destinations/intercom/dataDelivery/other.ts @@ -0,0 +1,384 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; + +const commonHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const createUserPayload = { + email: 'test_1@test.com', + phone: '9876543210', + name: 'Test Name', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const commonRequestParameters = { + JSON: createUserPayload, + headers: commonHeaders, +}; + +const expectedStatTags = { + destType: 'INTERCOM', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +const metadataArray = [generateMetadata(1)]; + +export const otherScenariosV0 = [ + { + id: 'intercom_v0_other_scenario_1', + name: 'intercom', + description: '[Proxy v0 API] :: Request Timeout Error Handling from Destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + message: + '[Intercom Response Handler] Request failed for destination intercom with status: 408', + destinationResponse: { + response: { + type: 'error.list', + request_id: '000on04msi4jpk7d3u60', + errors: [ + { + code: 'Request Timeout', + message: 'The server would not wait any longer for the client', + }, + ], + }, + status: 408, + }, + statTags: expectedStatTags, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_2', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 503 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test2', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 503, + body: { + output: { + status: 503, + message: 'Request Processed Successfully', + destinationResponse: { + type: 'error.list', + request_id: 'request127', + errors: [ + { + code: 'service_unavailable', + message: 'Sorry, the API service is temporarily unavailable', + }, + ], + }, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_3', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test3', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'client_error', + message: 'Unknown server error', + }, + ], + request_id: 'request128', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 500, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_4', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test4', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 504, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'server_timeout', + message: 'Server timed out when making request', + }, + ], + request_id: 'request129', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 504, + }, + }, + }, + }, + }, +]; + +export const otherScenariosV1: ProxyV1TestData[] = [ + { + id: 'intercom_v1_other_scenario_1', + name: 'intercom', + description: '[Proxy v1 API] :: Request Timeout Error Handling from Destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Intercom Response Handler] Request failed for destination intercom with status: 408', + response: [ + { + error: + '{"type":"error.list","request_id":"000on04msi4jpk7d3u60","errors":[{"code":"Request Timeout","message":"The server would not wait any longer for the client"}]}', + metadata: generateMetadata(1), + statusCode: 500, + }, + ], + statTags: expectedStatTags, + status: 500, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_2', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 503 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test2', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request127","errors":[{"code":"service_unavailable","message":"Sorry, the API service is temporarily unavailable"}]}', + metadata: generateMetadata(1), + statusCode: 503, + }, + ], + status: 503, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_3', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test3', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request128","errors":[{"code":"client_error","message":"Unknown server error"}]}', + metadata: generateMetadata(1), + statusCode: 500, + }, + ], + status: 500, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_4', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test4', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request129","errors":[{"code":"server_timeout","message":"Server timed out when making request"}]}', + metadata: generateMetadata(1), + statusCode: 504, + }, + ], + status: 504, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/intercom/network.ts b/test/integrations/destinations/intercom/network.ts index 74c861259f..5fa8ac6e96 100644 --- a/test/integrations/destinations/intercom/network.ts +++ b/test/integrations/destinations/intercom/network.ts @@ -1,3 +1,48 @@ +const commonHeaders = { + Accept: 'application/json', + Authorization: 'Bearer testApiKey', + 'Content-Type': 'application/json', +}; + +const v0VersionHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', + 'User-Agent': 'RudderLabs', +}; + +const v1VersionHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '2.10', + 'User-Agent': 'RudderLabs', +}; + +const userPayload = { + email: 'test_1@test.com', + phone: '9876543210', + name: 'Test Name', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const companyPayload = { + company_id: 'rudderlabs', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', +}; + const deleteNwData = [ { httpReq: { @@ -6,11 +51,7 @@ const deleteNwData = [ data: { intercom_user_id: '1', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { data: { @@ -33,11 +74,7 @@ const deleteNwData = [ data: { intercom_user_id: '12', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -54,11 +91,7 @@ const deleteNwData = [ data: { intercom_user_id: '7', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -75,11 +108,7 @@ const deleteNwData = [ data: { intercom_user_id: '9', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -89,6 +118,8 @@ const deleteNwData = [ }, }, }, +]; +const deliveryCallsData = [ { httpReq: { method: 'post', @@ -99,11 +130,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -131,11 +158,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test+2@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -172,11 +195,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test+5@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -213,11 +232,7 @@ const deleteNwData = [ value: [{ field: 'phone', operator: '=', value: '+91 9299999999' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -254,11 +269,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test+4@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -310,19 +321,8 @@ const deleteNwData = [ httpReq: { method: 'post', url: 'https://api.eu.intercom.io/companies', - data: { - company_id: 'rudderlabs', - name: 'RudderStack', - website: 'www.rudderstack.com', - plan: 'enterprise', - size: 500, - industry: 'CDP', - }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + data: companyPayload, + headers: commonHeaders, }, httpRes: { status: 200, @@ -346,19 +346,10 @@ const deleteNwData = [ method: 'post', url: 'https://api.eu.intercom.io/companies', data: { - company_id: 'rudderlabs', - name: 'RudderStack', - website: 'www.rudderstack.com', - plan: 'enterprise', - size: 500, - industry: 'CDP', + ...companyPayload, custom_attributes: { isOpenSource: true }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 401, @@ -374,55 +365,236 @@ const deleteNwData = [ }, }, }, -]; -const deliveryCallsData = [ + { + httpReq: { + url: 'https://api.intercom.io/users/test1', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: '000on04msi4jpk7d3u60', + errors: [ + { + code: 'Request Timeout', + message: 'The server would not wait any longer for the client', + }, + ], + }, + status: 408, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test1', + data: userPayload, + params: {}, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer invalidApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request123', + errors: [ + { + code: 'unauthorized', + message: 'Access Token Invalid', + }, + ], + }, + status: 401, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/messages', + data: { + from: { + type: 'user', + id: 'id@1', + }, + body: 'heyy, how are you', + referer: 'https://twitter.com/bob', + }, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request124', + errors: [ + { + code: 'api_plan_restricted', + message: 'Active subscription needed.', + }, + ], + }, + status: 403, + }, + }, { httpReq: { url: 'https://api.intercom.io/users/test1', data: { email: 'test_1@test.com', - phone: '9876543210', - name: 'Test Name', + phone: '9876543211', + name: 'Sample Name', signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', update_last_request_at: true, user_id: 'test_user_id_1', custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', 'address.city': 'Kolkata', 'address.state': 'West Bengal', - 'originalArray[0].nested_field': 'nested value', - 'originalArray[0].tags[0]': 'tag_1', - 'originalArray[0].tags[1]': 'tag_2', - 'originalArray[0].tags[2]': 'tag_3', - 'originalArray[1].nested_field': 'nested value', - 'originalArray[1].tags[0]': 'tag_1', - 'originalArray[2].nested_field': 'nested value', }, }, params: {}, - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - 'User-Agent': 'RudderLabs', + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request125', + errors: [ + { + code: 'rate_limit_exceeded', + message: 'The rate limit for the App has been exceeded', + }, + ], }, + status: 429, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/contacts', + data: { + email: 'test_1@test.com', + name: 'Rudder Labs', + signed_up_at: 1601496060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_2', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, + }, + params: {}, + headers: v1VersionHeaders, method: 'POST', }, httpRes: { data: { type: 'error.list', - request_id: '000on04msi4jpk7d3u60', + request_id: 'request126', errors: [ { - code: 'Request Timeout', - message: 'The server would not wait any longer for the client', + code: 'conflict', + message: 'A contact matching those details already exists with id=test1', }, ], }, - status: 408, + status: 409, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users', + data: userPayload, + params: {}, + headers: v1VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + errors: [ + { + code: 'media_type_not_acceptable', + message: 'The Accept header should send a media type of application/json', + }, + ], + type: 'error.list', + }, + status: 406, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test2', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request127', + errors: [ + { + code: 'service_unavailable', + message: 'Sorry, the API service is temporarily unavailable', + }, + ], + }, + status: 503, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test3', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request128', + errors: [ + { + code: 'client_error', + message: 'Unknown server error', + }, + ], + }, + status: 500, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test4', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request129', + errors: [ + { + code: 'server_timeout', + message: 'Server timed out when making request', + }, + ], + }, + status: 504, }, }, ]; From 9dff6860248c2bd7980140c8e21c4ed3e434ee33 Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Wed, 13 Mar 2024 12:35:27 +0530 Subject: [PATCH 18/35] chore: gaoc proxy test refactor (#3072) * chore: gaoc proxy test refactor * chore: code review changes * chore: partial failure tests added * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes --- .../dataDelivery/business.ts | 669 +++++++++++++++ .../dataDelivery/data.ts | 773 +----------------- .../dataDelivery/oauth.ts | 263 ++++++ .../network.ts | 154 ++++ 4 files changed, 1093 insertions(+), 766 deletions(-) create mode 100644 test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts create mode 100644 test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts diff --git a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts new file mode 100644 index 0000000000..fbeaf7f250 --- /dev/null +++ b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts @@ -0,0 +1,669 @@ +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; + +const transactionAttribute = { + CUSTOM_KEY: 'CUSTOM_VALUE', + currency_code: 'INR', + order_id: 'order id', + store_attribute: { + store_code: 'store code', + }, + transaction_amount_micros: '100000000', + transaction_date_time: '2019-10-14 11:15:18+00:00', +}; + +const createJobPayload = { + job: { + storeSalesMetadata: { + custom_key: 'CUSTOM_KEY', + loyaltyFraction: 1, + transaction_upload_fraction: '1', + }, + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + }, +}; + +const products = [ + { + product_id: '507f1f77bcf86cd799439011', + quantity: '2', + price: '50', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + position: '1', + category: 'cars', + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg', + }, +]; + +const headers = { + header1: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', + }, + header2: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + }, +}; + +const params = { + param1: { + customerId: '1112223333', + event: 'Sign-up - click', + }, + param2: { + event: 'Sign-up - click', + customerId: '1234567891', + customVariables: [ + { + from: 'Value', + to: 'revenue', + }, + { + from: 'total', + to: 'cost', + }, + ], + properties: { + gbraid: 'gbraid', + wbraid: 'wbraid', + externalAttributionCredit: 10, + externalAttributionModel: 'externalAttributionModel', + conversionCustomVariable: 'conversionCustomVariable', + Value: 'value', + merchantId: '9876merchantId', + feedCountryCode: 'feedCountryCode', + feedLanguageCode: 'feedLanguageCode', + localTransactionCost: 20, + products, + userIdentifierSource: 'FIRST_PARTY', + conversionEnvironment: 'WEB', + gclid: 'gclid', + conversionDateTime: '2022-01-01 12:32:45-08:00', + conversionValue: '1', + currency: 'GBP', + orderId: 'PL-123QR', + }, + }, + param3: {}, + param4: {}, +}; + +params['param3'] = { ...params.param2, customVariables: [] }; + +params['param4'] = { ...params.param3, customerId: '1234567893', conversionEnvironment: 'APP' }; + +const validRequestPayload1 = { + addConversionPayload: { + enable_partial_failure: false, + enable_warnings: false, + operations: [ + { + create: { + transaction_attribute: transactionAttribute, + userIdentifiers: [ + { + hashedEmail: '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', + userIdentifierSource: 'UNSPECIFIED', + }, + ], + }, + }, + ], + validate_only: false, + }, + createJobPayload, + event: '1112223333', + executeJobPayload: { + validate_only: false, + }, + isStoreConversion: true, +}; + +const validRequestPayload2 = { + conversions: [ + { + gbraid: 'gbraid', + wbraid: 'wbraid', + externalAttributionData: { + externalAttributionCredit: 10, + externalAttributionModel: 'externalAttributionModel', + }, + cartData: { + merchantId: 9876, + feedCountryCode: 'feedCountryCode', + feedLanguageCode: 'feedLanguageCode', + localTransactionCost: 20, + items: [ + { + productId: '507f1f77bcf86cd799439011', + quantity: 2, + unitPrice: 50, + }, + ], + }, + userIdentifiers: [ + { + userIdentifierSource: 'FIRST_PARTY', + hashedPhoneNumber: '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', + }, + ], + conversionEnvironment: 'WEB', + gclid: 'gclid', + conversionDateTime: '2022-01-01 12:32:45-08:00', + conversionValue: 1, + currencyCode: 'GBP', + orderId: 'PL-123QR', + }, + ], + partialFailure: true, +}; + +const invalidArgumentRequestPayload = { + addConversionPayload: { + enable_partial_failure: false, + enable_warnings: false, + operations: [ + { + create: { + transaction_attribute: transactionAttribute, + userIdentifiers: [ + { + hashedEmail: '6db61e6dcbcf2390e4a46af26f26a133a3bee45021422fc7ae86e9136f14110', + userIdentifierSource: 'UNSPECIFIED', + }, + ], + }, + }, + ], + validate_only: false, + }, + createJobPayload, + event: '1112223333', + executeJobPayload: { + validate_only: false, + }, + isStoreConversion: true, +}; + +const notAllowedToAccessFeatureRequestPayload = { + ...validRequestPayload2, + conversions: [ + { + ...validRequestPayload2.conversions[0], + conversionEnvironment: 'APP', + }, + ], +}; + +const metadataArray = [generateMetadata(1)]; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const testScenariosForV0API = [ + { + id: 'gaoc_v0_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for invalid argument - where the destination responds with 400 with invalid argument error', + successCriteria: 'Should return 400 with error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header1, + params: params.param1, + JSON: invalidArgumentRequestPayload, + endpoint: + 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + status: 400, + message: + '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', + destinationResponse: { + error: { + code: 400, + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v14.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + offlineUserDataJobError: 'INVALID_SHA256_FORMAT', + }, + message: 'The SHA256 encoded value is malformed.', + location: { + fieldPathElements: [ + { + fieldName: 'operations', + index: 0, + }, + { + fieldName: 'create', + }, + { + fieldName: 'user_identifiers', + index: 0, + }, + { + fieldName: 'hashed_email', + }, + ], + }, + }, + ], + requestId: '68697987', + }, + ], + message: 'Request contains an invalid argument.', + status: 'INVALID_ARGUMENT', + }, + }, + statTags: expectedStatTags, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for a valid operations request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header1, + params: params.param1, + JSON: validRequestPayload1, + endpoint: 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + destinationResponse: { + response: { + name: 'customers/111-222-3333/operations/abcd=', + }, + status: 200, + }, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_scenario_3', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header2, + params: params.param2, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + destinationResponse: { + response: [ + { + adjustmentType: 'ENHANCEMENT', + conversionAction: 'customers/1234567891/conversionActions/874224905', + adjustmentDateTime: '2021-01-01 12:32:45-08:00', + gclidDateTimePair: { + gclid: '1234', + conversionDateTime: '2021-01-01 12:32:45-08:00', + }, + orderId: '12345', + }, + ], + status: 200, + }, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_scenario_4', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for a valid conversion action request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header2, + params: params.param3, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + destinationResponse: { + response: [ + { + adjustmentDateTime: '2021-01-01 12:32:45-08:00', + adjustmentType: 'ENHANCEMENT', + conversionAction: 'customers/1234567891/conversionActions/874224905', + gclidDateTimePair: { + conversionDateTime: '2021-01-01 12:32:45-08:00', + gclid: '1234', + }, + orderId: '12345', + }, + ], + status: 200, + }, + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + status: 200, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API = [ + { + id: 'gaoc_v1_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for invalid argument - where the destination responds with 400 with invalid argument error', + successCriteria: 'Should return 400 with error with destination response', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header1, + params: params.param1, + JSON: invalidArgumentRequestPayload, + endpoint: + 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', + response: [ + { + error: + '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for a valid operations request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header1, + params: params.param1, + JSON: validRequestPayload1, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + response: [ + { + error: '{"name":"customers/111-222-3333/operations/abcd="}', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_3', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header2, + params: params.param2, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + response: [ + { + error: + '[{"adjustmentType":"ENHANCEMENT","conversionAction":"customers/1234567891/conversionActions/874224905","adjustmentDateTime":"2021-01-01 12:32:45-08:00","gclidDateTimePair":{"gclid":"1234","conversionDateTime":"2021-01-01 12:32:45-08:00"},"orderId":"12345"}]', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_4', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for a valid conversion action request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header2, + params: params.param3, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + response: [ + { + error: + '[{"adjustmentType":"ENHANCEMENT","conversionAction":"customers/1234567891/conversionActions/874224905","adjustmentDateTime":"2021-01-01 12:32:45-08:00","gclidDateTimePair":{"gclid":"1234","conversionDateTime":"2021-01-01 12:32:45-08:00"},"orderId":"12345"}]', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_5', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for customer is not allowed Test for a valid request with a successful 200 response from the destinationto access feature partial failure error', + successCriteria: 'Should return 400 with error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header2, + params: params.param4, + JSON: notAllowedToAccessFeatureRequestPayload, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1234567893:uploadClickConversions', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions]:: partialFailureError - Customer is not allowlisted for accessing this feature., at conversions[0].conversion_environment', + response: [ + { + error: + '[Google Ads Offline Conversions]:: partialFailureError - Customer is not allowlisted for accessing this feature., at conversions[0].conversion_environment', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts index ae75273399..709ab6d2a8 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts @@ -1,768 +1,9 @@ +import { v0oauthScenarios, v1oauthScenarios } from './oauth'; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + export const data = [ - { - name: 'google_adwords_offline_conversions', - description: 'Test 0', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': 'logincustomerid', - }, - params: { - customerId: '1112223333', - event: 'Sign-up - click', - }, - body: { - JSON: { - addConversionPayload: { - enable_partial_failure: false, - enable_warnings: false, - operations: [ - { - create: { - transaction_attribute: { - CUSTOM_KEY: 'CUSTOM_VALUE', - currency_code: 'INR', - order_id: 'order id', - store_attribute: { - store_code: 'store code', - }, - transaction_amount_micros: '100000000', - transaction_date_time: '2019-10-14 11:15:18+00:00', - }, - userIdentifiers: [ - { - hashedEmail: - '6db61e6dcbcf2390e4a46af26f26a133a3bee45021422fc7ae86e9136f14110', - userIdentifierSource: 'UNSPECIFIED', - }, - ], - }, - }, - ], - validate_only: false, - }, - createJobPayload: { - job: { - storeSalesMetadata: { - custom_key: 'CUSTOM_KEY', - loyaltyFraction: 1, - transaction_upload_fraction: '1', - }, - type: 'STORE_SALES_UPLOAD_FIRST_PARTY', - }, - }, - event: '1112223333', - executeJobPayload: { - validate_only: false, - }, - isStoreConversion: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - status: 400, - message: - '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', - destinationResponse: { - error: { - code: 400, - details: [ - { - '@type': 'type.googleapis.com/google.ads.googleads.v14.errors.GoogleAdsFailure', - errors: [ - { - errorCode: { - offlineUserDataJobError: 'INVALID_SHA256_FORMAT', - }, - message: 'The SHA256 encoded value is malformed.', - location: { - fieldPathElements: [ - { - fieldName: 'operations', - index: 0, - }, - { - fieldName: 'create', - }, - { - fieldName: 'user_identifiers', - index: 0, - }, - { - fieldName: 'hashed_email', - }, - ], - }, - }, - ], - requestId: '68697987', - }, - ], - message: 'Request contains an invalid argument.', - status: 'INVALID_ARGUMENT', - }, - }, - statTags: { - destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 1', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': 'logincustomerid', - }, - params: { - customerId: '1112223333', - event: 'Sign-up - click', - }, - body: { - JSON: { - addConversionPayload: { - enable_partial_failure: false, - enable_warnings: false, - operations: [ - { - create: { - transaction_attribute: { - CUSTOM_KEY: 'CUSTOM_VALUE', - currency_code: 'INR', - order_id: 'order id', - store_attribute: { - store_code: 'store code', - }, - transaction_amount_micros: '100000000', - transaction_date_time: '2019-10-14 11:15:18+00:00', - }, - userIdentifiers: [ - { - hashedEmail: - '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', - userIdentifierSource: 'UNSPECIFIED', - }, - ], - }, - }, - ], - validate_only: false, - }, - createJobPayload: { - job: { - storeSalesMetadata: { - custom_key: 'CUSTOM_KEY', - loyaltyFraction: 1, - transaction_upload_fraction: '1', - }, - type: 'STORE_SALES_UPLOAD_FIRST_PARTY', - }, - }, - event: '1112223333', - executeJobPayload: { - validate_only: false, - }, - isStoreConversion: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: { - status: 200, - message: - '[Google Ads Offline Conversions Response Handler] - Request processed successfully', - destinationResponse: { - response: { - name: 'customers/111-222-3333/operations/abcd=', - }, - status: 200, - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 2', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': 'logincustomerid', - }, - params: { - customerId: '1112223333', - event: 'Sign-up - click', - }, - body: { - JSON: { - addConversionPayload: { - enable_partial_failure: false, - enable_warnings: false, - operations: [ - { - create: { - transaction_attribute: { - CUSTOM_KEY: 'CUSTOM_VALUE', - currency_code: 'INR', - order_id: 'order id', - store_attribute: { - store_code: 'store code', - }, - transaction_amount_micros: '100000000', - transaction_date_time: '2019-10-14 11:15:18+00:00', - }, - userIdentifiers: [ - { - hashedEmail: - '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', - userIdentifierSource: 'UNSPECIFIED', - }, - ], - }, - }, - ], - validate_only: false, - }, - createJobPayload: { - job: { - storeSalesMetadata: { - custom_key: 'CUSTOM_KEY', - loyaltyFraction: 1, - transaction_upload_fraction: '1', - }, - type: 'STORE_SALES_UPLOAD_FIRST_PARTY', - }, - }, - event: '1112223333', - executeJobPayload: { - validate_only: false, - }, - isStoreConversion: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 401, - body: { - output: { - status: 401, - message: - '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', - authErrorCategory: 'REFRESH_TOKEN', - destinationResponse: { - error: { - code: 401, - message: - 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', - status: 'UNAUTHENTICATED', - }, - }, - statTags: { - destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 3', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567890:uploadClickConversions', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - }, - params: { - event: 'Sign-up - click', - customerId: '1234567890', - customVariables: [ - { - from: 'value', - to: 'revenue', - }, - { - from: 'total', - to: 'cost', - }, - ], - properties: { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - conversionCustomVariable: 'conversionCustomVariable', - value: 'value', - merchantId: '9876merchantId', - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - quantity: '2', - price: '50', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - position: '1', - category: 'cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - }, - ], - userIdentifierSource: 'FIRST_PARTY', - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: '1', - currency: 'GBP', - orderId: 'PL-123QR', - }, - }, - body: { - JSON: { - conversions: [ - { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionData: { - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - }, - cartData: { - merchantId: 9876, - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - items: [ - { - productId: '507f1f77bcf86cd799439011', - quantity: 2, - unitPrice: 50, - }, - ], - }, - userIdentifiers: [ - { - userIdentifierSource: 'FIRST_PARTY', - hashedEmail: - '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', - }, - ], - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: 1, - currencyCode: 'GBP', - orderId: 'PL-123QR', - }, - ], - partialFailure: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 401, - body: { - output: { - status: 401, - message: - '[Google Ads Offline Conversions]:: [{"error":{"code":401,"message":"Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.","status":"UNAUTHENTICATED"}}] during google_ads_offline_conversions response transformation', - authErrorCategory: 'REFRESH_TOKEN', - destinationResponse: [ - { - error: { - code: 401, - message: - 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', - status: 'UNAUTHENTICATED', - }, - }, - ], - statTags: { - destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 4', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - }, - params: { - event: 'Sign-up - click', - customerId: '1234567891', - customVariables: [ - { - from: 'Value', - to: 'revenue', - }, - { - from: 'total', - to: 'cost', - }, - ], - properties: { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - conversionCustomVariable: 'conversionCustomVariable', - Value: 'value', - merchantId: '9876merchantId', - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - quantity: '2', - price: '50', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - position: '1', - category: 'cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - }, - ], - userIdentifierSource: 'FIRST_PARTY', - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: '1', - currency: 'GBP', - orderId: 'PL-123QR', - }, - }, - body: { - JSON: { - conversions: [ - { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionData: { - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - }, - cartData: { - merchantId: 9876, - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - items: [ - { - productId: '507f1f77bcf86cd799439011', - quantity: 2, - unitPrice: 50, - }, - ], - }, - userIdentifiers: [ - { - userIdentifierSource: 'FIRST_PARTY', - hashedPhoneNumber: - '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', - }, - ], - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: 1, - currencyCode: 'GBP', - orderId: 'PL-123QR', - }, - ], - partialFailure: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: { - status: 200, - message: - '[Google Ads Offline Conversions Response Handler] - Request processed successfully', - destinationResponse: { - response: [ - { - adjustmentType: 'ENHANCEMENT', - conversionAction: 'customers/1234567891/conversionActions/874224905', - adjustmentDateTime: '2021-01-01 12:32:45-08:00', - gclidDateTimePair: { - gclid: '1234', - conversionDateTime: '2021-01-01 12:32:45-08:00', - }, - orderId: '12345', - }, - ], - status: 200, - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 5', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - }, - params: { - event: 'Sign-up - click', - customerId: '1234567891', - customVariables: [], - properties: { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - conversionCustomVariable: 'conversionCustomVariable', - value: 'value', - merchantId: '9876merchantId', - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - quantity: '2', - price: '50', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - position: '1', - category: 'cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - }, - ], - userIdentifierSource: 'FIRST_PARTY', - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: '1', - currency: 'GBP', - orderId: 'PL-123QR', - }, - }, - body: { - JSON: { - conversions: [ - { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionData: { - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - }, - cartData: { - merchantId: 9876, - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - items: [ - { - productId: '507f1f77bcf86cd799439011', - quantity: 2, - unitPrice: 50, - }, - ], - }, - userIdentifiers: [ - { - userIdentifierSource: 'FIRST_PARTY', - hashedPhoneNumber: - '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', - }, - ], - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: 1, - currencyCode: 'GBP', - orderId: 'PL-123QR', - }, - ], - partialFailure: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: { - destinationResponse: { - response: [ - { - adjustmentDateTime: '2021-01-01 12:32:45-08:00', - adjustmentType: 'ENHANCEMENT', - conversionAction: 'customers/1234567891/conversionActions/874224905', - gclidDateTimePair: { - conversionDateTime: '2021-01-01 12:32:45-08:00', - gclid: '1234', - }, - orderId: '12345', - }, - ], - status: 200, - }, - message: - '[Google Ads Offline Conversions Response Handler] - Request processed successfully', - status: 200, - }, - }, - }, - }, - }, + ...v0oauthScenarios, + ...v1oauthScenarios, + ...testScenariosForV0API, + ...testScenariosForV1API, ]; diff --git a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts new file mode 100644 index 0000000000..15a150d0e5 --- /dev/null +++ b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts @@ -0,0 +1,263 @@ +import { + generateMetadata, + generateProxyV1Payload, + generateProxyV0Payload, +} from '../../../testUtils'; + +const commonHeaders = { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', +}; + +const commonParams = { + customerId: '1112223333', + event: 'Sign-up - click', +}; + +const commonRequestPayload = { + addConversionPayload: { + enable_partial_failure: false, + enable_warnings: false, + operations: [ + { + create: { + transaction_attribute: { + CUSTOM_KEY: 'CUSTOM_VALUE', + currency_code: 'INR', + order_id: 'order id', + store_attribute: { + store_code: 'store code', + }, + transaction_amount_micros: '100000000', + transaction_date_time: '2019-10-14 11:15:18+00:00', + }, + userIdentifiers: [ + { + hashedEmail: '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', + userIdentifierSource: 'UNSPECIFIED', + }, + ], + }, + }, + ], + validate_only: false, + }, + createJobPayload: { + job: { + storeSalesMetadata: { + custom_key: 'CUSTOM_KEY', + loyaltyFraction: 1, + transaction_upload_fraction: '1', + }, + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + }, + }, + event: '1112223333', + executeJobPayload: { + validate_only: false, + }, + isStoreConversion: true, +}; + +const commonRequestParameters = { + headers: commonHeaders, + params: commonParams, + JSON: commonRequestPayload, +}; + +const metadataArray = [generateMetadata(1)]; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const v0oauthScenarios = [ + { + id: 'gaoc_v0_oauth_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: 'The proxy should return 401 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + status: 401, + message: + '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', + authErrorCategory: 'REFRESH_TOKEN', + destinationResponse: { + error: { + code: 401, + message: + 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', + status: 'UNAUTHENTICATED', + }, + }, + statTags: expectedStatTags, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_oauth_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Oauth where ACCESS_TOKEN_SCOPE_INSUFFICIENT error as mock response from destination', + successCriteria: + 'Since the error from the destination is 403 - the proxy should return 403 with authErrorCategory as AUTH_STATUS_INACTIVE', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://googleads.googleapis.com/v14/customers/1234/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + authErrorCategory: 'AUTH_STATUS_INACTIVE', + destinationResponse: { + error: { + code: 403, + message: 'Request had insufficient authentication scopes', + status: 'PERMISSION_DENIED', + }, + }, + message: + '[Google Ads Offline Conversions]:: Request had insufficient authentication scopes during google_ads_offline_store_conversions Job Creation', + statTags: expectedStatTags, + status: 403, + }, + }, + }, + }, + }, +]; + +export const v1oauthScenarios = [ + { + id: 'gaoc_v1_oauth_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: 'The proxy should return 401 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + authErrorCategory: 'REFRESH_TOKEN', + message: + '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', + response: [ + { + error: + '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', + metadata: generateMetadata(1), + statusCode: 401, + }, + ], + statTags: expectedStatTags, + status: 401, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_oauth_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Oauth where ACCESS_TOKEN_SCOPE_INSUFFICIENT error as mock response from destination', + successCriteria: + 'Since the error from the destination is 403 - the proxy should return 403 with authErrorCategory as AUTH_STATUS_INACTIVE', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://googleads.googleapis.com/v14/customers/1234/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + authErrorCategory: 'AUTH_STATUS_INACTIVE', + message: + '[Google Ads Offline Conversions]:: Request had insufficient authentication scopes during google_ads_offline_store_conversions Job Creation', + response: [ + { + error: + '[Google Ads Offline Conversions]:: Request had insufficient authentication scopes during google_ads_offline_store_conversions Job Creation', + metadata: generateMetadata(1), + statusCode: 403, + }, + ], + statTags: expectedStatTags, + status: 403, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_offline_conversions/network.ts b/test/integrations/destinations/google_adwords_offline_conversions/network.ts index 375879727b..7dc7f97933 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/network.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/network.ts @@ -235,6 +235,8 @@ export const networkCallsData = [ }, }, { + description: + 'Mock response from destination depicting a request with invalid authentication credentials', httpReq: { url: 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs:create', data: { @@ -268,6 +270,41 @@ export const networkCallsData = [ }, }, }, + { + description: + 'Mock response from destination depicting a request with invalid authentication scopes', + httpReq: { + url: 'https://googleads.googleapis.com/v14/customers/1234/offlineUserDataJobs:create', + data: { + job: { + storeSalesMetadata: { + custom_key: 'CUSTOM_KEY', + loyaltyFraction: 1, + transaction_upload_fraction: '1', + }, + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + }, + }, + params: { destination: 'google_adwords_offline_conversion' }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', + }, + method: 'POST', + }, + httpRes: { + status: 403, + data: { + error: { + code: 403, + message: 'Request had insufficient authentication scopes', + status: 'PERMISSION_DENIED', + }, + }, + }, + }, { httpReq: { url: 'https://googleads.googleapis.com/v14/customers/1234567890/googleAds:searchStream', @@ -491,4 +528,121 @@ export const networkCallsData = [ status: 200, }, }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v14/customers/1234567893/googleAds:searchStream', + data: { + query: + "SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Sign-up - click'", + }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + }, + method: 'POST', + params: { destination: 'google_adwords_offline_conversion' }, + }, + httpRes: { + data: [ + { + results: [ + { + conversionAction: { + resourceName: 'customers/1234567893/conversionActions/848898417', + id: '848898417', + }, + }, + ], + fieldMask: 'conversionAction.id', + requestId: 'dummyRequestId', + }, + ], + status: 200, + }, + }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v14/customers/1234567893:uploadClickConversions', + data: { + conversions: [ + { + gbraid: 'gbraid', + wbraid: 'wbraid', + externalAttributionData: { + externalAttributionCredit: 10, + externalAttributionModel: 'externalAttributionModel', + }, + cartData: { + merchantId: 9876, + feedCountryCode: 'feedCountryCode', + feedLanguageCode: 'feedLanguageCode', + localTransactionCost: 20, + items: [{ productId: '507f1f77bcf86cd799439011', quantity: 2, unitPrice: 50 }], + }, + userIdentifiers: [ + { + userIdentifierSource: 'FIRST_PARTY', + hashedPhoneNumber: + '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', + }, + ], + conversionEnvironment: 'APP', + gclid: 'gclid', + conversionDateTime: '2022-01-01 12:32:45-08:00', + conversionValue: 1, + currencyCode: 'GBP', + orderId: 'PL-123QR', + conversionAction: 'customers/1234567893/conversionActions/848898417', + }, + ], + partialFailure: true, + }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + }, + method: 'POST', + params: { destination: 'google_adwords_offline_conversion' }, + }, + httpRes: { + status: 200, + data: { + partialFailureError: { + code: 3, + message: + 'Customer is not allowlisted for accessing this feature., at conversions[0].conversion_environment', + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v14.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + notAllowlistedError: 'CUSTOMER_NOT_ALLOWLISTED_FOR_THIS_FEATURE', + }, + message: 'Customer is not allowlisted for accessing this feature.', + trigger: { + int64Value: '2', + }, + location: { + fieldPathElements: [ + { + fieldName: 'conversions', + index: 0, + }, + { + fieldName: 'conversion_environment', + }, + ], + }, + }, + ], + requestId: 'dummyRequestId', + }, + ], + }, + }, + }, + }, ]; From bdc0f41c57776e95809414b7598107004aa2aa6b Mon Sep 17 00:00:00 2001 From: Abhimanyu Babbar Date: Wed, 13 Mar 2024 14:58:30 +0530 Subject: [PATCH 19/35] chore: base support for raising stat per tracking plan event (#2847) chore: added base support for raising event per tracking plan event --- src/controllers/trackingPlan.ts | 2 +- src/services/trackingPlan.ts | 113 ++++++++++++++++---------------- src/util/prometheus.js | 30 +++++++-- src/v0/util/index.js | 6 ++ 4 files changed, 88 insertions(+), 63 deletions(-) diff --git a/src/controllers/trackingPlan.ts b/src/controllers/trackingPlan.ts index 74e47e0ec9..e4802cfc4d 100644 --- a/src/controllers/trackingPlan.ts +++ b/src/controllers/trackingPlan.ts @@ -7,7 +7,7 @@ export class TrackingPlanController { const events = ctx.request.body; const requestSize = Number(ctx.request.get('content-length')); const reqParams = ctx.request.query; - const response = await TrackingPlanservice.validateTrackingPlan(events, requestSize, reqParams); + const response = await TrackingPlanservice.validate(events, requestSize, reqParams); ctx.body = response.body; ControllerUtility.postProcess(ctx, response.status); return ctx; diff --git a/src/services/trackingPlan.ts b/src/services/trackingPlan.ts index 2e68df55e9..93b6ee11ff 100644 --- a/src/services/trackingPlan.ts +++ b/src/services/trackingPlan.ts @@ -1,88 +1,87 @@ import logger from '../logger'; import { RetryRequestError, RespStatusError, constructValidationErrors } from '../util/utils'; -import { getMetadata } from '../v0/util'; +import { getMetadata, getTrackingPlanMetadata } from '../v0/util'; import eventValidator from '../util/eventValidation'; import stats from '../util/stats'; +import { HTTP_STATUS_CODES } from '../v0/util/constant'; export class TrackingPlanservice { - public static async validateTrackingPlan(events, requestSize, reqParams) { - const requestStartTime = new Date(); + public static async validate(events, requestSize, reqParams) { + const startTime = Date.now(); const respList: any[] = []; - const metaTags = events[0].metadata ? getMetadata(events[0].metadata) : {}; + const metaTags = events.length && events[0].metadata ? getMetadata(events[0].metadata) : {}; + const tpTags: any = + events.length && events[0].metadata ? getTrackingPlanMetadata(events[0].metadata) : {}; let ctxStatusCode = 200; for (let i = 0; i < events.length; i++) { + let eventValidationResponse: any; + let exceptionOccured = false; + const eventStartTime = Date.now(); const event = events[i]; - const eventStartTime = new Date(); + try { - const parsedEvent = event; - parsedEvent.request = { query: reqParams }; - const hv = await eventValidator.handleValidation(parsedEvent); - if (hv['dropEvent']) { - respList.push({ - output: event.message, - metadata: event.metadata, - statusCode: 400, - validationErrors: hv['validationErrors'], - error: JSON.stringify(constructValidationErrors(hv['validationErrors'])), - }); - stats.counter('tp_violation_type', 1, { - violationType: hv['violationType'], - ...metaTags, - }); - } else { - respList.push({ - output: event.message, - metadata: event.metadata, - statusCode: 200, - validationErrors: hv['validationErrors'], - error: JSON.stringify(constructValidationErrors(hv['validationErrors'])), - }); - stats.counter('tp_propagated_events', 1, { - ...metaTags, - }); - } - } catch (error) { - const errMessage = `Error occurred while validating : ${error}`; - logger.error(errMessage); - let status = 200; + event.request = { query: reqParams }; + const validatedEvent = await eventValidator.handleValidation(event); + eventValidationResponse = { + output: event.message, + metadata: event.metadata, + statusCode: validatedEvent['dropEvent'] + ? HTTP_STATUS_CODES.BAD_REQUEST + : HTTP_STATUS_CODES.OK, + validationErrors: validatedEvent['validationErrors'], + error: JSON.stringify(constructValidationErrors(validatedEvent['validationErrors'])), + }; + } catch (error: any) { + logger.debug( + `Error occurred while validating event`, + 'event', + `${event.message?.event}::${event.message?.type}`, + 'trackingPlan', + `${tpTags?.trackingPlanId}`, + 'error', + error.message, + ); + + exceptionOccured = true; + // no need to process further if + // we have error of retry request error if (error instanceof RetryRequestError) { ctxStatusCode = error.statusCode; + break; } - if (error instanceof RespStatusError) { - status = error.statusCode; - } - respList.push({ + + eventValidationResponse = { output: event.message, metadata: event.metadata, - statusCode: status, + statusCode: error instanceof RespStatusError ? error.statusCode : HTTP_STATUS_CODES.OK, validationErrors: [], - error: errMessage, - }); - stats.counter('tp_errors', 1, { - ...metaTags, - workspaceId: event.metadata?.workspaceId, - trackingPlanId: event.metadata?.trackingPlanId, - }); + error: `Error occurred while validating: ${error}`, + }; } finally { - stats.timing('tp_event_latency', eventStartTime, { + // finally on every event, we need to + // capture the information related to the validates event + stats.timing('tp_event_validation_latency', eventStartTime, { ...metaTags, + ...tpTags, + status: eventValidationResponse.statusCode, + exception: exceptionOccured, }); } - } - stats.counter('tp_events_count', events.length, { - ...metaTags, - }); + respList.push(eventValidationResponse); + } - stats.histogram('tp_request_size', requestSize, { + stats.histogram('tp_batch_size', requestSize, { ...metaTags, + ...tpTags, }); - stats.timing('tp_request_latency', requestStartTime, { + // capture overall function latency + // with metadata tags + stats.histogram('tp_batch_validation_latency', (Date.now() - startTime) / 1000, { ...metaTags, - workspaceId: events[0]?.metadata?.workspaceId, - trackingPlanId: events[0]?.metadata?.trackingPlanId, + ...tpTags, }); return { body: respList, status: ctxStatusCode }; diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 89e5424c0c..b502681987 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -590,14 +590,34 @@ class Prometheus { labelNames: ['method', 'route', 'code'], }, { - name: 'tp_request_size', - help: 'tp_request_size', + name: 'tp_batch_size', + help: 'Size of batch of events for tracking plan validation', type: 'histogram', - labelNames: ['sourceType', 'destinationType', 'k8_namespace'], + labelNames: [ + 'sourceType', + 'destinationType', + 'k8_namespace', + 'workspaceId', + 'trackingPlanId', + ], + }, + { + name: 'tp_event_validation_latency', + help: 'Latency of validating tracking plan at event level', + type: 'histogram', + labelNames: [ + 'sourceType', + 'destinationType', + 'k8_namespace', + 'workspaceId', + 'trackingPlanId', + 'status', + 'exception', + ], }, { - name: 'tp_request_latency', - help: 'tp_request_latency', + name: 'tp_batch_validation_latency', + help: 'Latency of validating tracking plan at batch level', type: 'histogram', labelNames: [ 'sourceType', diff --git a/src/v0/util/index.js b/src/v0/util/index.js index c1debce088..32872cc5d9 100644 --- a/src/v0/util/index.js +++ b/src/v0/util/index.js @@ -1419,6 +1419,11 @@ function getStringValueOfJSON(json) { return output; } +const getTrackingPlanMetadata = (metadata) => ({ + trackingPlanId: metadata.trackingPlanId, + workspaceId: metadata.workspaceId, +}); + const getMetadata = (metadata) => ({ sourceType: metadata.sourceType, destinationType: metadata.destinationType, @@ -2267,6 +2272,7 @@ module.exports = { getMappingConfig, getMetadata, getTransformationMetadata, + getTrackingPlanMetadata, getParsedIP, getStringValueOfJSON, getSuccessRespEvents, From 3bda582c0d11631980b4038b4c73a81e84b0404b Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:37:40 +0530 Subject: [PATCH 20/35] chore: ga4 proxy test refactor (#3137) * chore: ga4 proxy test refactor * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes --- .../destinations/ga4/dataDelivery/business.ts | 350 ++++++++++++++++++ .../destinations/ga4/dataDelivery/data.ts | 180 +-------- test/integrations/destinations/ga4/network.ts | 166 +++++---- 3 files changed, 446 insertions(+), 250 deletions(-) create mode 100644 test/integrations/destinations/ga4/dataDelivery/business.ts diff --git a/test/integrations/destinations/ga4/dataDelivery/business.ts b/test/integrations/destinations/ga4/dataDelivery/business.ts new file mode 100644 index 0000000000..80271abbdb --- /dev/null +++ b/test/integrations/destinations/ga4/dataDelivery/business.ts @@ -0,0 +1,350 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; +import { JSON_MIME_TYPE } from '../../../../../src/v0/util/constant'; + +const headers = { + HOST: 'www.google-analytics.com', + 'Content-Type': JSON_MIME_TYPE, +}; + +const params = { + api_secret: 'dymmyApiSecret', +}; + +const validRequest = { + events: [ + { + name: 'sign_up', + params: { + method: 'google', + engagement_time_msec: 1, + }, + }, + ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, +}; + +const invalidEventNameRequest = { + events: [ + { + name: 'campaign@details', + params: { + term: 'summer+travel', + medium: 'cpc', + source: 'google', + content: 'logo link', + campaign: 'Summer_fun', + campaign_id: 'google_1234', + engagement_time_msec: 1, + }, + }, + ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, +}; + +const invalidParameterValueRequest = { + events: [ + { + name: 'add_to_cart', + params: { + currency: 'USD', + value: 7.77, + engagement_time_msec: 1, + items: [ + { + item_id: '507f1f77bcf86cd799439011', + item_name: 'Monopoly: 3rd Edition', + coupon: 'SUMMER_FUN', + item_category: 'Apparel', + item_brand: 'Google', + item_variant: 'green', + price: '$19', + quantity: 2, + affiliation: 'Google Merchandise Store', + currency: 'USD', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + }, + ], + }, + }, + ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, +}; + +const invalidParamMessage = + 'Validation of item.price should prevent conversion from unsupported value [string_value: "$19"]'; +const invalidParameterErrorMessage = `Validation Server Response Handler:: Validation Error for ga4 of field path :undefined | INTERNAL_ERROR-${invalidParamMessage}`; +const invalidEventNameErrorMessage = + 'Validation Server Response Handler:: Validation Error for ga4 of field path :events | NAME_INVALID-Event at index: [0] has invalid name [campaign@details]. Only alphanumeric characters and underscores are allowed.'; + +const metadataArray = [generateMetadata(1)]; + +const expectedStatTags = { + destType: 'GA4', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const testScenariosForV0API = [ + { + id: 'ga4_v0_scenario_1', + name: 'ga4', + description: + '[Proxy v0 API] :: Test for a valid request - where the destination responds with 200 without any error', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers, + params, + JSON: validRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + destinationResponse: { + response: { + validationMessages: [], + }, + status: 200, + }, + message: '[GA4 Response Handler] - Request Processed Successfully', + status: 200, + }, + }, + }, + }, + }, + { + id: 'ga4_v0_scenario_2', + name: 'ga4', + description: + '[Proxy v0 API] :: Test for a invalid event name - where the destination responds with 200 with error for invalid event name', + successCriteria: 'Should return 400 with error and destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers, + params, + JSON: invalidEventNameRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + destinationResponse: + 'Event at index: [0] has invalid name [campaign@details]. Only alphanumeric characters and underscores are allowed.', + message: invalidEventNameErrorMessage, + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'ga4_v0_scenario_3', + name: 'ga4', + description: + '[Proxy v0 API] :: Test for a invalid parameter value - where the destination responds with 200 with error for invalid parameter value', + successCriteria: 'Should return 400 with error and destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers, + params, + JSON: invalidParameterValueRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + destinationResponse: invalidParamMessage, + message: invalidParameterErrorMessage, + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'ga4_v1_scenario_1', + name: 'ga4', + description: + '[Proxy v1 API] :: Test for a valid request - where the destination responds with 200 without any error', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers, + params, + JSON: validRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: '[GA4 Response Handler] - Request Processed Successfully', + response: [ + { + error: '{"validationMessages":[]}', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'ga4_v1_scenario_2', + name: 'ga4', + description: + '[Proxy v1 API] :: Test for a invalid event name - where the destination responds with 200 with error for invalid event name', + successCriteria: 'Should return 400 with error and destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers, + params, + JSON: invalidEventNameRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: invalidEventNameErrorMessage, + response: [ + { + error: invalidEventNameErrorMessage, + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'ga4_v1_scenario_3', + name: 'ga4', + description: + '[Proxy v1 API] :: Test for a invalid parameter value - where the destination responds with 200 with error for invalid parameter value', + successCriteria: 'Should return 200 with error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers, + params, + JSON: invalidParameterValueRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: invalidParameterErrorMessage, + response: [ + { + error: invalidParameterErrorMessage, + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/ga4/dataDelivery/data.ts b/test/integrations/destinations/ga4/dataDelivery/data.ts index 9ccf35e2a1..51827a38e2 100644 --- a/test/integrations/destinations/ga4/dataDelivery/data.ts +++ b/test/integrations/destinations/ga4/dataDelivery/data.ts @@ -1,177 +1,3 @@ -export const data = [ - { - name: 'ga4', - description: 'Successful data delivery', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'dummyMeasurementId', - }, - body: { - JSON: { - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_item_list', - params: { - item_list_id: 'related_products', - item_list_name: 'Related_products', - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: { - destinationResponse: { - response: { - validationMessages: [], - }, - status: 200, - }, - message: '[GA4 Response Handler] - Request Processed Successfully', - status: 200, - }, - }, - }, - }, - }, - { - name: 'ga4', - description: 'Data delivery failure', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/debug/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'dummyMeasurementId', - }, - body: { - JSON: { - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_item', - params: { - category: 'Electronics', - productID: 'ABC123', - productName: 'Example Product', - customer_name: 'Sample User', - link_imageURL: 'https://example.com/images/product.jpg', - customer_email: 'testrudder@gmail.com', - link_productURL: 'https://example.com/products/ABC123', - stockAvailability: true, - details_features_0: 'wireless charging', - details_features_1: 'water-resistant', - engagement_time_msec: 1, - transaction_currency: 'USD', - customer_loyaltyPoints: 500, - transaction_totalAmount: 150.99, - transaction_discountApplied: 20.5, - details_specifications_color: 'blue', - details_specifications_specifications_specifications_specifications_color: - 'blue', - details_specifications_specifications_specifications_specifications_weight: - '1.5kg', - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - }, - }, - output: { - response: { - status: 400, - body: { - output: { - destinationResponse: - 'The event param [string_value: "1.5kg"] has a duplicate name [details_specifications_specifications_specifications_specifications_weight].', - message: - 'Validation Server Response Handler:: Validation Error for ga4 of field path :events.params | NAME_DUPLICATED-The event param [string_value: "1.5kg"] has a duplicate name [details_specifications_specifications_specifications_specifications_weight].', - statTags: { - destType: 'GA4', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - status: 400, - }, - }, - }, - }, - }, -]; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + +export const data = [...testScenariosForV0API, ...testScenariosForV1API]; diff --git a/test/integrations/destinations/ga4/network.ts b/test/integrations/destinations/ga4/network.ts index e8c91ef451..b5c8dc8e8e 100644 --- a/test/integrations/destinations/ga4/network.ts +++ b/test/integrations/destinations/ga4/network.ts @@ -1,119 +1,139 @@ -export const networkCallsData = [ +const headers = { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', +}; + +const params = { + api_secret: 'dymmyApiSecret', +}; + +const dataDeliveryMocksData = [ { + description: 'Mock response from destination depicting a valid request', httpReq: { - url: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - 'User-Agent': 'RudderLabs', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'dummyMeasurementId', - }, + method: 'post', + url: 'https://www.google-analytics.com/debug/mp/collect', data: { - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, events: [ { - name: 'view_item_list', + name: 'sign_up', params: { - item_list_id: 'related_products', - item_list_name: 'Related_products', - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], + method: 'google', engagement_time_msec: 1, }, }, ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, }, - method: 'POST', + headers, + params, }, httpRes: { data: { validationMessages: [], }, status: 200, + statusText: 'OK', }, }, { + description: 'Mock response from destination depicting a invalid event name request', httpReq: { + method: 'post', url: 'https://www.google-analytics.com/debug/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - 'User-Agent': 'RudderLabs', + data: { + events: [ + { + name: 'campaign@details', + params: { + term: 'summer+travel', + medium: 'cpc', + source: 'google', + content: 'logo link', + campaign: 'Summer_fun', + campaign_id: 'google_1234', + engagement_time_msec: 1, + }, + }, + ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'dummyMeasurementId', + headers, + params, + }, + httpRes: { + data: { + validationMessages: [ + { + fieldPath: 'events', + description: + 'Event at index: [0] has invalid name [campaign@details]. Only alphanumeric characters and underscores are allowed.', + validationCode: 'NAME_INVALID', + }, + ], }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response from destination depicting a invalid parameter value request', + httpReq: { + method: 'post', + url: 'https://www.google-analytics.com/debug/mp/collect', data: { - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, events: [ { - name: 'view_item', + name: 'add_to_cart', params: { - category: 'Electronics', - productID: 'ABC123', - productName: 'Example Product', - customer_name: 'Sample User', - link_imageURL: 'https://example.com/images/product.jpg', - customer_email: 'testrudder@gmail.com', - link_productURL: 'https://example.com/products/ABC123', - stockAvailability: true, - details_features_0: 'wireless charging', - details_features_1: 'water-resistant', + currency: 'USD', + value: 7.77, engagement_time_msec: 1, - transaction_currency: 'USD', - customer_loyaltyPoints: 500, - transaction_totalAmount: 150.99, - transaction_discountApplied: 20.5, - details_specifications_color: 'blue', - details_specifications_specifications_specifications_specifications_color: 'blue', - details_specifications_specifications_specifications_specifications_weight: '1.5kg', + items: [ + { + item_id: '507f1f77bcf86cd799439011', + item_name: 'Monopoly: 3rd Edition', + coupon: 'SUMMER_FUN', + item_category: 'Apparel', + item_brand: 'Google', + item_variant: 'green', + price: '$19', + quantity: 2, + affiliation: 'Google Merchandise Store', + currency: 'USD', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + }, + ], }, }, ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, }, - method: 'POST', + headers, + params, }, httpRes: { data: { validationMessages: [ { - fieldPath: 'events.params', description: - 'The event param [string_value: "1.5kg"] has a duplicate name [details_specifications_specifications_specifications_specifications_weight].', - validationCode: 'NAME_DUPLICATED', + 'Validation of item.price should prevent conversion from unsupported value [string_value: "$19"]', + validationCode: 'INTERNAL_ERROR', }, ], }, status: 200, + statusText: 'OK', }, }, ]; + +export const networkCallsData = [...dataDeliveryMocksData]; From c0ad21463981ef66154c8157083924f76825762d Mon Sep 17 00:00:00 2001 From: ItsSudip Date: Wed, 13 Mar 2024 22:09:43 +0530 Subject: [PATCH 21/35] feat: add support of --- src/v0/destinations/am/transform.js | 3 ++ src/v0/destinations/am/util.test.js | 35 ++++++++++++++++++- src/v0/destinations/am/utils.js | 9 +++++ .../destinations/am/processor/data.ts | 3 ++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/v0/destinations/am/transform.js b/src/v0/destinations/am/transform.js index 2d78479ced..ce157e7674 100644 --- a/src/v0/destinations/am/transform.js +++ b/src/v0/destinations/am/transform.js @@ -525,6 +525,9 @@ const responseBuilderSimple = ( ...campaign, }; + // we are updating the payload with skip_user_properties_sync + AMUtils.updateWithSkipAttribute(message, rawPayload); + const respData = getResponseData(evType, destination, rawPayload, message, groupInfo); const { groups, rawPayload: updatedRawPayload } = respData; diff --git a/src/v0/destinations/am/util.test.js b/src/v0/destinations/am/util.test.js index 723ff3a302..498980d182 100644 --- a/src/v0/destinations/am/util.test.js +++ b/src/v0/destinations/am/util.test.js @@ -1,4 +1,9 @@ -const { getUnsetObj, validateEventType, userPropertiesPostProcess } = require('./utils'); +const { + getUnsetObj, + validateEventType, + userPropertiesPostProcess, + updateWithSkipAttribute, +} = require('./utils'); describe('getUnsetObj', () => { it("should return undefined when 'message.integrations.Amplitude.fieldsToUnset' is not array", () => { @@ -164,3 +169,31 @@ describe('userPropertiesPostProcess', () => { }); }); }); + +describe('updateWithSkipAttribute', () => { + // when 'skipUserPropertiesSync ' is present in 'integrations.Amplitude', return the original payload. + it("should return the original payload when 'skipUserPropertiesSync' is present", () => { + const message = { integrations: { Amplitude: { skipUserPropertiesSync: true } } }; + const payload = { key: 'value' }; + const expectedPayload = { key: 'value', $skip_user_properties_sync: true }; + updateWithSkipAttribute(message, payload); + expect(expectedPayload).toEqual(payload); + }); + + // When 'skipUserPropertiesSync' is not present in 'integrations.Amplitude', return the original payload. + it("should return the original payload when 'skipUserPropertiesSync' is not present", () => { + const message = { integrations: { Amplitude: {} } }; + const payload = { key: 'value' }; + const expectedPayload = { key: 'value' }; + updateWithSkipAttribute(message, payload); + expect(payload).toEqual(expectedPayload); + }); + // When 'message' is null, return null. + it("should return null when 'message' is null", () => { + const message = null; + const payload = { key: 'value' }; + const expectedPayload = { key: 'value' }; + updateWithSkipAttribute(message, payload); + expect(payload).toEqual(expectedPayload); + }); +}); diff --git a/src/v0/destinations/am/utils.js b/src/v0/destinations/am/utils.js index 190a5c1bae..8de899182b 100644 --- a/src/v0/destinations/am/utils.js +++ b/src/v0/destinations/am/utils.js @@ -11,6 +11,7 @@ const get = require('get-value'); const uaParser = require('@amplitude/ua-parser-js'); const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const set = require('set-value'); const logger = require('../../../logger'); const { isDefinedAndNotNull } = require('../../util'); @@ -110,6 +111,13 @@ const getUnsetObj = (message) => { return unsetObject; }; +const updateWithSkipAttribute = (message, payload) => { + const skipAttribute = get(message, 'integrations.Amplitude.skipUserPropertiesSync'); + if (skipAttribute) { + set(payload, '$skip_user_properties_sync', true); + } +}; + /** * Check for evType as in some cases, like when the page name is absent, * either the template depends only on the event.name or there is no template provided by user @@ -187,4 +195,5 @@ module.exports = { getUnsetObj, validateEventType, userPropertiesPostProcess, + updateWithSkipAttribute, }; diff --git a/test/integrations/destinations/am/processor/data.ts b/test/integrations/destinations/am/processor/data.ts index b645fb5ac7..01f9feb44a 100644 --- a/test/integrations/destinations/am/processor/data.ts +++ b/test/integrations/destinations/am/processor/data.ts @@ -10739,6 +10739,7 @@ export const data = [ integrations: { All: true, Amplitude: { + skipUserPropertiesSync: false, event_id: 2, }, }, @@ -10894,6 +10895,7 @@ export const data = [ integrations: { All: true, Amplitude: { + skipUserPropertiesSync: true, event_id: 2, }, }, @@ -10949,6 +10951,7 @@ export const data = [ insert_id: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', ip: '1.1.1.1', event_id: 2, + $skip_user_properties_sync: true, user_properties: { initial_referrer: 'https://docs.rudderstack.com', initial_referring_domain: 'docs.rudderstack.com', From cff7d1c4578087a37614c0ef4529058481873479 Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Thu, 14 Mar 2024 11:05:53 +0530 Subject: [PATCH 22/35] fix: fb pixel test case refactor (#3075) * fix: initial commit * fix: identify validation page screen * fix: adding ecomm test cases * fix: adding config level features test cases * fix: updating the common destination * fix: review comments addressed * fix: review comments address * fix: enhance code coverage * fix: review comments addressed --- .../facebook_pixel/transform.test.js | 28 + .../destinations/facebook_pixel/utils.test.js | 154 + src/v0/util/facebookUtils/index.js | 1 + src/v0/util/facebookUtils/index.test.js | 340 +- .../processor/configLevelFeaturesTestData.ts | 766 ++ .../facebook_pixel/processor/data.ts | 6574 +---------------- .../facebook_pixel/processor/ecommTestData.ts | 1120 +++ .../processor/identifyTestData.ts | 209 + .../processor/pageScreenTestData.ts | 721 ++ .../facebook_pixel/processor/trackTestData.ts | 208 + .../processor/validationTestData.ts | 373 + test/integrations/testUtils.ts | 2 + 12 files changed, 3933 insertions(+), 6563 deletions(-) create mode 100644 src/v0/destinations/facebook_pixel/utils.test.js create mode 100644 test/integrations/destinations/facebook_pixel/processor/configLevelFeaturesTestData.ts create mode 100644 test/integrations/destinations/facebook_pixel/processor/ecommTestData.ts create mode 100644 test/integrations/destinations/facebook_pixel/processor/identifyTestData.ts create mode 100644 test/integrations/destinations/facebook_pixel/processor/pageScreenTestData.ts create mode 100644 test/integrations/destinations/facebook_pixel/processor/trackTestData.ts create mode 100644 test/integrations/destinations/facebook_pixel/processor/validationTestData.ts diff --git a/src/v0/destinations/facebook_pixel/transform.test.js b/src/v0/destinations/facebook_pixel/transform.test.js index 25332d770c..d3c5baa070 100644 --- a/src/v0/destinations/facebook_pixel/transform.test.js +++ b/src/v0/destinations/facebook_pixel/transform.test.js @@ -24,6 +24,23 @@ const getTestMessage = () => { return message; }; +const getTestMessageWithoutProductIdAndCategory = () => { + let message = { + properties: { + currency: 'CAD', + quantity: 1, + price: 24.75, + value: 30, + name: 'my product 1', + testDimension: true, + testMetric: true, + position: 4.5, + query: 'HDMI Cable', + }, + }; + return message; +}; + const getTestCategoryToContent = () => { let categoryToContent = [ { @@ -52,6 +69,17 @@ describe('Unit test cases for facebook_pixel handle search', () => { expect(handleSearch(getTestMessage())).toEqual(expectedOutput); }); + it('should return content with content_ids and content fields as empty array', async () => { + const expectedOutput = { + content_ids: [], + content_category: '', + value: 30, + search_string: 'HDMI Cable', + contents: [], + }; + expect(handleSearch(getTestMessageWithoutProductIdAndCategory())).toEqual(expectedOutput); + }); + it("mapping 'product_id' with contentId", async () => { let message = getTestMessage(); message.properties.product_id = 'prd-123'; diff --git a/src/v0/destinations/facebook_pixel/utils.test.js b/src/v0/destinations/facebook_pixel/utils.test.js new file mode 100644 index 0000000000..f32d7d7024 --- /dev/null +++ b/src/v0/destinations/facebook_pixel/utils.test.js @@ -0,0 +1,154 @@ +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { getActionSource, formatRevenue, getCategoryFromEvent } = require('./utils'); +const { CONFIG_CATEGORIES, OTHER_STANDARD_EVENTS } = require('./config'); + +describe('Test Facebook Pixel Utils', () => { + describe('getActionSource', () => { + // Returns 'other' if payload.action_source is not defined and channel is neither 'web' nor 'mobile' + it('should return "other" when payload.action_source is not defined and channel is neither "web" nor "mobile"', () => { + const payload = {}; + const channel = 'email'; + const result = getActionSource(payload, channel); + expect(result).toBe('other'); + }); + + // Returns payload.action_source if it is defined and is a valid value from ACTION_SOURCES_VALUES + it('should return payload.action_source when it is defined and is a valid value from ACTION_SOURCES_VALUES', () => { + const payload = { action_source: 'website' }; + const channel = 'email'; + const result = getActionSource(payload, channel); + expect(result).toBe('website'); + }); + + // Returns 'website' if channel is 'web' and payload.action_source is not defined + it('should return "website" when channel is "web" and payload.action_source is not defined', () => { + const payload = {}; + const channel = 'web'; + const result = getActionSource(payload, channel); + expect(result).toBe('website'); + }); + + // Throws an InstrumentationError if payload.action_source is defined but not a valid value from ACTION_SOURCES_VALUES + it('should throw an InstrumentationError when payload.action_source is defined but not a valid value from ACTION_SOURCES_VALUES', () => { + const payload = { action_source: 'invalid' }; + const channel = 'email'; + expect(() => { + getActionSource(payload, channel); + }).toThrow(InstrumentationError); + }); + }); + + describe('formatRevenue', () => { + // Returns a number with two decimal places when passed a valid revenue value. + it('should return a number with two decimal places when passed a valid revenue value', () => { + const revenue = '100.50'; + const formattedRevenue = formatRevenue(revenue); + expect(formattedRevenue).toBe(100.5); + }); + + // Returns 0 when passed a null revenue value. + it('should return 0 when passed a null revenue value', () => { + const revenue = null; + const formattedRevenue = formatRevenue(revenue); + expect(formattedRevenue).toBe(0); + }); + + // Returns 0 when passed an undefined revenue value. + it('should return 0 when passed an undefined revenue value', () => { + const revenue = undefined; + const formattedRevenue = formatRevenue(revenue); + expect(formattedRevenue).toBe(0); + }); + + // Throws an InstrumentationError when passed a non-numeric string revenue value. + it('should throw an InstrumentationError when passed a non-numeric string revenue value', () => { + const revenue = 'abc'; + expect(() => { + formatRevenue(revenue); + }).toThrow(InstrumentationError); + }); + + // Returns a number with two decimal places when passed a numeric string revenue value with more than two decimal places. + it('should return a number with two decimal places when passed a numeric string revenue value with more than two decimal places', () => { + const revenue = '100.555'; + const formattedRevenue = formatRevenue(revenue); + expect(formattedRevenue).toBe(100.56); + }); + + // Returns a number with two decimal places when passed a numeric value with more than two decimal places. + it('should return a number with two decimal places when passed a numeric value with more than two decimal places', () => { + const revenue = 100.555; + const formattedRevenue = formatRevenue(revenue); + expect(formattedRevenue).toBe(100.56); + }); + }); + + describe('getCategoryFromEvent', () => { + // The function correctly maps the eventName to its corresponding category. + it('should correctly map the eventName to its corresponding category', () => { + const eventName = CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED.type; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED); + }); + + // The function returns the correct category for a given eventName. + it('should return the correct category for a given eventName', () => { + const eventName = CONFIG_CATEGORIES.PRODUCT_VIEWED.type; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.PRODUCT_VIEWED); + }); + + // The function returns the default category if the eventName is not recognized. + it('should return the default category if the eventName is not recognized', () => { + const eventName = 'unknownEvent'; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); + }); + + // The function handles null or undefined eventName inputs. + it('should handle null or undefined eventName inputs', () => { + const eventName = null; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); + }); + + // The function handles empty string eventName inputs. + it('should handle empty string eventName inputs', () => { + const eventName = ''; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); + }); + + // The function handles eventName inputs that are not strings. + it('should handle eventName inputs that are not strings', () => { + const eventName = 123; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); + }); + + // The function handles multiple eventNames that map to the same category. + it('should correctly map multiple eventNames to the same category', () => { + const eventName1 = CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED.type; + const eventName2 = CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED.eventName; + const result1 = getCategoryFromEvent(eventName1); + const result2 = getCategoryFromEvent(eventName2); + expect(result1).toEqual(CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED); + expect(result2).toEqual(CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED); + }); + + // The function handles eventNames that are included in the OTHER_STANDARD_EVENTS list. + it('should correctly handle eventNames included in the OTHER_STANDARD_EVENTS list', () => { + const eventName = OTHER_STANDARD_EVENTS[0]; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.OTHER_STANDARD); + expect(result.eventName).toEqual(eventName); + }); + + // The function handles eventNames that are not recognized and not in the OTHER_STANDARD_EVENTS list. + it('should correctly handle unrecognized eventNames', () => { + const eventName = 'unrecognizedEvent'; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); + }); + }); +}); diff --git a/src/v0/util/facebookUtils/index.js b/src/v0/util/facebookUtils/index.js index 7fa1e898fe..c7753d255f 100644 --- a/src/v0/util/facebookUtils/index.js +++ b/src/v0/util/facebookUtils/index.js @@ -298,4 +298,5 @@ module.exports = { transformedPayloadData, formingFinalResponse, fetchUserData, + deduceFbcParam, }; diff --git a/src/v0/util/facebookUtils/index.test.js b/src/v0/util/facebookUtils/index.test.js index 98e4ccec40..20c4ee59f2 100644 --- a/src/v0/util/facebookUtils/index.test.js +++ b/src/v0/util/facebookUtils/index.test.js @@ -1,5 +1,11 @@ -const { transformedPayloadData } = require('./index'); +const { + transformedPayloadData, + fetchUserData, + deduceFbcParam, + getContentType, +} = require('./index'); const sha256 = require('sha256'); +const { MAPPING_CONFIG, CONFIG_CATEGORIES } = require('../../destinations/facebook_pixel/config'); describe('transformedPayloadData_function', () => { // Tests with default values for all parameters @@ -301,3 +307,335 @@ describe('transformedPayloadData_function', () => { expect(result).toEqual({}); }); }); + +describe('deduceFbcParam', () => { + // Should return undefined if message.context.page.url is undefined + it('should return undefined when message.context.page.url is undefined', () => { + const message = {}; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should return undefined if URL constructor throws an error + it('should return undefined when URL constructor throws an error', () => { + const message = { + context: { + page: { + url: 'invalid-url', + }, + }, + }; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should return undefined if fbclid is undefined + it('should return undefined when fbclid is undefined', () => { + const message = { + context: { + page: { + url: 'https://example.com', + }, + }, + }; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should handle message with empty context object + it('should handle message with empty context object', () => { + const message = { + context: {}, + }; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should handle message with empty page object + it('should handle message with empty page object', () => { + const message = { + context: { + page: {}, + }, + }; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should handle message with empty url string + it('should handle message with empty url string', () => { + const message = { + context: { + page: { + url: '', + }, + }, + }; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should return fbc parameter when all conditions are met + it('should return fbc parameter when all conditions are met', () => { + const message = { + context: { + page: { + url: 'https://example.com?fbclid=123456', + }, + }, + }; + const result = deduceFbcParam(message); + expect(result).toEqual(expect.stringContaining('fb.1.')); + }); +}); + +describe('fetchUserData', () => { + const message = { + channel: 'web', + context: { + traits: { + name: 'Rudder Test', + email: 'abc@gmail.com', + firstname: 'Rudder', + lastname: 'Test', + phone: 9000000000, + gender: 'female', + }, + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + properties: { + plan: 'standard plan', + name: 'rudder test', + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + originalTimestamp: '2023-10-14T15:46:51.693229+05:30', + anonymousId: '00000000000000000000000000', + userId: '123456', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }; + + const Config = { + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: true, + whitelistPiiProperties: [ + { + whitelistPiiProperties: '', + }, + ], + }; + + // Returns a valid user data object when given valid inputs. + it('should return a valid user data object when given valid inputs without integrations object', () => { + const mappingJson = MAPPING_CONFIG[CONFIG_CATEGORIES.USERDATA.name]; + const destinationName = 'fb_pixel'; + + const result = fetchUserData(message, Config, mappingJson, destinationName); + + expect(result).toEqual({ + external_id: '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + ph: '593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579', + ge: '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + ln: '532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25', + fn: '2c2ccf28d806f6f9a34b67aa874d2113b7ac1444f1a4092541b8b75b84771747', + client_ip_address: '0.0.0.0', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + fbc: undefined, + }); + }); + + it('should return a valid user data object when given valid inputs with integrations object', () => { + const mappingJson = MAPPING_CONFIG[CONFIG_CATEGORIES.USERDATA.name]; + const destinationName = 'fb_pixel'; + message.integrations.FacebookPixel = { hashed: true }; + + const result = fetchUserData(message, Config, mappingJson, destinationName); + + expect(result).toEqual({ + em: 'abc@gmail.com', + external_id: '123456', + ph: '9000000000', + ge: '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + ln: 'Test', + fn: 'Rudder', + client_ip_address: '0.0.0.0', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + fbc: undefined, + }); + }); + + it('should return null when mappingJson is undefined', () => { + const mappingJson = undefined; + const destinationName = 'fb_pixel'; + const result = fetchUserData(message, Config, mappingJson, destinationName); + + expect(result).toBeNull(); + }); + + it('should return hashed data when destinationName is undefined', () => { + const mappingJson = MAPPING_CONFIG[CONFIG_CATEGORIES.USERDATA.name]; + const destinationName = undefined; + + const result = fetchUserData(message, Config, mappingJson, destinationName); + + expect(result).toEqual({ + client_ip_address: '0.0.0.0', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + external_id: '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', + fbc: undefined, + fn: '2c2ccf28d806f6f9a34b67aa874d2113b7ac1444f1a4092541b8b75b84771747', + ge: '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + ln: '532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25', + ph: '593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579', + }); + }); +}); + +describe('getContentType', () => { + // Returns default value when no category or categoryToContent is provided + it('should return default value when no category or categoryToContent is provided', () => { + const message = { + properties: { + produtcs: [ + { + product_id: '123', + }, + ], + }, + }; + const defaultValue = 'product'; + const categoryToContent = []; + const destinationName = 'fb_pixel'; + + const result = getContentType(message, defaultValue, categoryToContent, destinationName); + + expect(result).toBe(defaultValue); + }); + + // Returns default value when categoryToContent is not an array + it('should return default value when categoryToContent is not an array', () => { + const message = { + properties: { + products: [ + { + product_id: '123', + }, + ], + }, + }; + const defaultValue = 'product'; + const categoryToContent = 'not an array'; + const destinationName = 'fb_pixel'; + + const result = getContentType(message, defaultValue, categoryToContent, destinationName); + + expect(result).toBe(defaultValue); + }); + + // Returns categoryToContent value when category is provided and matches with categoryToContent + it('should return categoryToContent value when category is provided and matches with categoryToContent', () => { + const message = { + properties: { + category: 'clothing', + }, + }; + const defaultValue = 'product'; + const categoryToContent = [{ from: 'clothing', to: 'garments' }]; + const destinationName = 'fb_pixel'; + + const result = getContentType(message, defaultValue, categoryToContent, destinationName); + + expect(result).toBe(categoryToContent[0].to); + }); + + // Returns integrationsObj.contentType when it exists + it('should return integrationsObj.contentType when it exists', () => { + const message = { + properties: { + products: [ + { + product_id: '123', + }, + ], + }, + integrations: { + fb_pixel: { + contentType: 'content_type_value', + }, + }, + }; + const defaultValue = 'product'; + const categoryToContent = []; + const destinationName = 'fb_pixel'; + const integrationsObj = { + contentType: 'content_type_value', + }; + + const result = getContentType(message, defaultValue, categoryToContent, destinationName); + + expect(result).toBe(integrationsObj.contentType); + }); + + // Returns 'product' when category is 'clothing' and categoryToContent is not provided + it("should return 'product' when category is 'clothing' and categoryToContent is not provided", () => { + const message = { + properties: { + category: 'clothing', + }, + }; + const defaultValue = 'product'; + const categoryToContent = []; + const destinationName = 'fb_pixel'; + + const result = getContentType(message, defaultValue, categoryToContent, destinationName); + + expect(result).toBe(defaultValue); + }); +}); diff --git a/test/integrations/destinations/facebook_pixel/processor/configLevelFeaturesTestData.ts b/test/integrations/destinations/facebook_pixel/processor/configLevelFeaturesTestData.ts new file mode 100644 index 0000000000..5a7beb4174 --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/configLevelFeaturesTestData.ts @@ -0,0 +1,766 @@ +import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { + overrideDestination, + generateTrackPayload, + generateMetadata, + transformResultBuilder, +} from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + limitedDataUSage: true, + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: 'ABC Started', + to: 'InitiateCheckout', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'email', + }, + ], + categoryToContent: [ + { + from: 'clothing', + to: 'newClothing', + }, + ], + }, + Enabled: true, +}; + +const commonUserTraits = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', +}; + +// the below object has properties that are used as whitelist and blacklist properties in below test cases +const piiPropertiesForAllowDeny = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', + firstName: 'John', + lastName: 'Doe', + whitelistProp1: 'val1', + blacklistProp2: 'val2', + blacklistProp3: 'val3', + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', +}; + +const commonPropertiesWithoutProductArray = { + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', +}; + +const commonTimestamp = new Date('2023-10-14'); + +export const configLevelFeaturesTestData: ProcessorTestData[] = [ + { + id: 'facebook_pixel-config-test-1', + name: 'facebook_pixel', + description: + 'config feature : limitedDataUSage switched on. Ref:https://developers.facebook.com/docs/marketing-apis/data-processing-options/#supported-tools-and-apis ', + scenario: 'configuration', + successCriteria: 'Response should contain limitedDataUSage related fields', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + dataProcessingOptions: ['val1', 'val2', 'val3'], + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + data_processing_options: 'val1', + data_processing_options_country: 'val2', + data_processing_options_state: 'val3', + custom_data: { + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['dummy'], + content_type: 'product_group', + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-2', + name: 'facebook_pixel', + scenario: 'configuration', + description: + 'config feature : While categoryToContent mapping is filled up in UI, but category is passed with message.properties as well. message.properties.category should be given priority over categoryToContent mapping', + successCriteria: 'Response should contain category mapped to newClothing', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...commonPropertiesWithoutProductArray, category: 'clothing' }, + context: { + traits: commonUserTraits, + dataProcessingOptions: ['val1', 'val2', 'val3'], + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + data_processing_options: 'val1', + data_processing_options_country: 'val2', + data_processing_options_state: 'val3', + custom_data: { + category: 'clothing', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['clothing'], + content_type: 'newClothing', + contents: [ + { + id: 'clothing', + quantity: 1, + }, + ], + content_category: 'clothing', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-3', + name: 'facebook_pixel', + description: + 'config feature : ContentCategoryMapping table is filled up, and category is passed with properties along with contentType via integrations object', + scenario: 'configuration', + successCriteria: 'contentType should be used from integrations object', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...commonPropertiesWithoutProductArray, category: 'clothing' }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + integrations: { + FacebookPixel: { + contentType: 'newClothingFromIntegrationObject', + }, + }, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'clothing', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['clothing'], + content_type: 'newClothingFromIntegrationObject', + contents: [ + { + id: 'clothing', + quantity: 1, + }, + ], + content_category: 'clothing', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-4', + name: 'facebook_pixel', + description: + 'config feature : Config mapped whiteList and blackListed properties with marked hashed within integrations object, along with default pii property email in the properties', + scenario: 'configuration', + successCriteria: + 'BlackListed properties should not be hashed and default pii property should be deleted from the properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...piiPropertiesForAllowDeny }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + integrations: { + FacebookPixel: { + hashed: true, + }, + }, + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'whitelistProp1', + }, + ], + blacklistPiiProperties: [ + { + blacklistPiiProperties: 'blacklistProp2', + }, + { + blacklistPiiProperties: 'blacklistProp3', + }, + ], + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: 'default-user-id', + em: 'abc@gmail.com', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + whitelistProp1: 'val1', + blacklistProp2: 'val2', + blacklistProp3: 'val3', + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['dummy'], + content_type: 'product_group', + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-5', + name: 'facebook_pixel', + description: + 'config feature : Config mapped whiteList and blackListed properties without marked hashed within integrations object but marked hashed true from UI, along with default pii property email in the properties', + scenario: 'configuration', + successCriteria: + 'BlackListed properties should be hashed and default pii property should be deleted from the properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...piiPropertiesForAllowDeny }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'whitelistProp1', + }, + ], + blacklistPiiProperties: [ + { + blacklistPiiProperties: 'blacklistProp2', + blacklistPiiHash: true, + }, + { + blacklistPiiProperties: 'blacklistProp3', + blacklistPiiHash: true, + }, + ], + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + whitelistProp1: 'val1', + blacklistProp2: + '528e5290f8ff0eb0325f0472b9c1a9ef4fac0b02ff6094b64d9382af4a10444b', + blacklistProp3: + 'bac8d4414984861d5199b7a97699c728bee36c4084299b2ca905434cf65d8944', + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['dummy'], + content_type: 'product_group', + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-6', + name: 'facebook_pixel', + description: + 'config feature : Config mapped whiteList and blackListed properties marked hashed within integrations object but marked hashed true from UI, along with default pii property email in the properties', + scenario: 'configuration', + successCriteria: + 'BlackListed properties should not be hashed again and default pii property should be deleted from the properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...piiPropertiesForAllowDeny }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + integrations: { + FacebookPixel: { + hashed: true, + }, + }, + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'whitelistProp1', + }, + ], + blacklistPiiProperties: [ + { + blacklistPiiProperties: 'blacklistProp2', + blacklistPiiHash: true, + }, + { + blacklistPiiProperties: 'blacklistProp3', + blacklistPiiHash: true, + }, + ], + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: 'default-user-id', + em: 'abc@gmail.com', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + whitelistProp1: 'val1', + blacklistProp2: 'val2', + blacklistProp3: 'val3', + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['dummy'], + content_type: 'product_group', + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-7', + name: 'facebook_pixel', + description: + 'properties.content_type is given priority over populating it from categoryToContent mapping.', + scenario: 'configuration', + successCriteria: 'Response should contain content_type as product_group and not newClothing', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...piiPropertiesForAllowDeny, content_type: 'product_group' }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + integrations: { + FacebookPixel: { + hashed: true, + }, + }, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: 'default-user-id', + em: 'abc@gmail.com', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + whitelistProp1: 'val1', + blacklistProp2: 'val2', + blacklistProp3: 'val3', + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_type: 'product_group', + content_ids: ['dummy'], + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/processor/data.ts b/test/integrations/destinations/facebook_pixel/processor/data.ts index f6a5cd1e20..6af7e3cd9b 100644 --- a/test/integrations/destinations/facebook_pixel/processor/data.ts +++ b/test/integrations/destinations/facebook_pixel/processor/data.ts @@ -1,4 +1,9 @@ -import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { identifyTestData } from './identifyTestData'; +import { trackTestData } from './trackTestData'; +import { validationTestData } from './validationTestData'; +import { pageScreenTestData } from './pageScreenTestData'; +import { ecommTestData } from './ecommTestData'; +import { configLevelFeaturesTestData } from './configLevelFeaturesTestData'; export const mockFns = (_) => { // @ts-ignore @@ -6,6565 +11,10 @@ export const mockFns = (_) => { }; export const data = [ - { - name: 'facebook_pixel', - description: 'Test 0', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - channel: 'mobile', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: ' aBc@gmail.com ', - address: { - zip: 1234, - }, - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T00:00:00.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - removeExternalId: true, - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"spin_result","event_time":1697221800,"action_source":"app","custom_data":{"additional_bet_index":0,"value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 1', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: ' aBc@gmail.com ', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'group', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: 'Message type group not supported', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 2', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - traits: { - name: 'Test', - }, - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - properties: { - plan: 'standard plan', - name: 'rudder test', - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: - 'For identify events, "Advanced Mapping" configuration must be enabled on the RudderStack dashboard', - statTags: { - errorCategory: 'dataValidation', - errorType: 'configuration', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 3', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - traits: { - name: 'Rudder Test', - email: 'abc@gmail.com', - firstname: 'Rudder', - lastname: 'Test', - phone: 9000000000, - gender: 'female', - }, - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - properties: { - plan: 'standard plan', - name: 'rudder test', - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: true, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","ph":"593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579","ge":"252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111","ln":"532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25","fn":"2c2ccf28d806f6f9a34b67aa874d2113b7ac1444f1a4092541b8b75b84771747","client_ip_address":"0.0.0.0","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"identify","event_time":1697278611,"event_id":"84e26acc-56a5-4835-8233-591137fca468","action_source":"website"}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 4', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - traits: { - name: 'Rudder Test', - email: 'abc@gmail.com', - phone: 9000000000, - address: { - postalCode: 1234, - }, - gender: 'female', - }, - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - properties: { - plan: 'standard plan', - name: 'rudder test', - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: true, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","ph":"593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579","ge":"252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4","client_ip_address":"0.0.0.0","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36","fn":"2c2ccf28d806f6f9a34b67aa874d2113b7ac1444f1a4092541b8b75b84771747","ln":"532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25"},"event_name":"identify","event_time":1697278611,"event_id":"84e26acc-56a5-4835-8233-591137fca468","action_source":"website"}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 5', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - email: 'abc@gmail.com', - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","custom_data":{"additional_bet_index":0,"value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 6', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - email: 'abc@gmail.com', - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","custom_data":{"additional_bet_index":0,"email":"abc@gmail.com","value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 7', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - email: 'abc@gmail.com', - phone: 9000000000, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","custom_data":{"additional_bet_index":0,"email":"abc@gmail.com","value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 8', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - email: 'abc@gmail.com', - phone: 9000000000, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","custom_data":{"additional_bet_index":0,"email":"abc@gmail.com","phone":"593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579","value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 9', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - timestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: 'xyz', - search: 'def', - title: 'ghi', - url: 'jkl', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_ip_address":"0.0.0.0","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Viewed page ApplicationLoaded","event_time":1697278611,"event_source_url":"jkl","event_id":"5e10d13a-bf9a-44bf-b884-43a9e591ea71","action_source":"website","custom_data":{"path":"/abc","referrer":"xyz","search":"def","title":"ghi","url":"jkl"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 10', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: 'xyz', - search: 'def', - title: 'ghi', - url: 'jkl', - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_ip_address":"0.0.0.0","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Viewed a page","event_time":1697278611,"event_source_url":"jkl","event_id":"5e10d13a-bf9a-44bf-b884-43a9e591ea71","action_source":"website","custom_data":{"path":"/abc","referrer":"xyz","search":"def","title":"ghi","url":"jkl"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 11', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product list viewed', - properties: { - phone: 9000000000, - email: 'abc@gmail.com', - category: 'cat 1', - list_id: '1234', - filters: [ - { - type: 'department', - value: 'beauty', - }, - { - type: 'price', - value: 'under', - }, - ], - sorts: [ - { - type: 'price', - value: 'desc', - }, - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"phone":"593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579","email":"abc@gmail.com","category":"cat 1","list_id":"1234","filters[0].type":"department","filters[0].value":"beauty","filters[1].type":"price","filters[1].value":"under","sorts[0].type":"price","sorts[0].value":"desc","testDimension":true,"testMetric":true,"content_ids":["cat 1"],"content_type":"product_group","contents":[{"id":"cat 1","quantity":1}],"content_category":"cat 1","value":0,"currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 12', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product list viewed', - properties: { - phone: 9000000000, - email: 'abc@gmail.com', - category: 'cat 1', - list_id: '1234', - filters: [ - { - type: 'department', - value: 'beauty', - }, - { - type: 'price', - value: 'under', - }, - ], - sorts: [ - { - type: 'price', - value: 'desc', - }, - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"category":"cat 1","list_id":"1234","filters[0].type":"department","filters[0].value":"beauty","filters[1].type":"price","filters[1].value":"under","sorts[0].type":"price","sorts[0].value":"desc","testDimension":true,"testMetric":true,"content_ids":["cat 1"],"content_type":"product_group","contents":[{"id":"cat 1","quantity":1}],"content_category":"cat 1","value":0,"currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 13', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product list viewed', - properties: { - email: 'abc@gmail.com', - quantity: 2, - category: 'cat 1', - list_id: '1234', - contentName: 'nutrition', - value: 18.9, - filters: [ - { - type: 'department', - value: 'beauty', - }, - { - type: 'price', - value: 'under', - }, - ], - sorts: [ - { - type: 'price', - value: 'desc', - }, - ], - products: [ - { - product_id: '507f1f77bcf86cd799439011', - productDimension: 'My Product Dimension', - productMetric: 'My Product Metric', - position: 10, - }, - { - product_id: '507f1f77bcf86cdef799439011', - productDimension: 'My Product Dimension1', - productMetric: 'My Product Metric1', - position: -10, - }, - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"quantity":2,"category":"cat 1","list_id":"1234","contentName":"nutrition","value":18.9,"filters[0].type":"department","filters[0].value":"beauty","filters[1].type":"price","filters[1].value":"under","sorts[0].type":"price","sorts[0].value":"desc","products[0].product_id":"507f1f77bcf86cd799439011","products[0].productDimension":"My Product Dimension","products[0].productMetric":"My Product Metric","products[0].position":10,"products[1].product_id":"507f1f77bcf86cdef799439011","products[1].productDimension":"My Product Dimension1","products[1].productMetric":"My Product Metric1","products[1].position":-10,"testDimension":true,"testMetric":true,"content_ids":["507f1f77bcf86cd799439011","507f1f77bcf86cdef799439011"],"content_type":"product","contents":[{"id":"507f1f77bcf86cd799439011","quantity":2},{"id":"507f1f77bcf86cdef799439011","quantity":2}],"content_category":"cat 1","content_name":"nutrition","currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 14', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'my product list', - properties: { - email: 'abc@gmail.com', - quantity: 2, - category: 'cat 1', - list_id: '1234', - filters: [ - { - type: 'department', - value: 'beauty', - }, - { - type: 'price', - value: 'under', - }, - ], - sorts: [ - { - type: 'price', - value: 'desc', - }, - ], - products: [ - { - product_id: '507f1f77bcf86cd799439011', - productDimension: 'My Product Dimension', - productMetric: 'My Product Metric', - position: 10, - }, - { - product_id: '507f1f77bcf86cdef799439011', - productDimension: 'My Product Dimension1', - productMetric: 'My Product Metric1', - position: -10, - }, - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - removeExternalId: false, - eventsToEvents: [ - { - from: 'My product list', - to: 'ViewContent', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: 'list_id', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"quantity":2,"category":"cat 1","list_id":"1234","filters[0].type":"department","filters[0].value":"beauty","filters[1].type":"price","filters[1].value":"under","sorts[0].type":"price","sorts[0].value":"desc","products[0].product_id":"507f1f77bcf86cd799439011","products[0].productDimension":"My Product Dimension","products[0].productMetric":"My Product Metric","products[0].position":10,"products[1].product_id":"507f1f77bcf86cdef799439011","products[1].productDimension":"My Product Dimension1","products[1].productMetric":"My Product Metric1","products[1].position":-10,"testDimension":true,"testMetric":true,"content_ids":["507f1f77bcf86cd799439011","507f1f77bcf86cdef799439011"],"content_type":"product","contents":[{"id":"507f1f77bcf86cd799439011","quantity":2},{"id":"507f1f77bcf86cdef799439011","quantity":2}],"content_category":"cat 1","value":0,"currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 15', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product viewed', - properties: { - currency: 'CAD', - quantity: 1, - price: 24.75, - name: 'my product 1', - category: 'clothing', - sku: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - removeExternalId: true, - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"price":24.75,"name":"my product 1","category":"clothing","sku":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"product","content_name":"my product 1","content_category":"clothing","value":24.75,"contents":[{"id":"p-298","quantity":1,"item_price":24.75}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 16', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product added', - properties: { - currency: 'CAD', - quantity: 1, - value: 24.75, - category: 'cat 1', - id: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - removeExternalId: false, - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.value', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"AddToCart","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"value":24.75,"category":"cat 1","id":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"product","content_name":"","content_category":"cat 1","contents":[{"id":"p-298","quantity":1}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 17', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - contentName: 'all about nutrition', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Purchase","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"order_id":"rudderstackorder1","total":99.99,"revenue":12.24,"shipping":13.99,"tax":20.99,"currency":"INR","contentName":"all about nutrition","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":3,"products[1].price":24.75,"products[1].name":"other product","products[1].sku":"p-299","content_ids":["p-298","p-299"],"content_type":"product","value":12.24,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":3,"item_price":24.75}],"num_items":2,"content_name":"all about nutrition"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 18', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'checkout started', - properties: { - currency: 'CAD', - category: 'clothing', - contentName: 'abc', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 1, - price: 24.75, - name: 'my product 2', - sku: 'p-299', - }, - ], - step: 1, - paymentMethod: 'Visa', - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: 'contentName', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"InitiateCheckout","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","category":"clothing","contentName":"abc","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":1,"products[1].price":24.75,"products[1].name":"my product 2","products[1].sku":"p-299","step":1,"paymentMethod":"Visa","testDimension":true,"testMetric":true,"content_category":"clothing","content_ids":["p-298","p-299"],"content_type":"product","value":0,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":1,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 19', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - dataProcessingOptions: [['LDU'], 1, 1000], - fbc: 'fb.1.1554763741205.AbCdEfGhIjKlMnOpQrStUvWxYz1234567890', - fbp: 'fb.1.1554763741205.234567890', - fb_login_id: 'fb_id', - lead_id: 'lead_id', - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUSage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","fbc":"fb.1.1554763741205.AbCdEfGhIjKlMnOpQrStUvWxYz1234567890","fbp":"fb.1.1554763741205.234567890","lead_id":"lead_id","fb_login_id":"fb_id"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","data_processing_options":["LDU"],"data_processing_options_country":1,"data_processing_options_state":1000,"custom_data":{"additional_bet_index":0,"value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 20', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - dataProcessingOptions: [['LDU'], 1, 1000], - fbc: 'fb.1.1554763741205.AbCdEfGhIjKlMnOpQrStUvWxYz1234567890', - fbp: 'fb.1.1554763741205.234567890', - fb_login_id: 'fb_id', - lead_id: 'lead_id', - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUSage: false, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","fbc":"fb.1.1554763741205.AbCdEfGhIjKlMnOpQrStUvWxYz1234567890","fbp":"fb.1.1554763741205.234567890","lead_id":"lead_id","fb_login_id":"fb_id"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","custom_data":{"additional_bet_index":0,"value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 21', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'products searched', - properties: { - product_id: 'p-298', - quantity: 2, - price: 18.9, - category: 'health', - value: 18.9, - query: 'HDMI cable', - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Search","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"product_id":"p-298","quantity":2,"price":18.9,"category":"health","value":18.9,"query":"HDMI cable","content_ids":["p-298"],"content_category":"health","contents":[{"id":"p-298","quantity":2,"item_price":18.9}],"search_string":"HDMI cable","currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 22', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'products searched', - properties: { - query: 'HDMI cable', - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - testDestination: true, - testEventCode: 'TEST1001', - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Search","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"query":"HDMI cable","content_ids":[],"content_category":"","value":0,"contents":[],"search_string":"HDMI cable","currency":"USD"}}', - ], - test_event_code: 'TEST1001', - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 23', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: 'xyz', - search: 'def', - title: 'ghi', - url: 'jkl', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - standardPageCall: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: 'url', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_ip_address":"0.0.0.0","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"PageView","event_time":1697278611,"event_source_url":"jkl","event_id":"5e10d13a-bf9a-44bf-b884-43a9e591ea71","action_source":"website","custom_data":{"path":"/abc","referrer":"xyz","search":"def","title":"ghi","url":"jkl"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 24', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'track page', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: 'track page', - to: 'PageView', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: 'additional_bet_index', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"PageView","event_time":1697278611,"action_source":"other","custom_data":{"revenue":400,"additional_bet_index":0}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 25', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'my product list', - properties: { - email: 'abc@gmail.com', - quantity: 2, - category: 'cat 1', - list_id: '1234', - filters: [ - { - type: 'department', - value: 'beauty', - }, - { - type: 'price', - value: 'under', - }, - ], - sorts: [ - { - type: 'price', - value: 'desc', - }, - ], - products: [ - { - product_id: '507f1f77bcf86cd799439011', - productDimension: 'My Product Dimension', - productMetric: 'My Product Metric', - position: 10, - }, - { - product_id: '507f1f77bcf86cdef799439011', - productDimension: 'My Product Dimension1', - productMetric: 'My Product Metric1', - position: -10, - }, - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: 'My product list', - to: 'Schedule', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: 'list_id', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Schedule","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"quantity":2,"category":"cat 1","list_id":"1234","filters[0].type":"department","filters[0].value":"beauty","filters[1].type":"price","filters[1].value":"under","sorts[0].type":"price","sorts[0].value":"desc","products[0].product_id":"507f1f77bcf86cd799439011","products[0].productDimension":"My Product Dimension","products[0].productMetric":"My Product Metric","products[0].position":10,"products[1].product_id":"507f1f77bcf86cdef799439011","products[1].productDimension":"My Product Dimension1","products[1].productMetric":"My Product Metric1","products[1].position":-10,"testDimension":true,"testMetric":true}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 26', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: "'event' is required", - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 27', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product added', - properties: { - currency: 'CAD', - quantity: 1, - value: '35.753', - category: 'cat 1', - id: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.value', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"AddToCart","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"value":35.75,"category":"cat 1","id":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"product","content_name":"","content_category":"cat 1","contents":[{"id":"p-298","quantity":1}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 28', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product added', - properties: { - currency: 'CAD', - quantity: 1, - value: '35.7A3', - category: 'cat 1', - id: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.value', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"AddToCart","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"value":35.7,"category":"cat 1","id":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"product","content_name":"","content_category":"cat 1","contents":[{"id":"p-298","quantity":1}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 29', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product added', - properties: { - currency: 'CAD', - quantity: 1, - value: 'ABC', - category: 'cat 1', - id: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.value', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: 'Revenue could not be converted to number', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 30', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - category: ['clothing', 'fishing'], - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Purchase","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"category[0]":"clothing","category[1]":"fishing","order_id":"rudderstackorder1","total":99.99,"revenue":12.24,"shipping":13.99,"tax":20.99,"currency":"INR","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":3,"products[1].price":24.75,"products[1].name":"other product","products[1].sku":"p-299","content_category":"clothing,fishing","content_ids":["p-298","p-299"],"content_type":"product","value":12.24,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":3,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 31', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - category: 100, - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Purchase","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"category":100,"order_id":"rudderstackorder1","total":99.99,"revenue":12.24,"shipping":13.99,"tax":20.99,"currency":"INR","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":3,"products[1].price":24.75,"products[1].name":"other product","products[1].sku":"p-299","content_category":"100","content_ids":["p-298","p-299"],"content_type":"product","value":12.24,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":3,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 32', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - category: { - category1: '1', - }, - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: "'properties.category' must be either be a string or an array", - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 33', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: 'spin_result', - to: 'Schedule', - }, - { - to: 'Schedule', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"Schedule","event_time":1697278611,"action_source":"other","custom_data":{"revenue":400,"additional_bet_index":0}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 34', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2019-08-24T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: 'spin_result', - to: 'Schedule', - }, - { - to: 'Schedule', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: - 'Events must be sent within seven days of their occurrence or up to one minute in the future.', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 35', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - originalTimestamp: '2019-04-16T15:50:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: 'validToken', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: true, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: - 'Events must be sent within seven days of their occurrence or up to one minute in the future.', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 36', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'products searched', - properties: { - query: { - key1: 'HDMI cable', - }, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: "'query' should be in string format only", - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 37', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'products searched', - properties: { - query: 50, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Search","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"query":50,"content_ids":[],"content_category":"","value":0,"contents":[],"search_string":50,"currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 38', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - page: { - url: 'https://theminimstory.com/collections/summer-of-pearls?utm_source=facebook&utm_medium=paidsocial&utm_campaign=carousel&utm_content=ad1-jul&fbclid=IwAR2SsDcjzd_TLZN-e93kxOeGBYO4pQ3AiyeXSheHW5emDeLw8uTvo6lTMPI', - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'checkout started', - properties: { - currency: 'CAD', - category: 'clothing', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 1, - price: 24.75, - name: 'my product 2', - sku: 'p-299', - }, - ], - step: 1, - paymentMethod: 'Visa', - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36","fbc":"fb.1.1697278611693.IwAR2SsDcjzd_TLZN-e93kxOeGBYO4pQ3AiyeXSheHW5emDeLw8uTvo6lTMPI"},"event_name":"InitiateCheckout","event_time":1697278611,"event_source_url":"https://theminimstory.com/collections/summer-of-pearls?utm_source=facebook&utm_medium=paidsocial&utm_campaign=carousel&utm_content=ad1-jul&fbclid=IwAR2SsDcjzd_TLZN-e93kxOeGBYO4pQ3AiyeXSheHW5emDeLw8uTvo6lTMPI","event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","category":"clothing","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":1,"products[1].price":24.75,"products[1].name":"my product 2","products[1].sku":"p-299","step":1,"paymentMethod":"Visa","testDimension":true,"testMetric":true,"content_category":"clothing","content_ids":["p-298","p-299"],"content_type":"product","value":0,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":1,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 39', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - page: { - url: 'https://theminimstory.com/collections/summer-of-pearls?utm_source=facebook&utm_medium=paidsocial&utm_campaign=carousel&utm_content=ad1-jul&fbclid=IwAR2SsDcjzd_TLZN-e93kxOeGBYO4pQ3AiyeXSheHW5emDeLw8uTvo6lTMPI', - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: { - name: 'checkout started', - }, - properties: { - currency: 'CAD', - category: 'clothing', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 1, - price: 24.75, - name: 'my product 2', - sku: 'p-299', - }, - ], - step: 1, - paymentMethod: 'Visa', - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: 'event name should be string', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 40', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - page: { - url: 'url in wrong format', - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'checkout started', - properties: { - currency: 'CAD', - category: 'clothing', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 1, - price: 24.75, - name: 'my product 2', - sku: 'p-299', - }, - ], - step: 1, - paymentMethod: 'Visa', - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"InitiateCheckout","event_time":1697278611,"event_source_url":"url in wrong format","event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","category":"clothing","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":1,"products[1].price":24.75,"products[1].name":"my product 2","products[1].sku":"p-299","step":1,"paymentMethod":"Visa","testDimension":true,"testMetric":true,"content_category":"clothing","content_ids":["p-298","p-299"],"content_type":"product","value":0,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":1,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 41', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product viewed', - properties: { - currency: 'CAD', - quantity: 1, - price: 24.75, - name: 'my product 1', - category: 'clothing', - sku: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - Facebook_Pixel: { - contentType: 'sending dedicated content type for this particular payload', - }, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - removeExternalId: true, - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"price":24.75,"name":"my product 1","category":"clothing","sku":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"sending dedicated content type for this particular payload","content_name":"my product 1","content_category":"clothing","value":24.75,"contents":[{"id":"p-298","quantity":1,"item_price":24.75}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 42', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product viewed', - properties: { - currency: 'CAD', - quantity: 1, - price: 24.75, - value: 18.9, - name: 'my product 1', - category: 'clothing', - sku: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - removeExternalId: true, - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.value', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"price":24.75,"value":18.9,"name":"my product 1","category":"clothing","sku":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"product","content_name":"my product 1","content_category":"clothing","contents":[{"id":"p-298","quantity":1,"item_price":24.75}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 43', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - contentName: 'all about nutrition', - products: { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: "'properties.products' is not sent as an Array", - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 44', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product list viewed', - properties: { - email: 'abc@gmail.com', - quantity: 2, - category: 'cat 1', - list_id: '1234', - contentName: 'nutrition', - value: 18.9, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - productDimension: 'My Product Dimension', - productMetric: 'My Product Metric', - position: 10, - }, - [ - { - product_id: '507f1f77bcf86cdef799439011', - productDimension: 'My Product Dimension1', - productMetric: 'My Product Metric1', - position: -10, - }, - ], - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: "'properties.products[1]' is not an object", - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 45', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'custom', - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - pixelId: 'dummyPixelId', - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: 'Access token not found. Aborting', - statTags: { - errorCategory: 'dataValidation', - errorType: 'configuration', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 46', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'custom', - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: 'Pixel Id not found. Aborting', - statTags: { - errorCategory: 'dataValidation', - errorType: 'configuration', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 47', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - metadata: { - jobId: 12, - }, - destination: { - secretConfig: {}, - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [], - limitedDataUSage: false, - accessToken: 'dummyAccessToken', - testDestination: false, - testEventCode: '', - standardPageCall: false, - blacklistedEvents: [], - whitelistedEvents: [], - eventFilteringOption: 'disable', - removeExternalId: false, - useUpdatedMapping: false, - oneTrustCookieCategories: [], - useNativeSDK: false, - eventDelivery: false, - eventDeliveryTS: 1686748039135, - }, - liveEventsConfig: { - eventDelivery: false, - eventDeliveryTS: 1686748039135, - }, - id: 'destId1', - workspaceId: 'wsp2', - transformations: [], - isConnectionEnabled: true, - isProcessorEnabled: true, - name: 'san-fb_pixel', - enabled: true, - deleted: false, - createdAt: '2023-06-06T13:36:08.579Z', - updatedAt: '2023-06-14T13:07:19.136Z', - revisionId: 'revId2', - secretVersion: 3, - }, - message: { - type: 'page', - sentAt: '2023-10-14T15:46:51.000Z', - userId: 'user@19', - channel: 'web', - context: { - os: { - name: '', - version: '', - }, - app: { - name: 'RudderLabs JavaScript SDK', - version: 'dev-snapshot', - namespace: 'com.rudderlabs.javascript', - }, - page: { - url: 'http://127.0.0.1:8888/', - path: '/', - title: 'Document', - search: '', - tab_url: 'http://127.0.0.1:8888/', - referrer: 'http://127.0.0.1:8888/', - initial_referrer: '$direct', - referring_domain: '127.0.0.1:8888', - initial_referring_domain: '', - }, - locale: 'en-GB', - screen: { - width: 1728, - height: 1117, - density: 2, - innerWidth: 547, - innerHeight: 915, - }, - traits: { - name: false, - source: 'rudderstack', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: 'dev-snapshot', - }, - campaign: {}, - sessionId: 1687769234506, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', - }, - rudderId: '6bbfd003-c074-4ee9-8674-c132ded9ff04', - timestamp: '2023-10-14T15:46:51.000Z', - properties: { - url: 'http://127.0.0.1:8888/', - path: '/', - title: 'Document', - search: '', - tab_url: 'http://127.0.0.1:8888/', - referrer: 'http://127.0.0.1:8888/', - initial_referrer: '$direct', - referring_domain: '127.0.0.1:8888', - initial_referring_domain: '', - }, - receivedAt: '2023-10-14T15:46:51.000Z', - request_ip: '49.206.54.243', - anonymousId: '700ab220-faad-4cdf-8484-63e4c6bce6fe', - integrations: { - All: true, - }, - originalTimestamp: '2023-10-14T15:46:51.000Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=dummyAccessToken`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"72fd46c9ecb386f6747664a3e1d524294a3d7a2c8ae4aeb22b1e578b75093635","client_ip_address":"49.206.54.243","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"},"event_name":"PageView","event_time":1697298411,"event_source_url":"http://127.0.0.1:8888/","action_source":"website","custom_data":{"url":"http://127.0.0.1:8888/","path":"/","title":"Document","search":"","tab_url":"http://127.0.0.1:8888/","referrer":"http://127.0.0.1:8888/","initial_referrer":"$direct","referring_domain":"127.0.0.1:8888","initial_referring_domain":""}}', - ], - }, - }, - files: {}, - userId: '', - }, - metadata: { - jobId: 12, - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 48', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - category: ['clothing', 'fishing'], - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - delivery_category: 'home_delivery', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - delivery_category: 'home_delivery', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Purchase","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"category[0]":"clothing","category[1]":"fishing","order_id":"rudderstackorder1","total":99.99,"revenue":12.24,"shipping":13.99,"tax":20.99,"currency":"INR","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[0].delivery_category":"home_delivery","products[1].quantity":3,"products[1].price":24.75,"products[1].name":"other product","products[1].sku":"p-299","products[1].delivery_category":"home_delivery","content_category":"clothing,fishing","content_ids":["p-298","p-299"],"content_type":"product","value":12.24,"contents":[{"id":"p-298","quantity":1,"item_price":24.75,"delivery_category":"home_delivery"},{"id":"p-299","quantity":3,"item_price":24.75,"delivery_category":"home_delivery"}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 49', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - channel: 'mobile', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: ' aBc@gmail.com ', - address: { - zip: 1234, - }, - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - content_ids: ['prod1', 'prod2'], - }, - timestamp: '2023-10-14T00:00:00.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - removeExternalId: true, - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"spin_result","event_time":1697221800,"action_source":"app","custom_data":{"additional_bet_index":0,"value":400,"content_ids":["prod1","prod2"]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 50', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - channel: 'mobile', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: ' aBc@gmail.com ', - address: { - zip: 1234, - }, - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - contents: [ - { - id: 'prod1', - quantity: 5, - item_price: 55, - }, - ], - }, - timestamp: '2023-10-14T00:00:00.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - removeExternalId: true, - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"spin_result","event_time":1697221800,"action_source":"app","custom_data":{"additional_bet_index":0,"value":400,"contents":[{"id":"prod1","quantity":5,"item_price":55}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: - 'Test 51: properties.content_type is given priority over populating it from categoryToContent mapping.', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - content_type: 'product_group', - category: ['clothing', 'fishing'], - order_id: 'rudderstackorder1', - revenue: 12.24, - currency: 'INR', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5"},"event_name":"Purchase","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"content_type":"product_group","category[0]":"clothing","category[1]":"fishing","order_id":"rudderstackorder1","revenue":12.24,"currency":"INR","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":3,"products[1].price":24.75,"products[1].name":"other product","products[1].sku":"p-299","content_category":"clothing,fishing","content_ids":["p-298","p-299"],"value":12.24,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":3,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, + ...identifyTestData, + ...trackTestData, + ...validationTestData, + ...pageScreenTestData, + ...ecommTestData, + ...configLevelFeaturesTestData, ].map((d) => ({ ...d, mockFns })); diff --git a/test/integrations/destinations/facebook_pixel/processor/ecommTestData.ts b/test/integrations/destinations/facebook_pixel/processor/ecommTestData.ts new file mode 100644 index 0000000000..5d429d297d --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/ecommTestData.ts @@ -0,0 +1,1120 @@ +import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { generateTrackPayload, generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: 'ABC Started', + to: 'InitiateCheckout', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'email', + }, + ], + }, + Enabled: true, +}; + +const commonUserTraits = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', +}; + +const commonPropertiesWithoutProductArray = { + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', +}; + +const commonPropertiesWithProductArray = { + products: [ + { + product_id: '017c6f5d5cf86a4b22432066', + sku: '8732-98', + name: 'Just Another Game', + price: 22, + position: 2, + category: 'Games and Entertainment', + url: 'https://www.myecommercewebsite.com/product', + image_url: 'https://www.myecommercewebsite.com/product/path.jpg', + }, + { + product_id: '89ac6f5d5cf86a4b64eac145', + sku: '1267-01', + name: 'Wrestling Trump Cards', + price: 4, + position: 21, + category: 'Card Games', + }, + ], + category: 'dummy', + quantity: 10, + revenue: 100, + price: 50, + product_id: '12345', + order_id: '23456', +}; +const commonTimestamp = new Date('2023-10-14'); +const commonStatTags = { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'FACEBOOK_PIXEL', + module: 'destination', + implementation: 'native', + feature: 'processor', +}; + +export const ecommTestData: ProcessorTestData[] = [ + { + id: 'facebook_pixel-ecomm-test-1', + name: 'facebook_pixel', + description: + 'Track call : product list viewed event call with properties without product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to ViewContent, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['dummy'], + content_type: 'product_group', + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-2', + name: 'facebook_pixel', + description: 'Track call : product list viewed event call with properties with product array', + successCriteria: + 'It should be internally mapped to ViewContent, with necessary mapping from message.properties and from products array and should be sent to the destination', + scenario: 'ecommerce', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: commonPropertiesWithProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/dummyPixelId/events?access_token=09876', + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + 'products[0].product_id': '017c6f5d5cf86a4b22432066', + 'products[0].sku': '8732-98', + 'products[0].name': 'Just Another Game', + 'products[0].price': 22, + 'products[0].position': 2, + 'products[0].category': 'Games and Entertainment', + 'products[0].url': 'https://www.myecommercewebsite.com/product', + 'products[0].image_url': + 'https://www.myecommercewebsite.com/product/path.jpg', + 'products[1].product_id': '89ac6f5d5cf86a4b64eac145', + 'products[1].sku': '1267-01', + 'products[1].name': 'Wrestling Trump Cards', + 'products[1].price': 4, + 'products[1].position': 21, + 'products[1].category': 'Card Games', + category: 'dummy', + quantity: 10, + revenue: 100, + price: 50, + product_id: '12345', + order_id: '23456', + content_ids: ['017c6f5d5cf86a4b22432066', '89ac6f5d5cf86a4b64eac145'], + content_type: 'product', + contents: [ + { + id: '017c6f5d5cf86a4b22432066', + quantity: 10, + item_price: 22, + }, + { + id: '89ac6f5d5cf86a4b64eac145', + quantity: 10, + item_price: 4, + }, + ], + content_category: 'dummy', + value: 0, + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-3', + name: 'facebook_pixel', + description: 'Track call : product viewed event call with properties without product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to ViewContent, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product viewed', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 0, + product_id: '12345', + content_ids: ['12345'], + content_type: 'product', + content_name: '', + content_category: 'dummy', + currency: 'USD', + contents: [ + { + id: '12345', + quantity: 10, + }, + ], + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-4', + name: 'facebook_pixel', + description: 'Track call : product added event call with properties without product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to AddToCart, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product added', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'AddToCart', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 0, + product_id: '12345', + content_ids: ['12345'], + content_type: 'product', + content_name: '', + content_category: 'dummy', + currency: 'USD', + contents: [ + { + id: '12345', + quantity: 10, + }, + ], + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-5', + name: 'facebook_pixel', + description: 'Track call : order completed event call with properties without product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to purchase, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'order completed', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'Purchase', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 0, + product_id: '12345', + content_category: 'dummy', + content_ids: [], + content_type: 'product', + currency: 'USD', + contents: [], + num_items: 0, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-6', + name: 'facebook_pixel', + description: 'Track call : order completed event call with properties with product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to purchase, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'order completed', + properties: commonPropertiesWithProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/dummyPixelId/events?access_token=09876', + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'Purchase', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + 'products[0].product_id': '017c6f5d5cf86a4b22432066', + 'products[0].sku': '8732-98', + 'products[0].name': 'Just Another Game', + 'products[0].price': 22, + 'products[0].position': 2, + 'products[0].category': 'Games and Entertainment', + 'products[0].url': 'https://www.myecommercewebsite.com/product', + 'products[0].image_url': + 'https://www.myecommercewebsite.com/product/path.jpg', + 'products[1].product_id': '89ac6f5d5cf86a4b64eac145', + 'products[1].sku': '1267-01', + 'products[1].name': 'Wrestling Trump Cards', + 'products[1].price': 4, + 'products[1].position': 21, + 'products[1].category': 'Card Games', + category: 'dummy', + quantity: 10, + revenue: 100, + price: 50, + product_id: '12345', + order_id: '23456', + content_category: 'dummy', + content_ids: ['017c6f5d5cf86a4b22432066', '89ac6f5d5cf86a4b64eac145'], + content_type: 'product', + currency: 'USD', + value: 100, + contents: [ + { + id: '017c6f5d5cf86a4b22432066', + quantity: 10, + item_price: 22, + }, + { + id: '89ac6f5d5cf86a4b64eac145', + quantity: 10, + item_price: 4, + }, + ], + num_items: 2, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-7', + name: 'facebook_pixel', + description: 'Track call : products searched event call with properties', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to Search, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'products searched', + properties: { ...commonPropertiesWithoutProductArray, query: 'dummy' }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'Search', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + query: 'dummy', + content_ids: ['12345'], + content_category: 'dummy', + contents: [ + { + id: '12345', + quantity: 10, + }, + ], + search_string: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-8', + name: 'facebook_pixel', + description: + 'Track call : products searched event call with properties and unsupported query type', + scenario: 'ecommerce', + successCriteria: + 'Error : It should throw an error as the query is not a string or an array of strings', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'products searched', + properties: { ...commonPropertiesWithoutProductArray, query: ['dummy'] }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: "'query' should be in string format only", + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-9', + name: 'facebook_pixel', + description: 'Track call : checkout started event call with properties without product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to InitiateCheckout, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'checkout started', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'InitiateCheckout', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 0, + product_id: '12345', + content_category: 'dummy', + content_ids: [], + content_type: 'product', + currency: 'USD', + contents: [], + num_items: 0, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-10', + name: 'facebook_pixel', + description: 'Track call : checkout started event call with properties with product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to InitiateCheckout, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'checkout started', + properties: commonPropertiesWithProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/dummyPixelId/events?access_token=09876', + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'InitiateCheckout', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + 'products[0].product_id': '017c6f5d5cf86a4b22432066', + 'products[0].sku': '8732-98', + 'products[0].name': 'Just Another Game', + 'products[0].price': 22, + 'products[0].position': 2, + 'products[0].category': 'Games and Entertainment', + 'products[0].url': 'https://www.myecommercewebsite.com/product', + 'products[0].image_url': + 'https://www.myecommercewebsite.com/product/path.jpg', + 'products[1].product_id': '89ac6f5d5cf86a4b64eac145', + 'products[1].sku': '1267-01', + 'products[1].name': 'Wrestling Trump Cards', + 'products[1].price': 4, + 'products[1].position': 21, + 'products[1].category': 'Card Games', + category: 'dummy', + quantity: 10, + revenue: 100, + price: 50, + product_id: '12345', + order_id: '23456', + content_category: 'dummy', + content_ids: ['017c6f5d5cf86a4b22432066', '89ac6f5d5cf86a4b64eac145'], + content_type: 'product', + currency: 'USD', + value: 100, + contents: [ + { + id: '017c6f5d5cf86a4b22432066', + quantity: 10, + item_price: 22, + }, + { + id: '89ac6f5d5cf86a4b64eac145', + quantity: 10, + item_price: 4, + }, + ], + num_items: 2, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-11', + name: 'facebook_pixel', + description: + 'Track call : custom event ABC Started event call with properties with product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to InitiateCheckout, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'ABC Started', + properties: commonPropertiesWithProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'InitiateCheckout', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + 'products[0].product_id': '017c6f5d5cf86a4b22432066', + 'products[0].sku': '8732-98', + 'products[0].name': 'Just Another Game', + 'products[0].price': 22, + 'products[0].position': 2, + 'products[0].category': 'Games and Entertainment', + 'products[0].url': 'https://www.myecommercewebsite.com/product', + 'products[0].image_url': + 'https://www.myecommercewebsite.com/product/path.jpg', + 'products[1].product_id': '89ac6f5d5cf86a4b64eac145', + 'products[1].sku': '1267-01', + 'products[1].name': 'Wrestling Trump Cards', + 'products[1].price': 4, + 'products[1].position': 21, + 'products[1].category': 'Card Games', + category: 'dummy', + quantity: 10, + revenue: 100, + price: 50, + product_id: '12345', + order_id: '23456', + content_category: 'dummy', + content_ids: ['017c6f5d5cf86a4b22432066', '89ac6f5d5cf86a4b64eac145'], + content_type: 'product', + currency: 'USD', + value: 100, + contents: [ + { + id: '017c6f5d5cf86a4b22432066', + quantity: 10, + item_price: 22, + }, + { + id: '89ac6f5d5cf86a4b64eac145', + quantity: 10, + item_price: 4, + }, + ], + num_items: 2, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-12', + name: 'facebook_pixel', + description: + 'Track call : product list viewed event call with properties without product array and revenue as string', + scenario: 'ecommerce', + successCriteria: + 'Error : It should throw an error as revenue is not a number and should not be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...commonPropertiesWithoutProductArray, value: '$20' }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Revenue could not be converted to number', + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/processor/identifyTestData.ts b/test/integrations/destinations/facebook_pixel/processor/identifyTestData.ts new file mode 100644 index 0000000000..d315b03cea --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/identifyTestData.ts @@ -0,0 +1,209 @@ +import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { Destination } from '../../../../../src/types'; +import { generateMetadata, transformResultBuilder, overrideDestination } from '../../../testUtils'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: true, + whitelistPiiProperties: [ + { + whitelistPiiProperties: '', + }, + ], + }, + Enabled: true, +}; +const commonMessage = { + channel: 'web', + context: { + traits: { + name: 'Rudder Test', + email: 'abc@gmail.com', + firstname: 'Rudder', + lastname: 'Test', + phone: 9000000000, + gender: 'female', + }, + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + properties: { + plan: 'standard plan', + name: 'rudder test', + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + originalTimestamp: '2023-10-14T15:46:51.693229+05:30', + anonymousId: '00000000000000000000000000', + userId: '123456', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', +}; +const commonStatTags = { + errorCategory: 'dataValidation', + errorType: 'configuration', + destType: 'FACEBOOK_PIXEL', + module: 'destination', + implementation: 'native', + feature: 'processor', +}; + +export const identifyTestData: ProcessorTestData[] = [ + { + id: 'fbPixel-identify-test-1', + name: 'facebook_pixel', + description: '[Error]: Check if advancedMapping configuration is enabled', + scenario: 'Framework', + successCriteria: + 'Response should contain error message and status code should be 400, we are sending identify event with advancedMapping disabled', + module: 'destination', + feature: 'processor', + version: 'v0', + input: { + request: { + body: [ + { + message: commonMessage, + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { advancedMapping: false }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'For identify events, "Advanced Mapping" configuration must be enabled on the RudderStack dashboard', + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'fbPixel-identify-test-2', + name: 'facebook_pixel', + description: 'Identify event happy flow : without integrations object hashed true', + scenario: 'Business', + successCriteria: + ' Response should contain status code 200 and body should contain unhashed user traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: commonMessage, + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + ph: '593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579', + ge: '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + ln: '532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25', + fn: '2c2ccf28d806f6f9a34b67aa874d2113b7ac1444f1a4092541b8b75b84771747', + client_ip_address: '0.0.0.0', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + }, + event_name: 'identify', + event_time: 1697278611, + event_id: '84e26acc-56a5-4835-8233-591137fca468', + action_source: 'website', + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/processor/pageScreenTestData.ts b/test/integrations/destinations/facebook_pixel/processor/pageScreenTestData.ts new file mode 100644 index 0000000000..dee772522a --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/pageScreenTestData.ts @@ -0,0 +1,721 @@ +import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { + generateSimplifiedPageOrScreenPayload, + overrideDestination, + generateMetadata, + transformResultBuilder, +} from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + blacklistPiiProperties: [ + { + blacklistPiiProperties: 'phone', + blacklistPiiHash: true, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'email', + }, + ], + }, + Enabled: true, +}; +const commonMessage = { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + type: 'page', + messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', + timestamp: '2023-10-14T15:46:51.693229+05:30', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + integrations: { + All: true, + }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', +}; + +const commonPageMessage = { ...commonMessage, type: 'page' }; + +const commonScreenMessage = { ...commonMessage, type: 'screen' }; + +const commonStatTags = { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'FACEBOOK_PIXEL', + module: 'destination', + implementation: 'native', + feature: 'processor', +}; + +export const pageScreenTestData: ProcessorTestData[] = [ + { + id: 'facebook_pixel-page-test-1', + name: 'facebook_pixel', + description: + 'Page call : Happy flow without standard page switched on and with name and properties', + scenario: 'Page', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + email: 'abc@example.com', + }, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + name: 'ApplicationLoaded', + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'page', + ), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + em: '9eceb13483d7f187ec014fd6d4854d1420cfc634328af85f51d0323ba8622e21', + }, + event_name: 'Viewed page ApplicationLoaded', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-page-test-2', + name: 'facebook_pixel', + description: 'Page call : with standard page switched on and no properties and no name', + scenario: 'Page', + successCriteria: + 'Response should contain error message and status code should be 400, as we are not sending any other properties other than standard page properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + email: 'abc@example.com', + }, + }, + properties: {}, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'page', + ), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { standardPageCall: true }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + "After excluding opt_out,event_id,action_source, no fields are present in 'properties' for a standard event", + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-page-test-3', + name: 'facebook_pixel', + description: 'Page call : with standard page switched on and properties', + scenario: 'Page', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + email: 'abc@example.com', + }, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'page', + ), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { standardPageCall: true }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + em: '9eceb13483d7f187ec014fd6d4854d1420cfc634328af85f51d0323ba8622e21', + }, + event_name: 'PageView', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-page-test-4', + name: 'facebook_pixel', + description: 'Page call : with standard page switched off and with properties but no page name', + scenario: 'Page', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: {}, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'page', + ), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + }, + event_name: 'PageView', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-screen-test-1', + name: 'facebook_pixel', + description: + 'Screen call : Happy flow without standard page switched on and with name and properties', + scenario: 'Screen', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: {}, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + anonymousId: '9c6bd77ea9da3e68', + name: 'ApplicationLoaded', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'screen', + ), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + }, + event_name: 'PageView', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-screen-test-2', + name: 'facebook_pixel', + description: 'Screen call : with standard page switched on and no properties and no name', + scenario: 'Screen', + successCriteria: + 'Response should contain error message and status code should be 400, as we are not sending any other properties other than standard page properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: {}, + }, + properties: {}, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'screen', + ), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { standardPageCall: true }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + "After excluding opt_out,event_id,action_source, no fields are present in 'properties' for a standard event", + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-screen-test-3', + name: 'facebook_pixel', + description: 'Screen call : with standard page switched on and properties', + scenario: 'Screen', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: {}, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'screen', + ), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { standardPageCall: true }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + }, + event_name: 'PageView', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-screen-test-4', + name: 'facebook_pixel', + description: + 'Screen call : with standard page switched off and with properties but no page name', + scenario: 'Screen', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: {}, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'screen', + ), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + }, + event_name: 'PageView', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/processor/trackTestData.ts b/test/integrations/destinations/facebook_pixel/processor/trackTestData.ts new file mode 100644 index 0000000000..9fd65945c4 --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/trackTestData.ts @@ -0,0 +1,208 @@ +import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { generateMetadata, generateTrackPayload, transformResultBuilder } from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'email', + }, + ], + }, + Enabled: true, +}; + +const commonUserTraits = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', +}; + +const commonUserProperties = { + revenue: 400, + additional_bet_index: 0, + email: 'abc@gmail.com', +}; + +const commonTimestamp = new Date('2023-10-14'); + +export const trackTestData: ProcessorTestData[] = [ + { + id: 'fbPixel-track-test-1', + name: 'facebook_pixel', + description: 'Track call : custom event calls with simple user properties and traits', + scenario: 'Business', + successCriteria: + 'event not respecting the internal mapping and as well as UI mapping should be considered as a custom event and should be sent as it is', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'spin_result', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + additional_bet_index: 0, + email: 'abc@gmail.com', + value: 400, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'fbPixel-track-test-2', + name: 'facebook_pixel', + description: + 'Track call : other standard type event calls with simple user properties and traits', + scenario: 'Business', + successCriteria: + 'event not respecting the internal mapping and as well as UI mapping but falls under other standard events, should be considered as a simple track event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'AddToWishlist', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'AddToWishlist', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + additional_bet_index: 0, + email: 'abc@gmail.com', + value: 400, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/processor/validationTestData.ts b/test/integrations/destinations/facebook_pixel/processor/validationTestData.ts new file mode 100644 index 0000000000..8e24801464 --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/validationTestData.ts @@ -0,0 +1,373 @@ +import { Destination } from '../../../../../src/types'; +import { + generateMetadata, + generateSimplifiedGroupPayload, + generateSimplifiedTrackPayload, + generateTrackPayload, + overrideDestination, +} from '../../../testUtils'; +const commonTimestamp = new Date('2023-10-12'); +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + limitedDataUsage: true, + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + removeExternalId: true, + valueFieldIdentifier: '', + advancedMapping: true, + whitelistPiiProperties: [ + { + whitelistPiiProperties: '', + }, + ], + }, + Enabled: true, +}; + +const commonStatTags = { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'FACEBOOK_PIXEL', + module: 'destination', + implementation: 'native', + feature: 'processor', +}; + +export const validationTestData = [ + { + id: 'fbPixel-validation-test-1', + name: 'facebook_pixel', + description: '[Error]: Check for unsupported message type', + scenario: 'Framework', + successCriteria: + 'Response should contain error message and status code should be 400, as we are sending a message type which is not supported by facebook pixel destination and the error message should be Event type random is not supported', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(commonDestination, { + accessToken: '09876', + pixelId: 'dummyPixelId', + }), + message: generateSimplifiedGroupPayload({ + userId: 'user123', + groupId: 'XUepkK', + traits: { + subscribe: true, + }, + context: { + traits: { + email: 'test@rudderstack.com', + phone: '+12 345 678 900', + consent: ['email'], + }, + }, + timestamp: '2023-10-14T00:21:34.208Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Message type group not supported', + statTags: commonStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-2', + name: 'facebook_pixel', + description: + 'Track call : error in instrumentation as pixel id is not mentioned in destination object', + scenario: 'Business', + successCriteria: + 'Error: Pixel Id not found. Aborting, as we are sending an event without pixel id and the status code should be 400', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: { + revenue: 400, + additional_bet_index: 0, + }, + context: { + traits: { + email: 'abc@gmail.com', + }, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Pixel Id not found. Aborting', + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + errorType: 'configuration', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-3', + name: 'facebook_pixel', + description: 'Track call : custom event calls with simple user properties and traits', + scenario: 'Business', + successCriteria: + 'event not respecting the internal mapping and as well as UI mapping should be considered as a custom event and should be sent as it is', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: { + revenue: 400, + additional_bet_index: 0, + }, + context: { + traits: { + email: 'abc@gmail.com', + }, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + pixelId: 'dummyPixelId', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Access token not found. Aborting', + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + errorType: 'configuration', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-3', + name: 'facebook_pixel', + description: '[Error]: validate event date and time', + scenario: 'Framework + business', + successCriteria: + 'Response should contain error message and status code should be 400, as we are sending an event which is older than 7 days and the error message should be Events must be sent within seven days of their occurrence or up to one minute in the future.', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + email: 'test@rudderstack.com', + phone: '9112340375', + plan_details: { + plan_type: 'gold', + duration: '3 months', + }, + }, + }, + properties: { + revenue: 400, + additional_bet_index: 0, + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }), + destination: overrideDestination(commonDestination, { + accessToken: '09876', + pixelId: 'dummyPixelId', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'Events must be sent within seven days of their occurrence or up to one minute in the future.', + statTags: commonStatTags, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-4', + name: 'facebook_pixel', + description: + 'Track call : error in instrumentation as event name is not mentioned in track call', + scenario: 'Business', + successCriteria: + 'event not respecting the internal mapping and as well as UI mapping should be considered as a custom event and should be sent as it is', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + properties: { + revenue: 400, + additional_bet_index: 0, + }, + }, + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + pixelId: 'dummyPixelId', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: "'event' is required", + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-4', + name: 'facebook_pixel', + description: 'Track call : error in instrumentation as event name is not a string', + scenario: 'Business', + successCriteria: + 'Error message should be event name should be string and status code should be 400, as we are sending an event which is not a string', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 1234, + properties: { + revenue: 400, + additional_bet_index: 0, + }, + }, + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + pixelId: 'dummyPixelId', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'event name should be string', + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/testUtils.ts b/test/integrations/testUtils.ts index 7aede97cf7..13a76702f9 100644 --- a/test/integrations/testUtils.ts +++ b/test/integrations/testUtils.ts @@ -216,6 +216,7 @@ export const generateTrackPayload: any = (parametersOverride: any) => { campaign: {}, userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + dataProcessingOptions: parametersOverride.context.dataProcessingOptions, }), rudderId: parametersOverride.rudderId || generateAlphanumericId(36), messageId: parametersOverride.messageId || generateAlphanumericId(36), @@ -310,6 +311,7 @@ export const generateSimplifiedPageOrScreenPayload: any = ( userId: parametersOverride.userId || 'default-userId', type: eventType || 'page', event: parametersOverride.event, + name: parametersOverride.name, properties: parametersOverride.properties, integrations: parametersOverride.integrations, rudderId: parametersOverride.rudderId || generateAlphanumericId(36), From a31d93cb239b3a44b9b4ff2a9d32bd5387ff7424 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Thu, 14 Mar 2024 13:17:34 +0530 Subject: [PATCH 23/35] chore: move pardot to new component structure (#3171) * chore: move pardot to new component structure --- src/v0/destinations/pardot/networkHandler.js | 13 + .../pardot/dataDelivery/business.ts | 372 ++++++++++++++++++ .../pardot/dataDelivery/constant.ts | 27 ++ .../destinations/pardot/dataDelivery/data.ts | 10 + .../destinations/pardot/dataDelivery/oauth.ts | 53 +++ .../destinations/pardot/dataDelivery/other.ts | 205 ++++++++++ .../destinations/pardot/network.ts | 143 +++---- 7 files changed, 736 insertions(+), 87 deletions(-) create mode 100644 test/integrations/destinations/pardot/dataDelivery/business.ts create mode 100644 test/integrations/destinations/pardot/dataDelivery/constant.ts create mode 100644 test/integrations/destinations/pardot/dataDelivery/data.ts create mode 100644 test/integrations/destinations/pardot/dataDelivery/oauth.ts create mode 100644 test/integrations/destinations/pardot/dataDelivery/other.ts diff --git a/src/v0/destinations/pardot/networkHandler.js b/src/v0/destinations/pardot/networkHandler.js index edf713ce97..60d2f7ee23 100644 --- a/src/v0/destinations/pardot/networkHandler.js +++ b/src/v0/destinations/pardot/networkHandler.js @@ -46,6 +46,19 @@ const getStatus = (code) => { const pardotRespHandler = (destResponse, stageMsg) => { const { status, response } = destResponse; const respAttributes = response['@attributes']; + + // to handle errors like service unavilable, wrong url, no response + if (!respAttributes) { + throw new NetworkError( + `${JSON.stringify(response)} ${stageMsg}`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + response, + ); + } + const { stat, err_code: errorCode } = respAttributes; if (isHttpStatusSuccess(status) && stat !== 'fail') { diff --git a/test/integrations/destinations/pardot/dataDelivery/business.ts b/test/integrations/destinations/pardot/dataDelivery/business.ts new file mode 100644 index 0000000000..f6baefbc8f --- /dev/null +++ b/test/integrations/destinations/pardot/dataDelivery/business.ts @@ -0,0 +1,372 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; +import { commonRequestParameters, retryStatTags } from './constant'; + +const pardotResponseRogerEmail = { + '@attributes': { stat: 'ok', version: 1 }, + prospect: { + id: 123435, + campaign_id: 42213, + salutation: null, + first_name: 'Roger_12', + last_name: 'Federer_12', + email: 'Roger_12@federer.io', + password: null, + company: null, + website: 'https://rudderstack.com', + job_title: null, + department: null, + country: 'AU', + address_one: null, + address_two: null, + city: null, + state: null, + territory: null, + zip: null, + phone: null, + fax: null, + source: null, + annual_revenue: null, + employees: null, + industry: null, + years_in_business: null, + comments: null, + notes: null, + score: 14, + grade: null, + last_activity_at: null, + recent_interaction: 'Never active.', + crm_lead_fid: '00Q6r000002LKhTPVR', + crm_contact_fid: null, + crm_owner_fid: '00G2v000004WYXaEAO', + crm_account_fid: null, + salesforce_fid: '00Q6r000002LKhTPVR', + crm_last_sync: '2022-01-21 18:47:37', + crm_url: 'https://testcompany.my.salesforce.com/00Q6r000002LKhTPVR', + is_do_not_email: null, + is_do_not_call: null, + opted_out: null, + is_reviewed: 1, + is_starred: null, + created_at: '2022-01-21 18:21:46', + updated_at: '2022-01-21 18:48:41', + campaign: { id: 42113, name: 'Test', crm_fid: '7012y000000MNOCLL4' }, + assigned_to: { + user: { + id: 38443703, + email: 'test_rudderstack@testcompany.com', + first_name: 'Rudderstack', + last_name: 'User', + job_title: null, + role: 'Administrator', + account: 489853, + created_at: '2021-02-26 06:25:17', + updated_at: '2021-02-26 06:25:17', + }, + }, + Are_you_shipping_large_fragile_or_bulky_items: false, + Calendly: false, + Country_Code: 'AU', + Currency: 'AUD', + Inventory_or_Warehouse_Management_System: false, + Lead_Status: 'New', + Marketing_Stage: 'SAL', + Record_Type_ID: 'TestCompany Lead', + profile: { + id: 304, + name: 'Default', + profile_criteria: [ + { id: 1500, name: 'Shipping Volume', matches: 'Unknown' }, + { id: 1502, name: 'Industry', matches: 'Unknown' }, + { id: 1506, name: 'Job Title', matches: 'Unknown' }, + { id: 1508, name: 'Department', matches: 'Unknown' }, + ], + }, + visitors: null, + visitor_activities: null, + lists: null, + }, +}; + +const pardotResponseWalterEmail = { + '@attributes': { stat: 'ok', version: 1 }, + prospect: { + id: 123435, + campaign_id: 42213, + salutation: null, + first_name: 'Roger_12', + last_name: 'Federer_12', + email: 'Roger_12@waltair.io', + password: null, + company: null, + website: 'https://rudderstack.com', + job_title: null, + department: null, + country: 'AU', + address_one: null, + address_two: null, + city: null, + state: null, + territory: null, + zip: null, + phone: null, + fax: null, + source: null, + annual_revenue: null, + employees: null, + industry: null, + years_in_business: null, + comments: null, + notes: null, + score: 14, + grade: null, + last_activity_at: null, + recent_interaction: 'Never active.', + crm_lead_fid: null, + crm_contact_fid: null, + crm_owner_fid: '00G2v000004WYXaEAO', + crm_account_fid: null, + salesforce_fid: null, + crm_last_sync: null, + crm_url: null, + is_do_not_email: null, + is_do_not_call: null, + opted_out: null, + is_reviewed: 1, + is_starred: null, + created_at: '2022-01-21 18:21:46', + updated_at: '2022-01-21 18:48:41', + campaign: { id: 42113, name: 'Test', crm_fid: '7012y000000MNOCLL4' }, + assigned_to: { + user: { + id: 38443703, + email: 'test_rudderstack@testcompany.com', + first_name: 'Rudderstack', + last_name: 'User', + job_title: null, + role: 'Administrator', + account: 489853, + created_at: '2021-02-26 06:25:17', + updated_at: '2021-02-26 06:25:17', + }, + }, + Are_you_shipping_large_fragile_or_bulky_items: false, + Calendly: false, + Country_Code: 'AU', + Currency: 'AUD', + Inventory_or_Warehouse_Management_System: false, + Lead_Status: 'New', + Marketing_Stage: 'SAL', + Record_Type_ID: 'TestCompany Lead', + profile: { + id: 304, + name: 'Default', + profile_criteria: [ + { id: 1500, name: 'Shipping Volume', matches: 'Unknown' }, + { id: 1502, name: 'Industry', matches: 'Unknown' }, + { id: 1506, name: 'Job Title', matches: 'Unknown' }, + { id: 1508, name: 'Department', matches: 'Unknown' }, + ], + }, + visitors: null, + visitor_activities: null, + lists: null, + }, +}; + +export const businessV0TestScenarios = [ + { + id: 'pardot_v0_bussiness_scenario_1', + name: 'pardot', + description: '[Proxy v0 API] :: pardot email upsert', + successCriteria: 'Proper response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: + 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/Roger_12@waltair.io', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 201, + message: 'Request Processed Successfully', + destinationResponse: { + response: pardotResponseWalterEmail, + status: 201, + }, + }, + }, + }, + }, + }, + { + id: 'pardot_v0_bussiness_scenario_2', + name: 'pardot', + description: '[Proxy v0 API] :: pardot fid type upsert', + successCriteria: 'Proper response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/fid/00Q6r000002LKhTPVR', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + destinationResponse: { + response: pardotResponseRogerEmail, + status: 200, + }, + }, + }, + }, + }, + }, +]; + +export const businessV1TestScenarios: ProxyV1TestData[] = [ + { + id: 'pardot_v1_bussiness_scenario_1', + name: 'pardot', + description: '[Proxy v1 API] :: pardot email type upsert', + successCriteria: 'Proper response from destination is received', + scenario: 'business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: + 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/Roger_12@waltair.io', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 201, + message: 'Request Processed Successfully', + response: [ + { + statusCode: 201, + metadata: generateMetadata(1), + error: JSON.stringify(pardotResponseWalterEmail), + }, + ], + }, + }, + }, + }, + }, + { + id: 'pardot_v1_bussiness_scenario_2', + name: 'pardot', + description: '[Proxy v1 API] :: pardot fid type upsert', + successCriteria: 'Proper response from destination is received', + scenario: 'business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/fid/00Q6r000002LKhTPVR', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: JSON.stringify(pardotResponseRogerEmail), + }, + ], + }, + }, + }, + }, + }, + { + id: 'pardot_v1_Business_scenario_2', + name: 'pardot', + description: '[Proxy v1 API] :: Response with other retryable codes', + successCriteria: 'the proxy should return 500 with retryable tag', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: + 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/rolex_waltair@test.com', + params: { + destination: 'pardot', + }, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + 'Unable to verify Salesforce connector during Pardot response transformation', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: retryStatTags, + message: 'Unable to verify Salesforce connector during Pardot response transformation', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/pardot/dataDelivery/constant.ts b/test/integrations/destinations/pardot/dataDelivery/constant.ts new file mode 100644 index 0000000000..97e456998e --- /dev/null +++ b/test/integrations/destinations/pardot/dataDelivery/constant.ts @@ -0,0 +1,27 @@ +export const retryStatTags = { + destType: 'PARDOT', + errorCategory: 'network', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', +}; +const commonHeaders = { + Authorization: 'Bearer myToken', + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + 'Pardot-Business-Unit-Id': '0Uv2v000000k9tHCAQ', + 'User-Agent': 'RudderLabs', +}; + +export const commonRequestParameters = { + headers: commonHeaders, + FORM: { + first_name: 'Roger12', + last_name: 'Federer12', + website: 'https://rudderstack.com', + score: 14, + campaign_id: 42213, + }, +}; diff --git a/test/integrations/destinations/pardot/dataDelivery/data.ts b/test/integrations/destinations/pardot/dataDelivery/data.ts new file mode 100644 index 0000000000..752ef22cb1 --- /dev/null +++ b/test/integrations/destinations/pardot/dataDelivery/data.ts @@ -0,0 +1,10 @@ +import { businessV0TestScenarios, businessV1TestScenarios } from './business'; +import { v1OauthScenarios } from './oauth'; +import { otherScenariosV1 } from './other'; + +export const data = [ + ...v1OauthScenarios, + ...businessV1TestScenarios, + ...businessV0TestScenarios, + ...otherScenariosV1, +]; diff --git a/test/integrations/destinations/pardot/dataDelivery/oauth.ts b/test/integrations/destinations/pardot/dataDelivery/oauth.ts new file mode 100644 index 0000000000..4a0116a951 --- /dev/null +++ b/test/integrations/destinations/pardot/dataDelivery/oauth.ts @@ -0,0 +1,53 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; +import { commonRequestParameters, retryStatTags } from './constant'; + +export const v1OauthScenarios: ProxyV1TestData[] = [ + { + id: 'pardot_v1_oauth_scenario_1', + name: 'pardot', + description: + '[Proxy v1 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: + 'Since the error from the destination is 401 - the proxy should return 500 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: + 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/rolex_waltair@mywebsite.io', + params: { + destination: 'pardot', + }, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + response: [ + { + error: + 'access_token is invalid, unknown, or malformed: Inactive token during Pardot response transformation', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: retryStatTags, + authErrorCategory: 'REFRESH_TOKEN', + message: + 'access_token is invalid, unknown, or malformed: Inactive token during Pardot response transformation', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/pardot/dataDelivery/other.ts b/test/integrations/destinations/pardot/dataDelivery/other.ts new file mode 100644 index 0000000000..b7454e691c --- /dev/null +++ b/test/integrations/destinations/pardot/dataDelivery/other.ts @@ -0,0 +1,205 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; + +const expectedStatTags = { + destType: 'PARDOT', + errorCategory: 'network', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', +}; + +export const otherScenariosV1: ProxyV1TestData[] = [ + { + id: 'pardot_v1_other_scenario_1', + name: 'pardot', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}} during Pardot response transformation', + statusCode: 503, + metadata: generateMetadata(1), + }, + ], + statTags: expectedStatTags, + status: 503, + message: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}} during Pardot response transformation', + }, + }, + }, + }, + }, + { + id: 'pardot_v1_other_scenario_2', + name: 'pardot', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error" during Pardot response transformation', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: expectedStatTags, + status: 500, + message: '"Internal Server Error" during Pardot response transformation', + }, + }, + }, + }, + }, + { + id: 'pardot_v1_other_scenario_3', + name: 'pardot', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout" during Pardot response transformation', + statusCode: 504, + metadata: generateMetadata(1), + }, + ], + statTags: expectedStatTags, + status: 504, + message: '"Gateway Timeout" during Pardot response transformation', + }, + }, + }, + }, + }, + { + id: 'pardot_v1_other_scenario_4', + name: 'pardot', + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_response', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"" during Pardot response transformation', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: expectedStatTags, + status: 500, + message: '"" during Pardot response transformation', + }, + }, + }, + }, + }, + { + id: 'pardot_v1_other_scenario_5', + name: 'pardot', + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"" during Pardot response transformation', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: expectedStatTags, + status: 500, + message: '"" during Pardot response transformation', + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/pardot/network.ts b/test/integrations/destinations/pardot/network.ts index bbbe0d70f9..9493aab01f 100644 --- a/test/integrations/destinations/pardot/network.ts +++ b/test/integrations/destinations/pardot/network.ts @@ -1,25 +1,9 @@ -import { enhanceRequestOptions, getFormData } from '../../../../src/adapters/network'; +import { getFormData } from '../../../../src/adapters/network'; export const networkCallsData = [ - // 2nd proxy test-case { httpReq: { url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/Roger_12@waltair.io', - data: getFormData({ - first_name: 'Roger_12', - last_name: 'Federer_12', - website: 'https://rudderstack.com', - score: 14, - campaign_id: 42213, - format: 'json', - }).toString(), - params: { destination: 'pardot' }, - headers: { - Authorization: 'Bearer myToken', - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - 'Pardot-Business-Unit-Id': '0Uv2v000000k9tHCAQ', - 'User-Agent': 'RudderLabs', - }, method: 'POST', }, httpRes: { @@ -135,59 +119,9 @@ export const networkCallsData = [ statusText: 'Created', }, }, - // 4th proxy test-case - { - httpReq: { - url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/rolex_waltair@mywebsite.io', - data: getFormData({ - first_name: 'Rolex', - last_name: 'Waltair', - website: 'https://rudderstack.com', - score: 15, - campaign_id: 42213, - format: 'json', - }).toString(), - params: { destination: 'pardot' }, - headers: { - Authorization: 'Bearer myExpiredToken', - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - 'Pardot-Business-Unit-Id': '0Uv2v000000k9tHCAQ', - 'User-Agent': 'RudderLabs', - }, - method: 'POST', - }, - httpRes: { - data: { - '@attributes': { - stat: 'fail', - version: 1, - err_code: 184, - }, - err: 'access_token is invalid, unknown, or malformed: Inactive token', - }, - status: 401, - statusText: 'Unauthorized', - }, - }, - // 1st proxy test-case { httpReq: { - url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/id/123435', - data: getFormData({ - first_name: 'Roger12', - last_name: 'Federer12', - website: 'https://rudderstack.com', - score: 14, - campaign_id: 42213, - format: 'json', - }).toString(), - params: { destination: 'pardot' }, - headers: { - Authorization: 'Bearer myToken', - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - 'Pardot-Business-Unit-Id': '0Uv2v000000k9tHCAQ', - 'User-Agent': 'RudderLabs', - }, + url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/fid/00Q6r000002LKhTPVR', method: 'POST', }, httpRes: { @@ -200,9 +134,9 @@ export const networkCallsData = [ id: 123435, campaign_id: 42213, salutation: null, - first_name: 'Roger12', - last_name: 'Federer12', - email: 'Roger12@waltair.io', + first_name: 'Roger_12', + last_name: 'Federer_12', + email: 'Roger_12@federer.io', password: null, company: null, website: 'https://rudderstack.com', @@ -228,13 +162,13 @@ export const networkCallsData = [ grade: null, last_activity_at: null, recent_interaction: 'Never active.', - crm_lead_fid: null, + crm_lead_fid: '00Q6r000002LKhTPVR', crm_contact_fid: null, crm_owner_fid: '00G2v000004WYXaEAO', crm_account_fid: null, - salesforce_fid: null, - crm_last_sync: null, - crm_url: null, + salesforce_fid: '00Q6r000002LKhTPVR', + crm_last_sync: '2022-01-21 18:47:37', + crm_url: 'https://testcompany.my.salesforce.com/00Q6r000002LKhTPVR', is_do_not_email: null, is_do_not_call: null, opted_out: null, @@ -303,15 +237,32 @@ export const networkCallsData = [ statusText: 'OK', }, }, - // 3rd proxy test-case { httpReq: { - url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/fid/00Q6r000002LKhTPVR', + url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/rolex_waltair@mywebsite.io', + method: 'POST', + }, + httpRes: { + data: { + '@attributes': { + stat: 'fail', + version: 1, + err_code: 184, + }, + err: 'access_token is invalid, unknown, or malformed: Inactive token', + }, + status: 401, + statusText: 'Unauthorized', + }, + }, + { + httpReq: { + url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/id/123435', data: getFormData({ - first_name: 'Nick', - last_name: 'Kyrgios', + first_name: 'Roger12', + last_name: 'Federer12', website: 'https://rudderstack.com', - score: 12, + score: 14, campaign_id: 42213, format: 'json', }).toString(), @@ -334,9 +285,9 @@ export const networkCallsData = [ id: 123435, campaign_id: 42213, salutation: null, - first_name: 'Roger_12', - last_name: 'Federer_12', - email: 'Roger_12@federer.io', + first_name: 'Roger12', + last_name: 'Federer12', + email: 'Roger12@waltair.io', password: null, company: null, website: 'https://rudderstack.com', @@ -362,13 +313,13 @@ export const networkCallsData = [ grade: null, last_activity_at: null, recent_interaction: 'Never active.', - crm_lead_fid: '00Q6r000002LKhTPVR', + crm_lead_fid: null, crm_contact_fid: null, crm_owner_fid: '00G2v000004WYXaEAO', crm_account_fid: null, - salesforce_fid: '00Q6r000002LKhTPVR', - crm_last_sync: '2022-01-21 18:47:37', - crm_url: 'https://testcompany.my.salesforce.com/00Q6r000002LKhTPVR', + salesforce_fid: null, + crm_last_sync: null, + crm_url: null, is_do_not_email: null, is_do_not_call: null, opted_out: null, @@ -437,4 +388,22 @@ export const networkCallsData = [ statusText: 'OK', }, }, + { + httpReq: { + url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/rolex_waltair@test.com', + method: 'POST', + }, + httpRes: { + data: { + '@attributes': { + stat: 'fail', + version: 1, + err_code: 120, + }, + err: 'Unable to verify Salesforce connector', + }, + status: 500, + statusText: 'Invalid action', + }, + }, ]; From 6330888ad5c67e3a800037b56501fc08da09e4d1 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Thu, 14 Mar 2024 14:25:26 +0530 Subject: [PATCH 24/35] fix: fixed 500 status for algolia dontBatch (#3178) * fix: fixed 500 for algolia dontBatch --- src/v1/destinations/algolia/networkHandler.js | 31 +++++-------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/src/v1/destinations/algolia/networkHandler.js b/src/v1/destinations/algolia/networkHandler.js index d953251050..21f415197f 100644 --- a/src/v1/destinations/algolia/networkHandler.js +++ b/src/v1/destinations/algolia/networkHandler.js @@ -13,12 +13,6 @@ const responseHandler = (responseParams) => { const { destinationResponse, rudderJobMetadata } = responseParams; const message = `[ALGOLIA Response V1 Handler] - Request Processed Successfully`; const responseWithIndividualEvents = []; - // response: - // {status: 200, message: 'OK'} - // {response:'[ENOTFOUND] :: DNS lookup failed', status: 400} - // destinationResponse = { - // response: {"status": 422, "message": "EventType must be one of \"click\", \"conversion\" or \"view\""}, status: 422 - // } const { response, status } = destinationResponse; if (isHttpStatusSuccess(status)) { @@ -41,30 +35,19 @@ const responseHandler = (responseParams) => { // in case of non 2xx status sending 500 for every event, populate response and update dontBatch to true const errorMessage = response?.error?.message || response?.message || 'unknown error format'; - let serverStatus = 400; for (const metadata of rudderJobMetadata) { - // handling case if dontBatch is true, and again we got invalid from destination - if (metadata.dontBatch && status === 422) { - responseWithIndividualEvents.push({ - statusCode: 400, - metadata, - error: errorMessage, - }); - } else { - serverStatus = 500; - metadata.dontBatch = true; - responseWithIndividualEvents.push({ - statusCode: 500, - metadata, - error: errorMessage, - }); - } + metadata.dontBatch = true; + responseWithIndividualEvents.push({ + statusCode: 500, + metadata, + error: errorMessage, + }); } // sending back 500 for retry throw new TransformerProxyError( `ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation`, - serverStatus, + 500, { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), }, From 4d9bddeaef0cb90c074e9f7e017c8394bcfd6d36 Mon Sep 17 00:00:00 2001 From: Sandeep Digumarty Date: Fri, 15 Mar 2024 12:53:48 +0530 Subject: [PATCH 25/35] chore: resolve sql injection vulnerabilities (#3172) --- package-lock.json | 9 +++++++++ package.json | 1 + .../networkHandler.js | 7 ++++++- .../google_adwords_offline_conversions/utils.js | 7 ++++++- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 700207e021..d708860537 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,6 +63,7 @@ "rudder-transformer-cdk": "^1.4.11", "set-value": "^4.1.0", "sha256": "^0.2.0", + "sqlstring": "^2.3.3", "stacktrace-parser": "^0.1.10", "statsd-client": "^0.4.7", "truncate-utf8-bytes": "^1.0.2", @@ -19087,6 +19088,14 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/stack-generator": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", diff --git a/package.json b/package.json index 070510029b..ec3ffbf4e6 100644 --- a/package.json +++ b/package.json @@ -108,6 +108,7 @@ "rudder-transformer-cdk": "^1.4.11", "set-value": "^4.1.0", "sha256": "^0.2.0", + "sqlstring": "^2.3.3", "stacktrace-parser": "^0.1.10", "statsd-client": "^0.4.7", "truncate-utf8-bytes": "^1.0.2", diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js index 3ea985e773..feedcf8975 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js +++ b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js @@ -1,6 +1,7 @@ const { get, set } = require('lodash'); const sha256 = require('sha256'); const { NetworkError, NetworkInstrumentationError } = require('@rudderstack/integrations-lib'); +const SqlString = require('sqlstring'); const { prepareProxyRequest, handleHttpRequest } = require('../../../adapters/network'); const { isHttpStatusSuccess, getAuthErrCategoryFromStCode } = require('../../util/index'); const { CONVERSION_ACTION_ID_CACHE_TTL } = require('./config'); @@ -29,8 +30,12 @@ const ERROR_MSG_PATH = 'response[0].error.message'; const getConversionActionId = async (method, headers, params) => { const conversionActionIdKey = sha256(params.event + params.customerId).toString(); return conversionActionIdCache.get(conversionActionIdKey, async () => { + const queryString = SqlString.format( + 'SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = ?', + [params.event], + ); const data = { - query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = '${params.event}'`, + query: queryString, }; const requestBody = { url: `${BASE_ENDPOINT}/${params.customerId}/googleAds:searchStream`, diff --git a/src/v0/destinations/google_adwords_offline_conversions/utils.js b/src/v0/destinations/google_adwords_offline_conversions/utils.js index ee677373a3..67c0ef31c8 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/utils.js +++ b/src/v0/destinations/google_adwords_offline_conversions/utils.js @@ -1,4 +1,5 @@ const sha256 = require('sha256'); +const SqlString = require('sqlstring'); const { get, set, cloneDeep } = require('lodash'); const { AbortedError, @@ -53,8 +54,12 @@ const validateDestinationConfig = ({ Config }) => { const getConversionActionId = async (headers, params) => { const conversionActionIdKey = sha256(params.event + params.customerId).toString(); return conversionActionIdCache.get(conversionActionIdKey, async () => { + const queryString = SqlString.format( + 'SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = ?', + [params.event], + ); const data = { - query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = '${params.event}'`, + query: queryString, }; const endpoint = SEARCH_STREAM.replace(':customerId', params.customerId); const requestOptions = { From f51e6b9d2b7324db14ff221ed70efa6484c1f496 Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:27:07 +0530 Subject: [PATCH 26/35] chore: gaec proxy test refactor (#3177) * chore: gaec proxy test refactor * chore: code review changes * chore: code review changes * chore: code review changes --- .../networkHandler.js | 2 +- .../dataDelivery/business.ts | 284 ++++++++++++++++ .../dataDelivery/data.ts | 312 +----------------- .../dataDelivery/oauth.ts | 276 ++++++++++++++++ .../network.ts | 142 ++++++++ 5 files changed, 710 insertions(+), 306 deletions(-) create mode 100644 test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/business.ts create mode 100644 test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/oauth.ts diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js index feedcf8975..f7ac660f53 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js +++ b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js @@ -122,7 +122,7 @@ const responseHandler = (responseParams) => { // Ref - https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto if (partialFailureError && partialFailureError.code !== 0) { throw new NetworkError( - `[Google Ads Offline Conversions]:: partialFailureError - ${JSON.stringify( + `[Google Adwords Enhanced Conversions]:: partialFailureError - ${JSON.stringify( partialFailureError, )}`, 400, diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/business.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/business.ts new file mode 100644 index 0000000000..0cee1418e1 --- /dev/null +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/business.ts @@ -0,0 +1,284 @@ +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; +import { ProxyV1TestData } from '../../../testTypes'; + +const headers = { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '0987654321', +}; + +const params = { + event: 'Product Added', + customerId: '1234567899', + destination: 'google_adwords_enhanced_conversions', +}; + +const validRequestPaylod = { + partialFailure: true, + conversionAdjustments: [ + { + gclidDateTimePair: { + gclid: 'gclid1234', + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + restatementValue: { + adjustedValue: 10, + currency: 'INR', + }, + order_id: '10000', + adjustmentDateTime: '2022-01-01 12:32:45-08:00', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + userIdentifiers: [ + { + addressInfo: { + hashedFirstName: 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', + hashedLastName: '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', + state: 'UK', + city: 'London', + hashedStreetAddress: '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', + }, + }, + ], + adjustmentType: 'ENHANCEMENT', + }, + ], +}; + +const commonRequestParameters = { + headers, + params, + JSON: validRequestPaylod, +}; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_ENHANCED_CONVERSIONS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const testScenariosForV0API = [ + { + id: 'gaec_v0_scenario_1', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v0 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567899:uploadConversionAdjustments', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + destinationResponse: { + response: [ + { + results: [ + { + adjustmentDateTime: '2021-01-01 12:32:45-08:00', + adjustmentType: 'ENHANCEMENT', + conversionAction: 'customers/7693729833/conversionActions/874224905', + gclidDateTimePair: { + conversionDateTime: '2021-01-01 12:32:45-08:00', + gclid: '1234', + }, + orderId: '12345', + }, + ], + }, + ], + status: 200, + }, + message: 'Request Processed Successfully', + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaec_v0_scenario_2', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v0 API] :: Test for a partial failure request with a 200 response from the destination', + successCriteria: 'Should return 400 with partial failure error', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + params: { + event: 'Product Added', + customerId: '1234567888', + destination: 'google_adwords_enhanced_conversions', + }, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567888:uploadConversionAdjustments', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + destinationResponse: { + code: 3, + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v15.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + conversionAdjustmentUploadError: 'CONVERSION_ALREADY_ENHANCED', + }, + location: { + fieldPathElements: [ + { + fieldName: 'conversion_adjustments', + index: 0, + }, + ], + }, + message: + 'Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again.', + }, + ], + }, + ], + message: + 'Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again., at conversion_adjustments[0]', + }, + message: + '[Google Adwords Enhanced Conversions]:: partialFailureError - {"code":3,"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again., at conversion_adjustments[0]","details":[{"@type":"type.googleapis.com/google.ads.googleads.v15.errors.GoogleAdsFailure","errors":[{"errorCode":{"conversionAdjustmentUploadError":"CONVERSION_ALREADY_ENHANCED"},"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again.","location":{"fieldPathElements":[{"fieldName":"conversion_adjustments","index":0}]}}]}]}', + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'gaec_v1_scenario_1', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v1 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567899:uploadConversionAdjustments', + }, + [generateMetadata(1)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '[{"results":[{"adjustmentType":"ENHANCEMENT","conversionAction":"customers/7693729833/conversionActions/874224905","adjustmentDateTime":"2021-01-01 12:32:45-08:00","gclidDateTimePair":{"gclid":"1234","conversionDateTime":"2021-01-01 12:32:45-08:00"},"orderId":"12345"}]}]', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaec_v1_scenario_2', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v1 API] :: Test for a partial failure request with a 200 response from the destination', + successCriteria: 'Should return 400 with partial failure error', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + params: { + event: 'Product Added', + customerId: '1234567888', + destination: 'google_adwords_enhanced_conversions', + }, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567888:uploadConversionAdjustments', + }, + [generateMetadata(1)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Adwords Enhanced Conversions]:: partialFailureError - {"code":3,"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again., at conversion_adjustments[0]","details":[{"@type":"type.googleapis.com/google.ads.googleads.v15.errors.GoogleAdsFailure","errors":[{"errorCode":{"conversionAdjustmentUploadError":"CONVERSION_ALREADY_ENHANCED"},"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again.","location":{"fieldPathElements":[{"fieldName":"conversion_adjustments","index":0}]}}]}]}', + response: [ + { + error: + '[Google Adwords Enhanced Conversions]:: partialFailureError - {"code":3,"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again., at conversion_adjustments[0]","details":[{"@type":"type.googleapis.com/google.ads.googleads.v15.errors.GoogleAdsFailure","errors":[{"errorCode":{"conversionAdjustmentUploadError":"CONVERSION_ALREADY_ENHANCED"},"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again.","location":{"fieldPathElements":[{"fieldName":"conversion_adjustments","index":0}]}}]}]}', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/data.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/data.ts index b544baaebd..709ab6d2a8 100644 --- a/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/data.ts +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/data.ts @@ -1,307 +1,9 @@ +import { v0oauthScenarios, v1oauthScenarios } from './oauth'; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + export const data = [ - { - name: 'google_adwords_enhanced_conversions', - description: 'Test 0', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v15/customers/1234567890:uploadConversionAdjustments', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': '0987654321', - }, - params: { - event: 'Product Added', - customerId: '1234567890', - destination: 'google_adwords_enhanced_conversions', - }, - body: { - JSON: { - partialFailure: true, - conversionAdjustments: [ - { - gclidDateTimePair: { - gclid: 'gclid1234', - conversionDateTime: '2022-01-01 12:32:45-08:00', - }, - restatementValue: { - adjustedValue: 10, - currency: 'INR', - }, - order_id: '10000', - adjustmentDateTime: '2022-01-01 12:32:45-08:00', - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - userIdentifiers: [ - { - addressInfo: { - hashedFirstName: - 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', - hashedLastName: - '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', - state: 'UK', - city: 'London', - hashedStreetAddress: - '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', - }, - }, - ], - adjustmentType: 'ENHANCEMENT', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 401, - body: { - output: { - message: - '""Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project." during Google_adwords_enhanced_conversions response transformation"', - authErrorCategory: 'REFRESH_TOKEN', - destinationResponse: [ - { - error: { - code: 401, - message: - 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', - status: 'UNAUTHENTICATED', - }, - }, - ], - statTags: { - destType: 'GOOGLE_ADWORDS_ENHANCED_CONVERSIONS', - errorCategory: 'network', - destinationId: 'Non-determininable', - workspaceId: 'Non-determininable', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - }, - status: 401, - }, - }, - }, - }, - }, - { - name: 'google_adwords_enhanced_conversions', - description: 'Test 1', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v15/customers/1234567899:uploadConversionAdjustments', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': '0987654321', - }, - params: { - event: 'Product Added', - customerId: '1234567899', - destination: 'google_adwords_enhanced_conversions', - }, - body: { - JSON: { - partialFailure: true, - conversionAdjustments: [ - { - gclidDateTimePair: { - gclid: 'gclid1234', - conversionDateTime: '2022-01-01 12:32:45-08:00', - }, - restatementValue: { - adjustedValue: 10, - currency: 'INR', - }, - order_id: '10000', - adjustmentDateTime: '2022-01-01 12:32:45-08:00', - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - userIdentifiers: [ - { - addressInfo: { - hashedFirstName: - 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', - hashedLastName: - '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', - state: 'UK', - city: 'London', - hashedStreetAddress: - '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', - }, - }, - ], - adjustmentType: 'ENHANCEMENT', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: { - destinationResponse: { - response: [ - { - results: [ - { - adjustmentDateTime: '2021-01-01 12:32:45-08:00', - adjustmentType: 'ENHANCEMENT', - conversionAction: 'customers/7693729833/conversionActions/874224905', - gclidDateTimePair: { - conversionDateTime: '2021-01-01 12:32:45-08:00', - gclid: '1234', - }, - orderId: '12345', - }, - ], - }, - ], - status: 200, - }, - message: 'Request Processed Successfully', - status: 200, - }, - }, - }, - }, - }, - { - name: 'google_adwords_enhanced_conversions', - description: 'Test 2', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v15/customers/1234567891:uploadConversionAdjustments', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': '0987654321', - }, - params: { - event: 'Product Added', - customerId: '1234567891', - destination: 'google_adwords_enhanced_conversions', - }, - body: { - JSON: { - partialFailure: true, - conversionAdjustments: [ - { - gclidDateTimePair: { - gclid: 'gclid1234', - conversionDateTime: '2022-01-01 12:32:45-08:00', - }, - restatementValue: { - adjustedValue: 10, - currency: 'INR', - }, - order_id: '10000', - adjustmentDateTime: '2022-01-01 12:32:45-08:00', - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - userIdentifiers: [ - { - addressInfo: { - hashedFirstName: - 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', - hashedLastName: - '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', - state: 'UK', - city: 'London', - hashedStreetAddress: - '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', - }, - }, - ], - adjustmentType: 'ENHANCEMENT', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - destinationResponse: [ - { - results: [ - { - conversionAction: { - id: 123434342, - }, - }, - ], - }, - ], - message: '" during Google_adwords_enhanced_conversions response transformation', - statTags: { - destType: 'GOOGLE_ADWORDS_ENHANCED_CONVERSIONS', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - status: 400, - }, - }, - }, - }, - }, + ...v0oauthScenarios, + ...v1oauthScenarios, + ...testScenariosForV0API, + ...testScenariosForV1API, ]; diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/oauth.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/oauth.ts new file mode 100644 index 0000000000..70d9eeaf33 --- /dev/null +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/oauth.ts @@ -0,0 +1,276 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { + generateProxyV1Payload, + generateProxyV0Payload, + generateMetadata, +} from '../../../testUtils'; + +const requestPayload = { + partialFailure: true, + conversionAdjustments: [ + { + gclidDateTimePair: { + gclid: 'gclid1234', + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + restatementValue: { + adjustedValue: 10, + currency: 'INR', + }, + order_id: '10000', + adjustmentDateTime: '2022-01-01 12:32:45-08:00', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + userIdentifiers: [ + { + addressInfo: { + hashedFirstName: 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', + hashedLastName: '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', + state: 'UK', + city: 'London', + hashedStreetAddress: '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', + }, + }, + ], + adjustmentType: 'ENHANCEMENT', + }, + ], +}; + +const headers = { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '0987654321', +}; + +const params = { + event: 'Product Added', + customerId: '1234567890', + destination: 'google_adwords_enhanced_conversions', +}; + +const commonRequestParameters = { + params, + headers, + JSON: requestPayload, +}; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_ENHANCED_CONVERSIONS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const v0oauthScenarios = [ + { + id: 'gaec_v0_oauth_scenario_1', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v0 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: + 'Since the error from the destination is 401 - the proxy should return 500 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567890:uploadConversionAdjustments', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + authErrorCategory: 'REFRESH_TOKEN', + destinationResponse: [ + { + error: { + code: 401, + message: + 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', + status: 'UNAUTHENTICATED', + }, + }, + ], + message: + '""Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project." during Google_adwords_enhanced_conversions response transformation"', + statTags: expectedStatTags, + status: 401, + }, + }, + }, + }, + }, + { + id: 'gaec_v0_oauth_scenario_2', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v0 API] :: Oauth where caller does not have permission mock response from destination', + successCriteria: + 'Since the error from the destination is 403 - the proxy should return 403 with error', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + JSON: { + query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Product Added'`, + }, + headers, + params: { + event: 'Product Added', + customerId: '1234567910', + destination: 'google_adwords_enhanced_conversions', + }, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567910/googleAds:searchStream', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + authErrorCategory: 'AUTH_STATUS_INACTIVE', + destinationResponse: [ + { + error: { + code: 403, + errors: [ + { + domain: 'global', + message: 'The caller does not have permission', + reason: 'forbidden', + }, + ], + message: 'The caller does not have permission', + status: 'PERMISSION_DENIED', + }, + }, + ], + message: + '""The caller does not have permission" during Google_adwords_enhanced_conversions response transformation"', + statTags: expectedStatTags, + status: 403, + }, + }, + }, + }, + }, +]; + +export const v1oauthScenarios = [ + { + id: 'gaec_v1_oauth_scenario_1', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v1 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: + 'Since the error from the destination is 401 - the proxy should return 500 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567890:uploadConversionAdjustments', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + authErrorCategory: 'REFRESH_TOKEN', + message: + '""Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project." during Google_adwords_enhanced_conversions response transformation"', + response: [ + { + error: + '""Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project." during Google_adwords_enhanced_conversions response transformation"', + metadata: generateMetadata(1), + statusCode: 401, + }, + ], + statTags: expectedStatTags, + status: 401, + }, + }, + }, + }, + }, + { + id: 'gaec_v1_oauth_scenario_2', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v1 API] :: Oauth where caller does not have permission mock response from destination', + successCriteria: + 'Since the error from the destination is 403 - the proxy should return 403 with error', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + JSON: { + query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Product Added'`, + }, + headers, + params: { + event: 'Product Added', + customerId: '1234567910', + destination: 'google_adwords_enhanced_conversions', + }, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567910/googleAds:searchStream', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + authErrorCategory: 'AUTH_STATUS_INACTIVE', + message: + '""The caller does not have permission" during Google_adwords_enhanced_conversions response transformation"', + response: [ + { + error: + '""The caller does not have permission" during Google_adwords_enhanced_conversions response transformation"', + metadata: generateMetadata(1), + statusCode: 403, + }, + ], + statTags: expectedStatTags, + status: 403, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/network.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/network.ts index 672cd73bf7..69b3a6103a 100644 --- a/test/integrations/destinations/google_adwords_enhanced_conversions/network.ts +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/network.ts @@ -273,4 +273,146 @@ export const networkCallsData = [ status: 400, }, }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v15/customers/1234567888/googleAds:searchStream', + data: { + query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Product Added'`, + }, + params: { destination: 'google_adwords_enhanced_conversion' }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '0987654321', + }, + method: 'POST', + }, + httpRes: { + data: [ + { + results: [ + { + conversionAction: { + id: 123434345, + }, + }, + ], + }, + ], + status: 200, + }, + }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v15/customers/1234567888:uploadConversionAdjustments', + data: { + conversionAdjustments: [ + { + adjustmentDateTime: '2022-01-01 12:32:45-08:00', + adjustmentType: 'ENHANCEMENT', + conversionAction: 'customers/1234567888/conversionActions/123434345', + gclidDateTimePair: { + conversionDateTime: '2022-01-01 12:32:45-08:00', + gclid: 'gclid1234', + }, + order_id: '10000', + restatementValue: { adjustedValue: 10, currency: 'INR' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + userIdentifiers: [ + { + addressInfo: { + hashedFirstName: + 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', + hashedLastName: + '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', + state: 'UK', + city: 'London', + hashedStreetAddress: + '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', + }, + }, + ], + }, + ], + partialFailure: true, + }, + params: { destination: 'google_adwords_enhanced_conversion' }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '0987654321', + }, + method: 'POST', + }, + httpRes: { + status: 200, + data: { + partialFailureError: { + code: 3, + message: + 'Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again., at conversion_adjustments[0]', + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v15.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + conversionAdjustmentUploadError: 'CONVERSION_ALREADY_ENHANCED', + }, + message: + 'Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again.', + location: { + fieldPathElements: [ + { + fieldName: 'conversion_adjustments', + index: 0, + }, + ], + }, + }, + ], + }, + ], + }, + }, + }, + }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v15/customers/1234567910/googleAds:searchStream', + data: { + query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Product Added'`, + }, + params: { destination: 'google_adwords_enhanced_conversion' }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '0987654321', + }, + method: 'POST', + }, + httpRes: { + data: [ + { + error: { + code: 403, + message: 'The caller does not have permission', + errors: [ + { + message: 'The caller does not have permission', + domain: 'global', + reason: 'forbidden', + }, + ], + status: 'PERMISSION_DENIED', + }, + }, + ], + status: 403, + }, + }, ]; From f00d411c254d69f0155c7aa267f187bf6f59f6d4 Mon Sep 17 00:00:00 2001 From: AASHISH MALIK Date: Fri, 15 Mar 2024 15:43:15 +0530 Subject: [PATCH 27/35] chore: algolia component tests (#3183) --- src/v1/destinations/algolia/networkHandler.js | 22 +- .../algolia/dataDelivery/business.ts | 331 +++++++++++ .../algolia/dataDelivery/constant.ts | 118 ++++ .../destinations/algolia/dataDelivery/data.ts | 9 + .../algolia/dataDelivery/other.ts | 524 ++++++++++++++++++ .../destinations/algolia/network.ts | 117 ++++ 6 files changed, 1117 insertions(+), 4 deletions(-) create mode 100644 test/integrations/destinations/algolia/dataDelivery/business.ts create mode 100644 test/integrations/destinations/algolia/dataDelivery/constant.ts create mode 100644 test/integrations/destinations/algolia/dataDelivery/data.ts create mode 100644 test/integrations/destinations/algolia/dataDelivery/other.ts create mode 100644 test/integrations/destinations/algolia/network.ts diff --git a/src/v1/destinations/algolia/networkHandler.js b/src/v1/destinations/algolia/networkHandler.js index 21f415197f..de25993fb1 100644 --- a/src/v1/destinations/algolia/networkHandler.js +++ b/src/v1/destinations/algolia/networkHandler.js @@ -1,7 +1,7 @@ /* eslint-disable no-restricted-syntax */ const { TransformerProxyError } = require('../../../v0/util/errorTypes'); const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); -const { isHttpStatusSuccess, getAuthErrCategoryFromStCode } = require('../../../v0/util/index'); +const { isHttpStatusSuccess } = require('../../../v0/util/index'); const { processAxiosResponse, @@ -44,15 +44,29 @@ const responseHandler = (responseParams) => { }); } - // sending back 500 for retry + // At least one event in the batch is invalid. + if (status === 422) { + // sending back 500 for retry + throw new TransformerProxyError( + `ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation`, + 500, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(500), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); + } + throw new TransformerProxyError( `ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation`, - 500, + status, { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), }, destinationResponse, - getAuthErrCategoryFromStCode(status), + '', responseWithIndividualEvents, ); }; diff --git a/test/integrations/destinations/algolia/dataDelivery/business.ts b/test/integrations/destinations/algolia/dataDelivery/business.ts new file mode 100644 index 0000000000..8ba964e2dd --- /dev/null +++ b/test/integrations/destinations/algolia/dataDelivery/business.ts @@ -0,0 +1,331 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV0Payload, generateProxyV1Payload } from '../../../testUtils'; +import { abortStatTags, commonRequestProperties, metadataArray, retryStatTags } from './constant'; +const proxyMetdata3 = { + jobId: 3, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; +export const testScenariosForV0API = [ + { + id: 'algolia_v0_bussiness_scenario_1', + name: 'algolia', + description: '[Proxy v0 API] :: algolia all valid events', + successCriteria: 'Proper response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: + '[Generic Response Handler] Request for destination: algolia Processed Successfully', + destinationResponse: { + response: { + message: 'OK', + status: 200, + }, + status: 200, + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_bussiness_scenario_2', + name: 'algolia', + description: '[Proxy v0 API] :: algolia with invalid event', + successCriteria: 'Error Response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + JSON: commonRequestProperties.singleInValidEvent, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 422, + body: { + output: { + status: 422, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 422', + destinationResponse: { + response: { + status: 422, + message: 'EventType must be one of "click", "conversion" or "view"', + }, + status: 422, + }, + statTags: abortStatTags, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_bussiness_scenario_3', + name: 'algolia', + description: '[Proxy v0 API] :: algolia with invalid events in batch', + successCriteria: 'Error Response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + JSON: commonRequestProperties.combinedValidInvalidEvents, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 422, + body: { + output: { + status: 422, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 422', + destinationResponse: { + response: { + status: 422, + message: 'EventType must be one of "click", "conversion" or "view"', + }, + status: 422, + }, + statTags: abortStatTags, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'algolia_v1_bussiness_scenario_1', + name: 'algolia', + description: '[Proxy v1 API] :: algolia all valid events in batch', + successCriteria: 'Success response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + JSON: commonRequestProperties.multipleValidEvent, + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[ALGOLIA Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + message: 'OK', + status: 200, + }, + status: 200, + }, + response: [ + { + error: 'success', + metadata: metadataArray[0], + statusCode: 200, + }, + { + error: 'success', + metadata: metadataArray[1], + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'algolia_v1_bussiness_scenario_2', + name: 'algolia', + description: '[Proxy v1 API] :: algolia all invalid events in batch', + successCriteria: 'Send response with dontBatch as true', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + JSON: commonRequestProperties.singleInValidEvent, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + response: [ + { + error: + '{"status":422,"message":"EventType must be one of \\"click\\", \\"conversion\\" or \\"view\\""}', + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + statusCode: 500, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v1_bussiness_scenario_3', + name: 'algolia', + description: '[Proxy v1 API] :: algolia combination of valid and invalid events in batch', + successCriteria: 'Should use dontBatch true and proper response returned', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + JSON: commonRequestProperties.combinedValidInvalidEvents, + }, + [...metadataArray, proxyMetdata3], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + response: [ + { + error: + '{"status":422,"message":"EventType must be one of \\"click\\", \\"conversion\\" or \\"view\\""}', + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: true, + }, + statusCode: 500, + }, + { + error: + '{"status":422,"message":"EventType must be one of \\"click\\", \\"conversion\\" or \\"view\\""}', + metadata: { + jobId: 2, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: true, + }, + statusCode: 500, + }, + { + error: + '{"status":422,"message":"EventType must be one of \\"click\\", \\"conversion\\" or \\"view\\""}', + metadata: { + jobId: 3, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: true, + }, + statusCode: 500, + }, + ], + statTags: retryStatTags, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/algolia/dataDelivery/constant.ts b/test/integrations/destinations/algolia/dataDelivery/constant.ts new file mode 100644 index 0000000000..e8d0817a7f --- /dev/null +++ b/test/integrations/destinations/algolia/dataDelivery/constant.ts @@ -0,0 +1,118 @@ +const proxyMetdata1 = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; + +const proxyMetdata2 = { + jobId: 2, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; + +export const metadataArray = [proxyMetdata1, proxyMetdata2]; + +export const abortStatTags = { + errorCategory: 'network', + errorType: 'aborted', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +export const commonRequestProperties = { + commonHeaders: { + 'X-Algolia-API-Key': 'dummyApiKey', + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'User-Agent': 'RudderLabs', + }, + singleValidEvent: { + events: [ + { + eventName: 'product clicked', + eventType: 'click', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + singleInValidEvent: { + events: [ + { + eventName: 'product clicked', + eventType: 'abc', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + multipleValidEvent: { + events: [ + { + eventName: 'product clicked', + eventType: 'click', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'view', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + combinedValidInvalidEvents: { + events: [ + { + eventName: 'product clicked', + eventType: 'click', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'view', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'abc', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, +}; + +export const retryStatTags = { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', +}; diff --git a/test/integrations/destinations/algolia/dataDelivery/data.ts b/test/integrations/destinations/algolia/dataDelivery/data.ts new file mode 100644 index 0000000000..feb4eb46c5 --- /dev/null +++ b/test/integrations/destinations/algolia/dataDelivery/data.ts @@ -0,0 +1,9 @@ +import { testScenariosForV0API, testScenariosForV1API } from './business'; +import { otherScenariosV0, otherScenariosV1 } from './other'; + +export const data = [ + ...testScenariosForV0API, + ...testScenariosForV1API, + ...otherScenariosV0, + ...otherScenariosV1, +]; diff --git a/test/integrations/destinations/algolia/dataDelivery/other.ts b/test/integrations/destinations/algolia/dataDelivery/other.ts new file mode 100644 index 0000000000..f5ccc70337 --- /dev/null +++ b/test/integrations/destinations/algolia/dataDelivery/other.ts @@ -0,0 +1,524 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV0Payload, generateProxyV1Payload } from '../../../testUtils'; + +export const otherScenariosV0 = [ + { + id: 'algolia_v0_other_scenario_1', + name: 'algolia', + description: + '[Proxy v0 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + endpoint: 'https://random_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 503, + body: { + output: { + status: 503, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 503', + destinationResponse: { + response: { + error: { + message: 'Service Unavailable', + description: + 'The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later.', + }, + }, + status: 503, + }, + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_other_scenario_2', + name: 'algolia', + description: '[Proxy v0 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 500', + destinationResponse: { + response: 'Internal Server Error', + status: 500, + }, + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_other_scenario_3', + name: 'algolia', + description: '[Proxy v0 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 504, + body: { + output: { + status: 504, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 504', + destinationResponse: { + response: 'Gateway Timeout', + status: 504, + }, + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_other_scenario_4', + name: 'algolia', + description: '[Proxy v0 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + endpoint: 'https://random_test_url/test_for_null_response', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 500', + destinationResponse: { + response: '', + status: 500, + }, + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_other_scenario_5', + name: 'algolia', + description: + '[Proxy v0 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 500', + destinationResponse: { + response: '', + status: 500, + }, + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, +]; + +export const otherScenariosV1: ProxyV1TestData[] = [ + { + id: 'algolia_v1_other_scenario_1', + name: 'algolia', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 503, + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + status: 503, + }, + }, + }, + }, + }, + { + id: 'algolia_v1_other_scenario_2', + name: 'algolia', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + status: 500, + }, + }, + }, + }, + }, + { + id: 'algolia_v1_other_scenario_3', + name: 'algolia', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout"', + statusCode: 504, + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + status: 504, + }, + }, + }, + }, + }, + { + id: 'algolia_v1_other_scenario_4', + name: 'algolia', + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_response', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + status: 500, + }, + }, + }, + }, + }, + { + id: 'algolia_v1_other_scenario_5', + name: 'algolia', + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/algolia/network.ts b/test/integrations/destinations/algolia/network.ts new file mode 100644 index 0000000000..84e932bbdb --- /dev/null +++ b/test/integrations/destinations/algolia/network.ts @@ -0,0 +1,117 @@ +export const networkCallsData = [ + { + httpReq: { + url: 'https://insights.algolia.io/1/events', + data: { + events: [ + { + eventName: 'product clicked', + eventType: 'abc', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + params: {}, + headers: { 'User-Agent': 'RudderLabs' }, + method: 'POST', + }, + httpRes: { + data: { + status: 422, + message: 'EventType must be one of "click", "conversion" or "view"', + }, + status: 422, + }, + }, + { + httpReq: { + url: 'https://insights.algolia.io/1/events', + data: { + events: [ + { + eventName: 'product clicked', + eventType: 'abc', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'click', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + params: {}, + headers: { 'User-Agent': 'RudderLabs' }, + method: 'POST', + }, + httpRes: { + data: { + status: 422, + message: 'EventType must be one of "click", "conversion" or "view"', + }, + status: 422, + }, + }, + { + httpReq: { + url: 'https://insights.algolia.io/1/events', + data: { + events: [ + { + eventName: 'product clicked', + eventType: 'click', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'view', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'abc', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + params: {}, + headers: { 'User-Agent': 'RudderLabs' }, + method: 'POST', + }, + httpRes: { + data: { + status: 422, + message: 'EventType must be one of "click", "conversion" or "view"', + }, + status: 422, + }, + }, + { + httpReq: { + url: 'https://insights.algolia.io/1/events', + method: 'POST', + headers: { + 'User-Agent': 'RudderLabs', + }, + }, + httpRes: { + data: { + status: 200, + message: 'OK', + }, + status: 200, + }, + }, +]; From 5befa02848b66bcd89827d7e33728999ea243d0c Mon Sep 17 00:00:00 2001 From: Jayachand Date: Fri, 15 Mar 2024 16:36:56 +0530 Subject: [PATCH 28/35] chore: adding a metric to capture event batch size DAT-909 (#3070) * chore: adding a metric to capture event batch size --- src/controllers/userTransform.ts | 3 ++- src/services/userTransform.ts | 14 +++++++++----- src/util/prometheus.js | 20 ++++++++++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/controllers/userTransform.ts b/src/controllers/userTransform.ts index c344bd072a..3e01686a52 100644 --- a/src/controllers/userTransform.ts +++ b/src/controllers/userTransform.ts @@ -15,9 +15,10 @@ export class UserTransformController { '(User transform - router:/customTransform ):: Request to transformer', JSON.stringify(ctx.request.body), ); + const requestSize = Number(ctx.request.get('content-length')); const events = ctx.request.body as ProcessorTransformationRequest[]; const processedRespone: UserTransformationServiceResponse = - await UserTransformService.transformRoutine(events, ctx.state.features); + await UserTransformService.transformRoutine(events, ctx.state.features, requestSize); ctx.body = processedRespone.transformedEvents; ControllerUtility.postProcess(ctx, processedRespone.retryStatus); logger.debug( diff --git a/src/services/userTransform.ts b/src/services/userTransform.ts index bae833c86a..18c47ddc83 100644 --- a/src/services/userTransform.ts +++ b/src/services/userTransform.ts @@ -14,7 +14,7 @@ import { RetryRequestError, extractStackTraceUptoLastSubstringMatch, } from '../util/utils'; -import { getMetadata, isNonFuncObject } from '../v0/util'; +import { getMetadata, getTransformationMetadata, isNonFuncObject } from '../v0/util'; import { SUPPORTED_FUNC_NAMES } from '../util/ivmFactory'; import logger from '../logger'; import stats from '../util/stats'; @@ -28,6 +28,7 @@ export class UserTransformService { public static async transformRoutine( events: ProcessorTransformationRequest[], features: FeatureFlags = {}, + requestSize = 0, ): Promise { let retryStatus = 200; const groupedEvents: NonNullable = groupBy( @@ -162,16 +163,19 @@ export class UserTransformService { ), ); stats.counter('user_transform_errors', eventsToProcess.length, { - transformationId: eventsToProcess[0]?.metadata?.transformationId, - workspaceId: eventsToProcess[0]?.metadata?.workspaceId, status, ...metaTags, + ...getTransformationMetadata(eventsToProcess[0]?.metadata), }); } finally { stats.timing('user_transform_request_latency', userFuncStartTime, { - workspaceId: eventsToProcess[0]?.metadata?.workspaceId, - transformationId: eventsToProcess[0]?.metadata?.transformationId, ...metaTags, + ...getTransformationMetadata(eventsToProcess[0]?.metadata), + }); + + stats.histogram('user_transform_batch_size', requestSize, { + ...metaTags, + ...getTransformationMetadata(eventsToProcess[0]?.metadata), }); } diff --git a/src/util/prometheus.js b/src/util/prometheus.js index b502681987..5de7ac899d 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -593,6 +593,10 @@ class Prometheus { name: 'tp_batch_size', help: 'Size of batch of events for tracking plan validation', type: 'histogram', + buckets: [ + 1024, 102400, 524288, 1048576, 10485760, 20971520, 52428800, 104857600, 209715200, + 524288000, + ], labelNames: [ 'sourceType', 'destinationType', @@ -670,6 +674,22 @@ class Prometheus { 'k8_namespace', ], }, + { + name: 'user_transform_batch_size', + help: 'user_transform_batch_size', + type: 'histogram', + labelNames: [ + 'workspaceId', + 'transformationId', + 'sourceType', + 'destinationType', + 'k8_namespace', + ], + buckets: [ + 1024, 102400, 524288, 1048576, 10485760, 20971520, 52428800, 104857600, 209715200, + 524288000, + ], // 1KB, 100KB, 0.5MB, 1MB, 10MB, 20MB, 50MB, 100MB, 200MB, 500MB + }, { name: 'source_transform_request_latency', help: 'source_transform_request_latency', From 2ad12399040cbdc15453c2bb84f3aa9ab87e5c1f Mon Sep 17 00:00:00 2001 From: Yashasvi Bajpai <33063622+yashasvibajpai@users.noreply.github.com> Date: Mon, 18 Mar 2024 09:57:03 +0530 Subject: [PATCH 29/35] chore: onboard adobe to proxy v1 tests (#3163) * chore: onboard adobe to proxy v1 tests * chore: fix lint * chore: fix lintx2 --- .../adobe_analytics/dataDelivery/business.ts | 181 ++++++++++++++++++ .../adobe_analytics/dataDelivery/data.ts | 8 +- .../destinations/adobe_analytics/network.ts | 2 +- 3 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 test/integrations/destinations/adobe_analytics/dataDelivery/business.ts diff --git a/test/integrations/destinations/adobe_analytics/dataDelivery/business.ts b/test/integrations/destinations/adobe_analytics/dataDelivery/business.ts new file mode 100644 index 0000000000..76e07690cf --- /dev/null +++ b/test/integrations/destinations/adobe_analytics/dataDelivery/business.ts @@ -0,0 +1,181 @@ +import { ProxyMetdata } from '../../../../../src/types'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const statTags = { + aborted: { + destType: 'ADOBE_ANALYTICS', + destinationId: 'dummyDestinationId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, +}; + +export const proxyMetdata: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; +const headers = { + 'Content-Type': 'application/xml', +}; + +export const reqMetadataArray = [proxyMetdata]; + +const failureRequestParameters = { + XML: { + payload: + '17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerprodViewGames;;11;148.39failureReport', + }, + params: {}, +}; + +const successRequestParameters = { + XML: { + payload: + '127.0.1.0www.google.co.inGoogleid1110011prodViewGames;Monopoly;1;14.00,Games;UNO;2;6.90successreport', + }, + params: {}, +}; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'adobe_analytics_v1_scenario_1', + name: 'adobe_analytics', + description: '[Proxy v1 API] :: Test for Failure response from Adobe Analytics with reason', + successCriteria: 'Should return a 400 status code with reason', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...failureRequestParameters, + headers, + endpoint: 'https://adobe.failure.omtrdc.net/b/ss//6', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: + '[ADOBE_ANALYTICS Response Handler] Request failed for destination adobe_analytics : NO pagename OR pageurl', + response: [ + { + error: + '[ADOBE_ANALYTICS Response Handler] Request failed for destination adobe_analytics : NO pagename OR pageurl', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, + { + id: 'adobe_analytics_v1_scenario_2', + name: 'adobe_analytics', + description: + '[Proxy v1 API] :: Test for Failure response from Adobe Analytics without reason (Generic error)', + successCriteria: 'Should return a 400 status code with a general error', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...failureRequestParameters, + headers, + endpoint: 'https://adobe.failure2.omtrdc.net/b/ss//6', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: + '[ADOBE_ANALYTICS Response Handler] Request failed for destination adobe_analytics with a general error', + response: [ + { + error: + '[ADOBE_ANALYTICS Response Handler] Request failed for destination adobe_analytics with a general error', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, + { + id: 'adobe_analytics_v1_scenario_3', + name: 'adobe_analytics', + description: '[Proxy v1 API] :: Test for Success response from Adobe Analytics', + successCriteria: 'Should return a 200 status code with status SUCCESS', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...successRequestParameters, + headers, + endpoint: 'https://adobe.success.omtrdc.net/b/ss//6', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[ADOBE_ANALYTICS] - Request Processed Successfully', + response: [ + { + error: '"SUCCESS"', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/adobe_analytics/dataDelivery/data.ts b/test/integrations/destinations/adobe_analytics/dataDelivery/data.ts index 182969da73..2535a0639e 100644 --- a/test/integrations/destinations/adobe_analytics/dataDelivery/data.ts +++ b/test/integrations/destinations/adobe_analytics/dataDelivery/data.ts @@ -1,4 +1,6 @@ -export const data = [ +import { testScenariosForV1API } from './business'; + +const legacyTests = [ { name: 'adobe_analytics', description: 'Test 0: Failure response from Adobe Analytics with reason', @@ -72,7 +74,7 @@ export const data = [ JSON_ARRAY: {}, XML: { payload: - '17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerprodViewGames;;11;148.39failureReportgeneric', + '17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerprodViewGames;;11;148.39failureReport', }, FORM: {}, }, @@ -140,3 +142,5 @@ export const data = [ }, }, ]; + +export const data = [...testScenariosForV1API, ...legacyTests]; diff --git a/test/integrations/destinations/adobe_analytics/network.ts b/test/integrations/destinations/adobe_analytics/network.ts index 2fe4f0204e..7e32c5f10b 100644 --- a/test/integrations/destinations/adobe_analytics/network.ts +++ b/test/integrations/destinations/adobe_analytics/network.ts @@ -17,7 +17,7 @@ export const networkCallsData = [ { httpReq: { url: 'https://adobe.failure2.omtrdc.net/b/ss//6', - data: '17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerprodViewGames;;11;148.39failureReportgeneric', + data: '17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerprodViewGames;;11;148.39failureReport', params: {}, headers: { 'Content-Type': 'application/xml', From 7018b1e5e7f37ae177191c5ecf3a71cfe2f3d147 Mon Sep 17 00:00:00 2001 From: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Date: Mon, 18 Mar 2024 10:36:32 +0530 Subject: [PATCH 30/35] feat: onboard destination movable ink (#3167) * feat: onboard destination movable ink * test: update common.ts * feat: add batching support * feat: batching on max size in bytes * docs: added comments --- src/cdk/v2/destinations/movable_ink/config.js | 3 + .../movable_ink/procWorkflow.yaml | 72 +++++++ .../destinations/movable_ink/rtWorkflow.yaml | 74 +++++++ src/features.json | 1 + .../destinations/movable_ink/common.ts | 128 ++++++++++++ .../movable_ink/processor/data.ts | 4 + .../movable_ink/processor/identify.ts | 64 ++++++ .../movable_ink/processor/track.ts | 189 ++++++++++++++++++ .../movable_ink/processor/validation.ts | 131 ++++++++++++ .../destinations/movable_ink/router/data.ts | 162 +++++++++++++++ 10 files changed, 828 insertions(+) create mode 100644 src/cdk/v2/destinations/movable_ink/config.js create mode 100644 src/cdk/v2/destinations/movable_ink/procWorkflow.yaml create mode 100644 src/cdk/v2/destinations/movable_ink/rtWorkflow.yaml create mode 100644 test/integrations/destinations/movable_ink/common.ts create mode 100644 test/integrations/destinations/movable_ink/processor/data.ts create mode 100644 test/integrations/destinations/movable_ink/processor/identify.ts create mode 100644 test/integrations/destinations/movable_ink/processor/track.ts create mode 100644 test/integrations/destinations/movable_ink/processor/validation.ts create mode 100644 test/integrations/destinations/movable_ink/router/data.ts diff --git a/src/cdk/v2/destinations/movable_ink/config.js b/src/cdk/v2/destinations/movable_ink/config.js new file mode 100644 index 0000000000..673e94620e --- /dev/null +++ b/src/cdk/v2/destinations/movable_ink/config.js @@ -0,0 +1,3 @@ +module.exports = { + MAX_REQUEST_SIZE_IN_BYTES: 13500, +}; diff --git a/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml b/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml new file mode 100644 index 0000000000..25270058c5 --- /dev/null +++ b/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml @@ -0,0 +1,72 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + - name: defaultRequestConfig + path: ../../../../v0/util + - name: toUnixTimestampInMS + path: ../../../../v0/util + - name: base64Convertor + path: ../../../../v0/util + - path: ./utils + +steps: + - name: messageType + template: | + .message.type.toLowerCase(); + + - name: validateInput + template: | + let messageType = $.outputs.messageType; + $.assert(messageType, "message Type is not present. Aborting"); + $.assert(messageType in {{$.EventType.([.IDENTIFY,.TRACK])}}, "message type " + messageType + " is not supported"); + $.assertConfig(.destination.Config.endpoint, "Movable Ink Endpoint is not present. Aborting"); + $.assertConfig(.destination.Config.accessKey, "Access key is not present . Aborting"); + $.assertConfig(.destination.Config.accessSecret, "Access Secret is not present. Aborting"); + $.assert(.message.timestamp ?? .message.originalTimestamp, "Timestamp is not present. Aborting"); + + const userId = .message.().( + {{{{$.getGenericPaths("userIdOnly")}}}}; + ); + const email = .message.().( + {{{{$.getGenericPaths("email")}}}}; + ); + + $.assert(userId ?? email ?? .message.anonymousId, "Either one of userId or email or anonymousId is required. Aborting"); + + - name: preparePayload + description: Prepare payload for identify and track. This payload schema needs to be configured in the Movable Ink dashboard. Movable Ink will discard any additional fields from the input payload. + template: | + const userId = .message.().( + {{{{$.getGenericPaths("userIdOnly")}}}}; + ); + const email = .message.().( + {{{{$.getGenericPaths("email")}}}}; + ); + const timestampInUnix = $.toUnixTimestampInMS(.message.().( + {{{{$.getGenericPaths("timestamp")}}}}; + )); + $.context.payload = { + ...(.message), + userId: userId ?? email, + timestamp: timestampInUnix, + anonymousId: .message.anonymousId + } + + - name: buildResponse + description: In batchMode we return payload directly + condition: $.batchMode + template: | + $.context.payload + else: + name: buildResponseForProcessTransformation + template: | + const response = $.defaultRequestConfig(); + response.body.JSON = $.context.payload; + response.endpoint = .destination.Config.endpoint; + response.method = "POST"; + response.headers = { + "Content-Type": "application/json", + "Authorization": "Basic " + $.base64Convertor(.destination.Config.accessKey + ":" + .destination.Config.accessSecret) + } + response; diff --git a/src/cdk/v2/destinations/movable_ink/rtWorkflow.yaml b/src/cdk/v2/destinations/movable_ink/rtWorkflow.yaml new file mode 100644 index 0000000000..46afb34d53 --- /dev/null +++ b/src/cdk/v2/destinations/movable_ink/rtWorkflow.yaml @@ -0,0 +1,74 @@ +bindings: + - name: handleRtTfSingleEventError + path: ../../../../v0/util/index + - path: ./utils + exportAll: true + - name: base64Convertor + path: ../../../../v0/util + - name: BatchUtils + path: '@rudderstack/workflow-engine' + - path: ./config + +steps: + - name: validateInput + template: | + $.assert(Array.isArray(^) && ^.length > 0, "Invalid event array") + + - name: transform + externalWorkflow: + path: ./procWorkflow.yaml + bindings: + - name: batchMode + value: true + loopOverInput: true + + - name: successfulEvents + template: | + $.outputs.transform#idx.output.({ + "batchedRequest": ., + "batched": false, + "destination": ^[idx].destination, + "metadata": ^[idx].metadata, + "statusCode": 200 + })[] + + - name: failedEvents + template: | + $.outputs.transform#idx.error.( + $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) + )[] + + - name: batchSuccessfulEvents + description: Batches the successfulEvents + template: | + let batches = $.BatchUtils.chunkArrayBySizeAndLength( + $.outputs.successfulEvents, {maxSizeInBytes: $.MAX_REQUEST_SIZE_IN_BYTES}).items; + + batches@batch.({ + "batchedRequest": { + "body": { + "JSON": {"events": ~r batch.batchedRequest[]}, + "JSON_ARRAY": {}, + "XML": {}, + "FORM": {} + }, + "version": "1", + "type": "REST", + "method": "POST", + "endpoint": batch[0].destination.Config.().(.endpoint), + "headers": batch[0].destination.Config.().({ + "Content-Type": "application/json", + "Authorization": "Basic " + $.base64Convertor(.accessKey + ":" + .accessSecret) + }), + "params": {}, + "files": {} + }, + "metadata": ~r batch.metadata[], + "batched": true, + "statusCode": 200, + "destination": batch[0].destination + })[]; + + - name: finalPayload + template: | + [...$.outputs.batchSuccessfulEvents, ...$.outputs.failedEvents] diff --git a/src/features.json b/src/features.json index dc52044048..76b562a825 100644 --- a/src/features.json +++ b/src/features.json @@ -67,6 +67,7 @@ "THE_TRADE_DESK": true, "INTERCOM": true, "NINETAILED": true, + "MOVABLE_INK": true, "KOALA": true }, "regulations": [ diff --git a/test/integrations/destinations/movable_ink/common.ts b/test/integrations/destinations/movable_ink/common.ts new file mode 100644 index 0000000000..f7eaa7af39 --- /dev/null +++ b/test/integrations/destinations/movable_ink/common.ts @@ -0,0 +1,128 @@ +import { Destination } from '../../../../src/types'; + +const destType = 'movable_ink'; +const destTypeInUpperCase = 'MOVABLE_INK'; +const displayName = 'Movable Ink'; +const channel = 'web'; +const destination: Destination = { + Config: { + endpoint: 'https://collector.movableink-dmz.com/behavioral/abc123', + accessKey: 'test-access-key', + accessSecret: 'test_access_secret', + }, + DestinationDefinition: { + DisplayName: displayName, + ID: '123', + Name: destTypeInUpperCase, + Config: { cdkV2Enabled: true }, + }, + Enabled: true, + ID: '123', + Name: destTypeInUpperCase, + Transformations: [], + WorkspaceID: 'test-workspace-id', +}; + +const processorInstrumentationErrorStatTags = { + destType: destTypeInUpperCase, + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +const RouterInstrumentationErrorStatTags = { + ...processorInstrumentationErrorStatTags, + feature: 'router', +}; + +const traits = { + email: 'test@example.com', + firstName: 'John', + lastName: 'Doe', + phone: '1234567890', +}; + +const headers = { + 'Content-Type': 'application/json', + Authorization: 'Basic dGVzdC1hY2Nlc3Mta2V5OnRlc3RfYWNjZXNzX3NlY3JldA==', +}; + +const commonProperties = { + product_id: '622c6f5d5cf86a4c77358033', + sku: '8472-998-0112', + categories: [ + { url: 'https://example1', id: '1' }, + { url: 'https://example2', id: '2' }, + ], + name: 'Cones of Dunshire', + brand: 'Wyatt Games', + variant: 'expansion pack', + price: 49.99, + quantity: 5, + coupon: 'PREORDER15', + position: 1, + url: 'https://www.website.com/product/path', + image_url: 'https://www.website.com/product/path.webp', +}; + +const customProperties = { + key1: 'value1', + key2: true, + key3: ['value3'], + key4: { key5: { key6: 'value6' } }, +}; + +const trackTestProperties = { + 'Product Added': { ...commonProperties, ...customProperties }, + 'Product Viewed': { ...commonProperties, ...customProperties }, + 'Order Completed': { + checkout_id: '70324a1f0eaf000000000000', + order_id: '40684e8f0eaf000000000000', + affiliation: 'Vandelay Games', + total: 52, + subtotal: 45, + revenue: 50, + shipping: 4, + tax: 3, + discount: 5, + coupon: 'NEWCUST5', + currency: 'USD', + products: [ + { + product_id: '622c6f5d5cf86a4c77358033', + sku: '8472-998-0112', + name: 'Cones of Dunshire', + price: 40, + position: 1, + category: 'Games', + url: 'https://www.website.com/product/path', + image_url: 'https://www.website.com/product/path.jpg', + }, + { + product_id: '577c6f5d5cf86a4c7735ba03', + sku: '3309-483-2201', + name: 'Five Crowns', + price: 5, + position: 2, + category: 'Games', + }, + ], + }, + 'Products Searched': { query: 'HDMI cable', url: 'https://www.website.com/product/path' }, + 'Custom event': { ...commonProperties, key1: 'value1', key2: true }, +}; + +export { + destType, + channel, + destination, + processorInstrumentationErrorStatTags, + RouterInstrumentationErrorStatTags, + traits, + headers, + trackTestProperties, +}; diff --git a/test/integrations/destinations/movable_ink/processor/data.ts b/test/integrations/destinations/movable_ink/processor/data.ts new file mode 100644 index 0000000000..45453c74cd --- /dev/null +++ b/test/integrations/destinations/movable_ink/processor/data.ts @@ -0,0 +1,4 @@ +import { validation } from './validation'; +import { identify } from './identify'; +import { track } from './track'; +export const data = [...identify, ...track, ...validation]; diff --git a/test/integrations/destinations/movable_ink/processor/identify.ts b/test/integrations/destinations/movable_ink/processor/identify.ts new file mode 100644 index 0000000000..27186da05c --- /dev/null +++ b/test/integrations/destinations/movable_ink/processor/identify.ts @@ -0,0 +1,64 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, channel, destination, traits, headers } from '../common'; + +export const identify: ProcessorTestData[] = [ + { + id: 'MovableInk-identify-test-1', + name: destType, + description: 'Identify call with traits and anonymousId', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with few additional mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint: destination.Config.endpoint, + headers, + JSON: { + type: 'identify', + userId: traits.email, + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/movable_ink/processor/track.ts b/test/integrations/destinations/movable_ink/processor/track.ts new file mode 100644 index 0000000000..5f30a3de83 --- /dev/null +++ b/test/integrations/destinations/movable_ink/processor/track.ts @@ -0,0 +1,189 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, channel, destination, headers, trackTestProperties } from '../common'; + +export const track: ProcessorTestData[] = [ + { + id: 'MovableInk-track-test-1', + name: destType, + description: 'Track call: Product Added event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with few additional mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + properties: trackTestProperties['Product Added'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint: destination.Config.endpoint, + headers, + JSON: { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + properties: trackTestProperties['Product Added'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'MovableInk-track-test-2', + name: destType, + description: 'Track call: Order Completed event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with few additional mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + properties: trackTestProperties['Order Completed'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint: destination.Config.endpoint, + headers, + JSON: { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + properties: trackTestProperties['Order Completed'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'MovableInk-track-test-3', + name: destType, + description: 'Track call: Custom event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with few additional mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + properties: trackTestProperties['Custom Event'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint: destination.Config.endpoint, + headers, + JSON: { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + properties: trackTestProperties['Custom Event'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/movable_ink/processor/validation.ts b/test/integrations/destinations/movable_ink/processor/validation.ts new file mode 100644 index 0000000000..f9f6c6a927 --- /dev/null +++ b/test/integrations/destinations/movable_ink/processor/validation.ts @@ -0,0 +1,131 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata } from '../../../testUtils'; +import { destType, destination, processorInstrumentationErrorStatTags } from '../common'; + +export const validation: ProcessorTestData[] = [ + { + id: 'MovableInk-validation-test-1', + name: destType, + description: 'All of the required fields — userId, email, and anonymousId — are missing.', + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Either one of userId or email or anonymousId is required. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Either one of userId or email or anonymousId is required. Aborting', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'MovableInk-validation-test-2', + name: destType, + description: 'Unsupported message type -> group', + scenario: 'Framework', + successCriteria: 'Instrumentation Error for Unsupported message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'group', + userId: 'userId123', + channel: 'mobile', + anonymousId: 'anon_123', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'message type group is not supported: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message type group is not supported', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'MovableInk-validation-test-3', + name: destType, + description: 'Missing required field -> timestamp', + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + integrations: { + All: true, + }, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Timestamp is not present. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Timestamp is not present. Aborting', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/movable_ink/router/data.ts b/test/integrations/destinations/movable_ink/router/data.ts new file mode 100644 index 0000000000..72df3d7074 --- /dev/null +++ b/test/integrations/destinations/movable_ink/router/data.ts @@ -0,0 +1,162 @@ +import { RouterTestData } from '../../../testTypes'; +import { RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; +import { + destType, + channel, + destination, + traits, + headers, + trackTestProperties, + RouterInstrumentationErrorStatTags, +} from '../common'; + +const routerRequest: RouterTransformationRequest = { + input: [ + { + message: { + type: 'identify', + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + destination, + }, + { + message: { + type: 'identify', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(2), + destination, + }, + { + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + properties: trackTestProperties['Product Added'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(3), + destination, + }, + { + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + properties: trackTestProperties['Custom Event'], + integrations: { + All: true, + }, + }, + metadata: generateMetadata(4), + destination, + }, + ], + destType, +}; + +export const data: RouterTestData[] = [ + { + id: 'MovableInk-router-test-1', + name: destType, + description: 'Basic Router Test to test multiple payloads', + scenario: 'Framework', + successCriteria: + 'Some events should be transformed successfully and some should fail for missing fields and status code should be 200', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: routerRequest, + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: destination.Config.endpoint, + headers, + params: {}, + body: { + JSON: { + events: [ + { + type: 'identify', + userId: traits.email, + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + properties: trackTestProperties['Product Added'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [generateMetadata(1), generateMetadata(3)], + batched: true, + statusCode: 200, + destination, + }, + { + metadata: [generateMetadata(2)], + batched: false, + statusCode: 400, + error: 'Either one of userId or email or anonymousId is required. Aborting', + statTags: RouterInstrumentationErrorStatTags, + destination, + }, + { + metadata: [generateMetadata(4)], + batched: false, + statusCode: 400, + error: 'Timestamp is not present. Aborting', + statTags: RouterInstrumentationErrorStatTags, + destination, + }, + ], + }, + }, + }, + }, +]; From c5105302f5d8f518b7caaf3e8e1012ca59cddde5 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 18 Mar 2024 07:22:12 +0000 Subject: [PATCH 31/35] chore(release): 1.59.0 --- CHANGELOG.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c143851091..7390317ab5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,57 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.59.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.59.0) (2024-03-18) + + +### Features + +* add Koala destination ([#3122](https://github.com/rudderlabs/rudder-transformer/issues/3122)) ([1ca039d](https://github.com/rudderlabs/rudder-transformer/commit/1ca039d64ebb1a18a0fc6b78ed5ee08528ad6b48)) +* add support for interaction events in sfmc ([#3109](https://github.com/rudderlabs/rudder-transformer/issues/3109)) ([0486049](https://github.com/rudderlabs/rudder-transformer/commit/0486049ba2ad96b50d8f29e96b46b96a8a5c9f76)) +* add support of ([c0ad214](https://github.com/rudderlabs/rudder-transformer/commit/c0ad21463981ef66154c8157083924f76825762d)) +* add support of custom page/screen event name in mixpanel ([#3098](https://github.com/rudderlabs/rudder-transformer/issues/3098)) ([0eb2393](https://github.com/rudderlabs/rudder-transformer/commit/0eb2393939fba2452ef7f07a1d149d87f18290c3)) +* add support of skip_user_properties_sync on Amplitude ([#3181](https://github.com/rudderlabs/rudder-transformer/issues/3181)) ([5e4ddbd](https://github.com/rudderlabs/rudder-transformer/commit/5e4ddbd8a591341a581a5721505d6dcb010f2eec)) +* adding zod validations ([#3066](https://github.com/rudderlabs/rudder-transformer/issues/3066)) ([325433b](https://github.com/rudderlabs/rudder-transformer/commit/325433b9188c8d1dbe740c7e193cdc2e58fdd751)) +* consent mode support for google adwords remarketing list ([#3143](https://github.com/rudderlabs/rudder-transformer/issues/3143)) ([7532c90](https://github.com/rudderlabs/rudder-transformer/commit/7532c90d7e1feac00f12961c56da18757010f44a)) +* **facebook:** update content_type mapping logic for fb pixel and fb conversions ([#3113](https://github.com/rudderlabs/rudder-transformer/issues/3113)) ([aea417c](https://github.com/rudderlabs/rudder-transformer/commit/aea417cd2691547399010c034cadbc5db6b0c6ee)) +* klaviyo profile mapping ([#3105](https://github.com/rudderlabs/rudder-transformer/issues/3105)) ([2761786](https://github.com/rudderlabs/rudder-transformer/commit/2761786ff3fc99ed6d4d3b7a6c2400226b1cfb12)) +* onboard destination movable ink ([#3167](https://github.com/rudderlabs/rudder-transformer/issues/3167)) ([7018b1e](https://github.com/rudderlabs/rudder-transformer/commit/7018b1e5e7f37ae177191c5ecf3a71cfe2f3d147)) +* onboard new destination ninetailed ([#3106](https://github.com/rudderlabs/rudder-transformer/issues/3106)) ([0e2588e](https://github.com/rudderlabs/rudder-transformer/commit/0e2588ecd87f3b2c6877a099aa1cbf2d5325966c)) +* update proxy data type for response handler input ([7d6ea12](https://github.com/rudderlabs/rudder-transformer/commit/7d6ea123e08b793a87f35290e740cbef547c3862)) +* update proxy tests for cm360 ([9dd8625](https://github.com/rudderlabs/rudder-transformer/commit/9dd862540cc8e4e56b9bc638cc1da62e5f19c45f)) +* update proxy tests for cm360 ([#3039](https://github.com/rudderlabs/rudder-transformer/issues/3039)) ([0504ffa](https://github.com/rudderlabs/rudder-transformer/commit/0504ffa898956f5b61771fb32ecfd0e0bf15248f)) +* update proxy v1 test cases ([b1327eb](https://github.com/rudderlabs/rudder-transformer/commit/b1327ebdb049163b3c5f046cb4605518e99481f3)) +* use dontBatch directive in algolia ([#3169](https://github.com/rudderlabs/rudder-transformer/issues/3169)) ([916aaec](https://github.com/rudderlabs/rudder-transformer/commit/916aaecb1939160620d5fd3c4c0c0e33f2a371b2)) + + +### Bug Fixes + +* add error handling for tiktok ads ([#3144](https://github.com/rudderlabs/rudder-transformer/issues/3144)) ([e93e47f](https://github.com/rudderlabs/rudder-transformer/commit/e93e47f33e098104fb532916932fe38bbfeaa4a1)) +* **algolia:** added check for objectIds or filters to be non empty ([#3126](https://github.com/rudderlabs/rudder-transformer/issues/3126)) ([d619c97](https://github.com/rudderlabs/rudder-transformer/commit/d619c9769cd270cb2d16dad0865683ff4beb2d19)) +* am formatting issues ([4653b74](https://github.com/rudderlabs/rudder-transformer/commit/4653b74522cc917230c211ce1df1b57e8a607ad7)) +* api contract for v1 proxy ([76e0284](https://github.com/rudderlabs/rudder-transformer/commit/76e02848c58a6630c36f724dc4ccbac3d29a8007)) +* api contract for v1 proxy ([#3049](https://github.com/rudderlabs/rudder-transformer/issues/3049)) ([93947db](https://github.com/rudderlabs/rudder-transformer/commit/93947db35cdaf1ca7ed87ec5f73567754af312ab)) +* clevertap remove stringification of array object properties ([#3048](https://github.com/rudderlabs/rudder-transformer/issues/3048)) ([69e43b6](https://github.com/rudderlabs/rudder-transformer/commit/69e43b6ffadeaec87b7440da34a341890ceba252)) +* convert to string from null in hs ([#3136](https://github.com/rudderlabs/rudder-transformer/issues/3136)) ([75e9f46](https://github.com/rudderlabs/rudder-transformer/commit/75e9f462b0ff9b9a8abab3c78dc7d147926e9e5e)) +* email mapping for clevertap ([c1b3736](https://github.com/rudderlabs/rudder-transformer/commit/c1b3736ab60c9582bdf1c4b07a761976de0da16f)) +* email mapping for clevertap ([#3173](https://github.com/rudderlabs/rudder-transformer/issues/3173)) ([04eab92](https://github.com/rudderlabs/rudder-transformer/commit/04eab92e1c383f9e8cdd5c845530a42a0af2932a)) +* event fix and added utility ([#3142](https://github.com/rudderlabs/rudder-transformer/issues/3142)) ([9b705b7](https://github.com/rudderlabs/rudder-transformer/commit/9b705b71a9d3a595ea0fbf532602c3941b0a18db)) +* fb pixel test case refactor ([#3075](https://github.com/rudderlabs/rudder-transformer/issues/3075)) ([cff7d1c](https://github.com/rudderlabs/rudder-transformer/commit/cff7d1c4578087a37614c0ef4529058481873479)) +* fixed 500 status for algolia dontBatch ([#3178](https://github.com/rudderlabs/rudder-transformer/issues/3178)) ([6330888](https://github.com/rudderlabs/rudder-transformer/commit/6330888ad5c67e3a800037b56501fc08da09e4d1)) +* label not present in prometheus metrics ([#3176](https://github.com/rudderlabs/rudder-transformer/issues/3176)) ([01d460c](https://github.com/rudderlabs/rudder-transformer/commit/01d460c3edaf39b35c4686516c9e9140be46aa5e)) +* metadata structure correction ([#3119](https://github.com/rudderlabs/rudder-transformer/issues/3119)) ([8351b5c](https://github.com/rudderlabs/rudder-transformer/commit/8351b5cbbf81bbc14b2f884feaae4ad3ca59a39a)) +* one_signal: Encode external_id in endpoint ([#3140](https://github.com/rudderlabs/rudder-transformer/issues/3140)) ([8a20886](https://github.com/rudderlabs/rudder-transformer/commit/8a2088608d6da4b35bbb506db2fc3df1e4d41f3b)) +* prepare-for-staging-deploy.yml ([afb2f45](https://github.com/rudderlabs/rudder-transformer/commit/afb2f450ddee0522e802327dce68ac33a04c9639)) +* prepare-for-staging-deploy.yml ([05ffe82](https://github.com/rudderlabs/rudder-transformer/commit/05ffe820e5c5a3b346f39c268dd49fca47568461)) +* rakuten: sync property mapping sourcekeys to rudderstack standard spec ([#3129](https://github.com/rudderlabs/rudder-transformer/issues/3129)) ([2ebff95](https://github.com/rudderlabs/rudder-transformer/commit/2ebff956ff2aa74b008a8de832a31d8774d2d47e)) +* reddit revenue mapping for floating point values ([#3118](https://github.com/rudderlabs/rudder-transformer/issues/3118)) ([41f4078](https://github.com/rudderlabs/rudder-transformer/commit/41f4078011ef54334bb9ecc11a7b2ccc8831a4aa)) +* release action git ([#3166](https://github.com/rudderlabs/rudder-transformer/issues/3166)) ([dff7eb9](https://github.com/rudderlabs/rudder-transformer/commit/dff7eb9b8072016a16e7083c60507a9d03302f17)) +* release fix feat, bug order ([#3165](https://github.com/rudderlabs/rudder-transformer/issues/3165)) ([17da0a9](https://github.com/rudderlabs/rudder-transformer/commit/17da0a9cd2efb7b3ae061db081c737cb38d30df2)) +* send proper status to server in cm360 ([#3127](https://github.com/rudderlabs/rudder-transformer/issues/3127)) ([229ce47](https://github.com/rudderlabs/rudder-transformer/commit/229ce473af1ddd62d946bea1b018c882b142a5ef)) +* typo ([650911e](https://github.com/rudderlabs/rudder-transformer/commit/650911e44c5c99f346f4bcfd8145fcd6993d7759)) +* upload js coverage to codecov ([#3179](https://github.com/rudderlabs/rudder-transformer/issues/3179)) ([d2eba21](https://github.com/rudderlabs/rudder-transformer/commit/d2eba21191dc4f7b610414158af68e5533016014)) +* version deprecation failure false positive ([#3104](https://github.com/rudderlabs/rudder-transformer/issues/3104)) ([657b780](https://github.com/rudderlabs/rudder-transformer/commit/657b7805eb01da25a007d978198d5debf03917fd)) + ## [1.58.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.58.0) (2024-03-04) diff --git a/package-lock.json b/package-lock.json index d708860537..63f19b12c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.58.0", + "version": "1.59.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.58.0", + "version": "1.59.0", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", diff --git a/package.json b/package.json index ec3ffbf4e6..51b8b43a54 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.58.0", + "version": "1.59.0", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { From ac7e8879fcd230dae09f757a759fb42e4cdc09d0 Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Mon, 18 Mar 2024 16:00:25 +0530 Subject: [PATCH 32/35] fix: update correct staging deployment file (#3189) --- .github/workflows/prepare-for-staging-deploy.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/prepare-for-staging-deploy.yml b/.github/workflows/prepare-for-staging-deploy.yml index 1bd7e276f4..3e0b3aac19 100644 --- a/.github/workflows/prepare-for-staging-deploy.yml +++ b/.github/workflows/prepare-for-staging-deploy.yml @@ -112,9 +112,9 @@ jobs: yq eval -i ".user-transformer.image.tag=\"$TAG_NAME\"" staging.yaml git add staging.yaml - cd ../../../../config-be-rudder-transformer - yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" values.staging.yaml - yq eval -i ".config-be-user-transformer.image.tag=\"$TAG_NAME\"" values.staging.yaml + cd ../../../../config-be-rudder-transformer/environment/staging + yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" base.yaml + yq eval -i ".config-be-user-transformer.image.tag=\"$TAG_NAME\"" base.yaml git add values.staging.yaml git commit -m "chore: upgrade staging env transformers to \"$TAG_NAME\"" From 750fe8f73b153601e3c503b611d617236efb39a6 Mon Sep 17 00:00:00 2001 From: Gauravudia <60897972+Gauravudia@users.noreply.github.com> Date: Mon, 18 Mar 2024 18:18:58 +0530 Subject: [PATCH 33/35] chore: add event validation for movable ink destination (#3190) chore: add validation for movable ink destination --- .../movable_ink/procWorkflow.yaml | 1 + src/cdk/v2/destinations/movable_ink/utils.js | 21 +++++ .../movable_ink/processor/validation.ts | 86 +++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 src/cdk/v2/destinations/movable_ink/utils.js diff --git a/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml b/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml index 25270058c5..43dbb3cbce 100644 --- a/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml +++ b/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml @@ -33,6 +33,7 @@ steps: ); $.assert(userId ?? email ?? .message.anonymousId, "Either one of userId or email or anonymousId is required. Aborting"); + $.validateEventPayload(.message); - name: preparePayload description: Prepare payload for identify and track. This payload schema needs to be configured in the Movable Ink dashboard. Movable Ink will discard any additional fields from the input payload. diff --git a/src/cdk/v2/destinations/movable_ink/utils.js b/src/cdk/v2/destinations/movable_ink/utils.js new file mode 100644 index 0000000000..04d7046b1a --- /dev/null +++ b/src/cdk/v2/destinations/movable_ink/utils.js @@ -0,0 +1,21 @@ +const { InstrumentationError } = require('@rudderstack/integrations-lib'); + +const validateEventPayload = (message) => { + const { event } = message; + const { properties } = message; + if (event === 'Products Searched' && !properties?.query) { + throw new InstrumentationError("Missing 'query' property in properties. Aborting"); + } + + if ( + (event === 'Product Added' || + event === 'Product Removed' || + event === 'Product Viewed' || + event === 'Category Viewed') && + !properties?.product_id + ) { + throw new InstrumentationError("Missing 'product_id' property in properties. Aborting"); + } +}; + +module.exports = { validateEventPayload }; diff --git a/test/integrations/destinations/movable_ink/processor/validation.ts b/test/integrations/destinations/movable_ink/processor/validation.ts index f9f6c6a927..ab6b123eb7 100644 --- a/test/integrations/destinations/movable_ink/processor/validation.ts +++ b/test/integrations/destinations/movable_ink/processor/validation.ts @@ -128,4 +128,90 @@ export const validation: ProcessorTestData[] = [ }, }, }, + { + id: 'MovableInk-validation-test-4', + name: destType, + description: "Products Searched event - Missing 'query' property", + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + userId: 'user123', + integrations: { + All: true, + }, + event: 'Products Searched', + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + "Missing 'query' property in properties. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Missing 'query' property in properties. Aborting", + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'MovableInk-validation-test-5', + name: destType, + description: "Products Added event - Missing 'product_id' property", + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + userId: 'user123', + integrations: { + All: true, + }, + event: 'Product Added', + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + "Missing 'product_id' property in properties. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Missing 'product_id' property in properties. Aborting", + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, ]; From 5876441004da398c635baca393c9acfb99794704 Mon Sep 17 00:00:00 2001 From: sandeepdigumarty Date: Tue, 19 Mar 2024 14:45:27 +0530 Subject: [PATCH 34/35] chore: updated CHANGELOG --- CHANGELOG.md | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7390317ab5..b624ed9ef7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,50 +8,27 @@ All notable changes to this project will be documented in this file. See [standa ### Features * add Koala destination ([#3122](https://github.com/rudderlabs/rudder-transformer/issues/3122)) ([1ca039d](https://github.com/rudderlabs/rudder-transformer/commit/1ca039d64ebb1a18a0fc6b78ed5ee08528ad6b48)) -* add support for interaction events in sfmc ([#3109](https://github.com/rudderlabs/rudder-transformer/issues/3109)) ([0486049](https://github.com/rudderlabs/rudder-transformer/commit/0486049ba2ad96b50d8f29e96b46b96a8a5c9f76)) -* add support of ([c0ad214](https://github.com/rudderlabs/rudder-transformer/commit/c0ad21463981ef66154c8157083924f76825762d)) -* add support of custom page/screen event name in mixpanel ([#3098](https://github.com/rudderlabs/rudder-transformer/issues/3098)) ([0eb2393](https://github.com/rudderlabs/rudder-transformer/commit/0eb2393939fba2452ef7f07a1d149d87f18290c3)) * add support of skip_user_properties_sync on Amplitude ([#3181](https://github.com/rudderlabs/rudder-transformer/issues/3181)) ([5e4ddbd](https://github.com/rudderlabs/rudder-transformer/commit/5e4ddbd8a591341a581a5721505d6dcb010f2eec)) * adding zod validations ([#3066](https://github.com/rudderlabs/rudder-transformer/issues/3066)) ([325433b](https://github.com/rudderlabs/rudder-transformer/commit/325433b9188c8d1dbe740c7e193cdc2e58fdd751)) -* consent mode support for google adwords remarketing list ([#3143](https://github.com/rudderlabs/rudder-transformer/issues/3143)) ([7532c90](https://github.com/rudderlabs/rudder-transformer/commit/7532c90d7e1feac00f12961c56da18757010f44a)) -* **facebook:** update content_type mapping logic for fb pixel and fb conversions ([#3113](https://github.com/rudderlabs/rudder-transformer/issues/3113)) ([aea417c](https://github.com/rudderlabs/rudder-transformer/commit/aea417cd2691547399010c034cadbc5db6b0c6ee)) -* klaviyo profile mapping ([#3105](https://github.com/rudderlabs/rudder-transformer/issues/3105)) ([2761786](https://github.com/rudderlabs/rudder-transformer/commit/2761786ff3fc99ed6d4d3b7a6c2400226b1cfb12)) * onboard destination movable ink ([#3167](https://github.com/rudderlabs/rudder-transformer/issues/3167)) ([7018b1e](https://github.com/rudderlabs/rudder-transformer/commit/7018b1e5e7f37ae177191c5ecf3a71cfe2f3d147)) -* onboard new destination ninetailed ([#3106](https://github.com/rudderlabs/rudder-transformer/issues/3106)) ([0e2588e](https://github.com/rudderlabs/rudder-transformer/commit/0e2588ecd87f3b2c6877a099aa1cbf2d5325966c)) -* update proxy data type for response handler input ([7d6ea12](https://github.com/rudderlabs/rudder-transformer/commit/7d6ea123e08b793a87f35290e740cbef547c3862)) -* update proxy tests for cm360 ([9dd8625](https://github.com/rudderlabs/rudder-transformer/commit/9dd862540cc8e4e56b9bc638cc1da62e5f19c45f)) * update proxy tests for cm360 ([#3039](https://github.com/rudderlabs/rudder-transformer/issues/3039)) ([0504ffa](https://github.com/rudderlabs/rudder-transformer/commit/0504ffa898956f5b61771fb32ecfd0e0bf15248f)) -* update proxy v1 test cases ([b1327eb](https://github.com/rudderlabs/rudder-transformer/commit/b1327ebdb049163b3c5f046cb4605518e99481f3)) * use dontBatch directive in algolia ([#3169](https://github.com/rudderlabs/rudder-transformer/issues/3169)) ([916aaec](https://github.com/rudderlabs/rudder-transformer/commit/916aaecb1939160620d5fd3c4c0c0e33f2a371b2)) ### Bug Fixes -* add error handling for tiktok ads ([#3144](https://github.com/rudderlabs/rudder-transformer/issues/3144)) ([e93e47f](https://github.com/rudderlabs/rudder-transformer/commit/e93e47f33e098104fb532916932fe38bbfeaa4a1)) -* **algolia:** added check for objectIds or filters to be non empty ([#3126](https://github.com/rudderlabs/rudder-transformer/issues/3126)) ([d619c97](https://github.com/rudderlabs/rudder-transformer/commit/d619c9769cd270cb2d16dad0865683ff4beb2d19)) * am formatting issues ([4653b74](https://github.com/rudderlabs/rudder-transformer/commit/4653b74522cc917230c211ce1df1b57e8a607ad7)) -* api contract for v1 proxy ([76e0284](https://github.com/rudderlabs/rudder-transformer/commit/76e02848c58a6630c36f724dc4ccbac3d29a8007)) * api contract for v1 proxy ([#3049](https://github.com/rudderlabs/rudder-transformer/issues/3049)) ([93947db](https://github.com/rudderlabs/rudder-transformer/commit/93947db35cdaf1ca7ed87ec5f73567754af312ab)) -* clevertap remove stringification of array object properties ([#3048](https://github.com/rudderlabs/rudder-transformer/issues/3048)) ([69e43b6](https://github.com/rudderlabs/rudder-transformer/commit/69e43b6ffadeaec87b7440da34a341890ceba252)) -* convert to string from null in hs ([#3136](https://github.com/rudderlabs/rudder-transformer/issues/3136)) ([75e9f46](https://github.com/rudderlabs/rudder-transformer/commit/75e9f462b0ff9b9a8abab3c78dc7d147926e9e5e)) -* email mapping for clevertap ([c1b3736](https://github.com/rudderlabs/rudder-transformer/commit/c1b3736ab60c9582bdf1c4b07a761976de0da16f)) * email mapping for clevertap ([#3173](https://github.com/rudderlabs/rudder-transformer/issues/3173)) ([04eab92](https://github.com/rudderlabs/rudder-transformer/commit/04eab92e1c383f9e8cdd5c845530a42a0af2932a)) -* event fix and added utility ([#3142](https://github.com/rudderlabs/rudder-transformer/issues/3142)) ([9b705b7](https://github.com/rudderlabs/rudder-transformer/commit/9b705b71a9d3a595ea0fbf532602c3941b0a18db)) * fb pixel test case refactor ([#3075](https://github.com/rudderlabs/rudder-transformer/issues/3075)) ([cff7d1c](https://github.com/rudderlabs/rudder-transformer/commit/cff7d1c4578087a37614c0ef4529058481873479)) * fixed 500 status for algolia dontBatch ([#3178](https://github.com/rudderlabs/rudder-transformer/issues/3178)) ([6330888](https://github.com/rudderlabs/rudder-transformer/commit/6330888ad5c67e3a800037b56501fc08da09e4d1)) * label not present in prometheus metrics ([#3176](https://github.com/rudderlabs/rudder-transformer/issues/3176)) ([01d460c](https://github.com/rudderlabs/rudder-transformer/commit/01d460c3edaf39b35c4686516c9e9140be46aa5e)) -* metadata structure correction ([#3119](https://github.com/rudderlabs/rudder-transformer/issues/3119)) ([8351b5c](https://github.com/rudderlabs/rudder-transformer/commit/8351b5cbbf81bbc14b2f884feaae4ad3ca59a39a)) -* one_signal: Encode external_id in endpoint ([#3140](https://github.com/rudderlabs/rudder-transformer/issues/3140)) ([8a20886](https://github.com/rudderlabs/rudder-transformer/commit/8a2088608d6da4b35bbb506db2fc3df1e4d41f3b)) * prepare-for-staging-deploy.yml ([afb2f45](https://github.com/rudderlabs/rudder-transformer/commit/afb2f450ddee0522e802327dce68ac33a04c9639)) * prepare-for-staging-deploy.yml ([05ffe82](https://github.com/rudderlabs/rudder-transformer/commit/05ffe820e5c5a3b346f39c268dd49fca47568461)) -* rakuten: sync property mapping sourcekeys to rudderstack standard spec ([#3129](https://github.com/rudderlabs/rudder-transformer/issues/3129)) ([2ebff95](https://github.com/rudderlabs/rudder-transformer/commit/2ebff956ff2aa74b008a8de832a31d8774d2d47e)) -* reddit revenue mapping for floating point values ([#3118](https://github.com/rudderlabs/rudder-transformer/issues/3118)) ([41f4078](https://github.com/rudderlabs/rudder-transformer/commit/41f4078011ef54334bb9ecc11a7b2ccc8831a4aa)) * release action git ([#3166](https://github.com/rudderlabs/rudder-transformer/issues/3166)) ([dff7eb9](https://github.com/rudderlabs/rudder-transformer/commit/dff7eb9b8072016a16e7083c60507a9d03302f17)) * release fix feat, bug order ([#3165](https://github.com/rudderlabs/rudder-transformer/issues/3165)) ([17da0a9](https://github.com/rudderlabs/rudder-transformer/commit/17da0a9cd2efb7b3ae061db081c737cb38d30df2)) * send proper status to server in cm360 ([#3127](https://github.com/rudderlabs/rudder-transformer/issues/3127)) ([229ce47](https://github.com/rudderlabs/rudder-transformer/commit/229ce473af1ddd62d946bea1b018c882b142a5ef)) -* typo ([650911e](https://github.com/rudderlabs/rudder-transformer/commit/650911e44c5c99f346f4bcfd8145fcd6993d7759)) * upload js coverage to codecov ([#3179](https://github.com/rudderlabs/rudder-transformer/issues/3179)) ([d2eba21](https://github.com/rudderlabs/rudder-transformer/commit/d2eba21191dc4f7b610414158af68e5533016014)) -* version deprecation failure false positive ([#3104](https://github.com/rudderlabs/rudder-transformer/issues/3104)) ([657b780](https://github.com/rudderlabs/rudder-transformer/commit/657b7805eb01da25a007d978198d5debf03917fd)) ## [1.58.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.58.0) (2024-03-04) From 05ebcc8269667cb2be9437d9779aec7328fe2bda Mon Sep 17 00:00:00 2001 From: sandeepdigumarty Date: Tue, 19 Mar 2024 15:42:38 +0530 Subject: [PATCH 35/35] chore: updated CHANGELOG --- CHANGELOG.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b624ed9ef7..d3f2b57d19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,18 +17,12 @@ All notable changes to this project will be documented in this file. See [standa ### Bug Fixes -* am formatting issues ([4653b74](https://github.com/rudderlabs/rudder-transformer/commit/4653b74522cc917230c211ce1df1b57e8a607ad7)) * api contract for v1 proxy ([#3049](https://github.com/rudderlabs/rudder-transformer/issues/3049)) ([93947db](https://github.com/rudderlabs/rudder-transformer/commit/93947db35cdaf1ca7ed87ec5f73567754af312ab)) * email mapping for clevertap ([#3173](https://github.com/rudderlabs/rudder-transformer/issues/3173)) ([04eab92](https://github.com/rudderlabs/rudder-transformer/commit/04eab92e1c383f9e8cdd5c845530a42a0af2932a)) * fb pixel test case refactor ([#3075](https://github.com/rudderlabs/rudder-transformer/issues/3075)) ([cff7d1c](https://github.com/rudderlabs/rudder-transformer/commit/cff7d1c4578087a37614c0ef4529058481873479)) * fixed 500 status for algolia dontBatch ([#3178](https://github.com/rudderlabs/rudder-transformer/issues/3178)) ([6330888](https://github.com/rudderlabs/rudder-transformer/commit/6330888ad5c67e3a800037b56501fc08da09e4d1)) * label not present in prometheus metrics ([#3176](https://github.com/rudderlabs/rudder-transformer/issues/3176)) ([01d460c](https://github.com/rudderlabs/rudder-transformer/commit/01d460c3edaf39b35c4686516c9e9140be46aa5e)) -* prepare-for-staging-deploy.yml ([afb2f45](https://github.com/rudderlabs/rudder-transformer/commit/afb2f450ddee0522e802327dce68ac33a04c9639)) -* prepare-for-staging-deploy.yml ([05ffe82](https://github.com/rudderlabs/rudder-transformer/commit/05ffe820e5c5a3b346f39c268dd49fca47568461)) -* release action git ([#3166](https://github.com/rudderlabs/rudder-transformer/issues/3166)) ([dff7eb9](https://github.com/rudderlabs/rudder-transformer/commit/dff7eb9b8072016a16e7083c60507a9d03302f17)) -* release fix feat, bug order ([#3165](https://github.com/rudderlabs/rudder-transformer/issues/3165)) ([17da0a9](https://github.com/rudderlabs/rudder-transformer/commit/17da0a9cd2efb7b3ae061db081c737cb38d30df2)) * send proper status to server in cm360 ([#3127](https://github.com/rudderlabs/rudder-transformer/issues/3127)) ([229ce47](https://github.com/rudderlabs/rudder-transformer/commit/229ce473af1ddd62d946bea1b018c882b142a5ef)) -* upload js coverage to codecov ([#3179](https://github.com/rudderlabs/rudder-transformer/issues/3179)) ([d2eba21](https://github.com/rudderlabs/rudder-transformer/commit/d2eba21191dc4f7b610414158af68e5533016014)) ## [1.58.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.58.0) (2024-03-04)