From 096d00882fc7f56cbb25e2a26126b58637c36ba8 Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Thu, 14 Nov 2024 10:42:59 +0530 Subject: [PATCH 01/21] fix: adding transformer proxy for iterable --- src/v0/destinations/iterable/config.js | 19 +++ src/v0/destinations/iterable/util.js | 48 ++++++ src/v0/destinations/iterable/util.test.js | 147 ++++++++++++++++++ .../destinations/iterable/networkHandler.js | 89 +++++++++++ 4 files changed, 303 insertions(+) create mode 100644 src/v1/destinations/iterable/networkHandler.js diff --git a/src/v0/destinations/iterable/config.js b/src/v0/destinations/iterable/config.js index f74fdb4975e..ec654337fc9 100644 --- a/src/v0/destinations/iterable/config.js +++ b/src/v0/destinations/iterable/config.js @@ -74,6 +74,24 @@ const IDENTIFY_BATCH_ENDPOINT = 'https://api.iterable.com/api/users/bulkUpdate'; const TRACK_MAX_BATCH_SIZE = 8000; const TRACK_BATCH_ENDPOINT = 'https://api.iterable.com/api/events/trackBulk'; +const API_RESPONSE_PATHS = [ + 'invalidEmails', + 'invalidUserIds', + 'disallowedEventNames', + 'filteredOutFields', + 'createdFields', + 'failedUpdates.invalidEmails', + 'failedUpdates.invalidUserIds', + 'failedUpdates.notFoundEmails', + 'failedUpdates.notFoundUserIds', + 'failedUpdates.forgottenEmails', + 'failedUpdates.forgottenUserIds', + 'failedUpdates.conflictUserIds', + 'failedUpdates.conflictEmails', + 'failedUpdates.invalidDataUserIds', + 'failedUpdates.invalidDataEmails', +]; + module.exports = { mappingConfig, ConfigCategory, @@ -82,4 +100,5 @@ module.exports = { IDENTIFY_MAX_BATCH_SIZE, IDENTIFY_BATCH_ENDPOINT, IDENTIFY_MAX_BODY_SIZE_IN_BYTES, + API_RESPONSE_PATHS, }; diff --git a/src/v0/destinations/iterable/util.js b/src/v0/destinations/iterable/util.js index 7c1509c2b79..e42218b44bd 100644 --- a/src/v0/destinations/iterable/util.js +++ b/src/v0/destinations/iterable/util.js @@ -18,6 +18,7 @@ const { IDENTIFY_MAX_BATCH_SIZE, IDENTIFY_BATCH_ENDPOINT, IDENTIFY_MAX_BODY_SIZE_IN_BYTES, + API_RESPONSE_PATHS, } = require('./config'); const { JSON_MIME_TYPE } = require('../../util/constant'); const { EventType, MappedToDestinationKey } = require('../../../constants'); @@ -739,6 +740,52 @@ const filterEventsAndPrepareBatchRequests = (transformedEvents) => { return prepareBatchRequests(filteredEvents); }; +function checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse) { + const { failCount } = destinationResponse.response.data; + + if (failCount === 0) { + return { isAbortable: false, errorMsg: '' }; + } + + const eventValues = new Set( + [ + event.email, + event.userId, + event.eventName, + event.id, + event.createdAt, + event.campaignId, + event.templateId, + event.createNewFields, + ...(event.dataFields ? Object.values(event.dataFields) : []), + ].filter((value) => value !== undefined), + ); + + const matchingPath = API_RESPONSE_PATHS.find((path) => { + const responseArray = path + .split('.') + .reduce((obj, key) => obj?.[key], destinationResponse.response.data); + + return Array.isArray(responseArray) && responseArray.some((value) => eventValues.has(value)); + }); + + if (matchingPath) { + const responseArray = matchingPath + .split('.') + .reduce((obj, key) => obj?.[key], destinationResponse.response.data); + + const matchingValue = responseArray.find((value) => eventValues.has(value)); + + return { + isAbortable: true, + errorMsg: `Request failed for value "${matchingValue}" because it is "${matchingPath}".`, + }; + } + + // Return false and an empty error message if no error is found + return { isAbortable: false, errorMsg: '' }; +} + module.exports = { getCatalogEndpoint, hasMultipleResponses, @@ -753,4 +800,5 @@ module.exports = { filterEventsAndPrepareBatchRequests, registerDeviceTokenEventPayloadBuilder, registerBrowserTokenEventPayloadBuilder, + checkIfEventIsAbortableAndExtractErrorMessage, }; diff --git a/src/v0/destinations/iterable/util.test.js b/src/v0/destinations/iterable/util.test.js index 098960ac775..24a5bc19fc3 100644 --- a/src/v0/destinations/iterable/util.test.js +++ b/src/v0/destinations/iterable/util.test.js @@ -7,6 +7,7 @@ const { updateUserEventPayloadBuilder, registerDeviceTokenEventPayloadBuilder, registerBrowserTokenEventPayloadBuilder, + checkIfEventIsAbortableAndExtractErrorMessage, } = require('./util'); const { ConfigCategory } = require('./config'); @@ -799,4 +800,150 @@ describe('iterable utils test', () => { ); }); }); + describe('checkIfEventIsAbortableAndExtractErrorMessage', () => { + // Returns non-abortable and empty error message when failCount is 0 + it('should return non-abortable and empty error message when failCount is 0', () => { + const event = { + email: 'test@example.com', + userId: 'user123', + eventName: 'testEvent', + }; + const destinationResponse = { + response: { + data: { + failCount: 0, + }, + }, + }; + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + expect(result).toEqual({ isAbortable: false, errorMsg: '' }); + }); + + // Handles undefined or null event fields gracefully + it('should handle undefined or null event fields gracefully', () => { + const event = { + email: null, + userId: undefined, + eventName: 'testEvent', + }; + const destinationResponse = { + response: { + data: { + failCount: 1, + invalidEmails: ['test@example.com'], + }, + }, + }; + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + expect(result).toEqual({ isAbortable: false, errorMsg: '' }); + }); + + // Correctly identifies abortable events with matching values in API_RESPONSE_PATHS + it('should return abortable true when event has matching values in API_RESPONSE_PATHS', () => { + const event = { + email: 'test@example.com', + userId: 'user123', + eventName: 'purchase', + id: 'event123', + createdAt: '2023-10-01T00:00:00Z', + campaignId: 'campaign123', + templateId: 'template123', + createNewFields: true, + dataFields: { field1: 'value1' }, + }; + + const destinationResponse = { + response: { + data: { + failCount: 1, + invalidEmails: ['test@example.com'], + }, + }, + }; + + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + + expect(result.isAbortable).toBe(true); + expect(result.errorMsg).toBe( + 'Request failed for value "test@example.com" because it is "invalidEmails".', + ); + }); + + // Handles events with all expected fields present + it('should handle events with all expected fields present and return non-abortable when no match', () => { + const event = { + email: 'test@example.com', + userId: 'user123', + eventName: 'purchase', + id: 'event123', + createdAt: '2023-10-01T00:00:00Z', + campaignId: 'campaign123', + templateId: 'template123', + createNewFields: true, + dataFields: { field1: 'value1' }, + }; + + const destinationResponse = { + response: { + data: { + failCount: 1, + invalidEmails: ['another@example.com'], + }, + }, + }; + + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + + expect(result.isAbortable).toBe(false); + expect(result.errorMsg).toBe(''); + }); + + // Returns appropriate error message for abortable events + it('should return an error message when the event is abortable', () => { + const event = { + email: 'test@example.com', + userId: 'user123', + eventName: 'purchase', + }; + const destinationResponse = { + response: { + data: { + failCount: 1, + invalidEmails: ['test@example.com'], + }, + }, + }; + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + expect(result).toEqual({ + isAbortable: true, + errorMsg: 'Request failed for value "test@example.com" because it is "invalidEmails".', + }); + }); + + // Processes events with additional dataFields correctly + it('should process events with additional dataFields correctly', () => { + const event = { + email: 'test@example.com', + userId: 'user123', + eventName: 'purchase', + dataFields: { customField1: 'value1', customField2: 'value2' }, + }; + const destinationResponse = { + response: { + data: { + failCount: 1, + failedUpdates: { + invalidDataEmails: ['value1'], + }, + }, + }, + }; + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + expect(result).toEqual({ + isAbortable: true, + errorMsg: + 'Request failed for value "value1" because it is "failedUpdates.invalidDataEmails".', + }); + }); + }); }); diff --git a/src/v1/destinations/iterable/networkHandler.js b/src/v1/destinations/iterable/networkHandler.js new file mode 100644 index 00000000000..b107dfe4efa --- /dev/null +++ b/src/v1/destinations/iterable/networkHandler.js @@ -0,0 +1,89 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +const { TransformerProxyError } = require('../../../v0/util/errorTypes'); +const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); +const { isHttpStatusSuccess } = require('../../../v0/util/index'); + +const { + processAxiosResponse, + getDynamicErrorType, +} = require('../../../adapters/utils/networkUtils'); +const tags = require('../../../v0/util/tags'); +const { + checkIfEventIsAbortableAndExtractErrorMessage, +} = require('../../../v0/destinations/iterable/util'); + +const responseHandler = (responseParams) => { + const { destinationResponse, rudderJobMetadata, destinationRequest } = responseParams; + const message = `[ITERABLE Response V1 Handler] - Request Processed Successfully`; + let responseWithIndividualEvents = []; + const { response, status } = destinationResponse; + + if (!isHttpStatusSuccess(status)) { + const errorMessage = JSON.stringify(response.params) || 'unknown error format'; + responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ + statusCode: status, + metadata, + error: errorMessage, + })); + throw new TransformerProxyError( + `ITERABLE: Error transformer proxy v1 during ITERABLE response transformation. ${errorMessage}`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); + } + + if (isHttpStatusSuccess(status)) { + // check for Partial Event failures and Successes + // eslint-disable-next-line @typescript-eslint/naming-convention + const { events } = destinationRequest.body.JSON; + const finalData = events; + finalData.forEach((event, idx) => { + const proxyOutput = { + statusCode: 200, + metadata: rudderJobMetadata[idx], + error: 'success', + }; + // update status of partial event if abortable + const { isAbortable, errorMsg } = checkIfEventIsAbortableAndExtractErrorMessage( + event, + destinationResponse, + ); + if (isAbortable) { + proxyOutput.statusCode = 400; + proxyOutput.error = errorMsg; + } + responseWithIndividualEvents.push(proxyOutput); + }); + return { + status, + message, + destinationResponse, + response: responseWithIndividualEvents, + }; + } + + throw new TransformerProxyError( + `ITERABLE: Error transformer proxy v1 during ITERABLE response transformation`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); +}; + +function networkHandler() { + this.prepareProxy = prepareProxyRequest; + this.proxy = proxyRequest; + this.processAxiosResponse = processAxiosResponse; + this.responseHandler = responseHandler; +} + +module.exports = { networkHandler, checkIfEventIsAbortableAndExtractErrorMessage }; From 2b34182068f8fd6612a9a4b21c523ecd17fc5f84 Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Thu, 14 Nov 2024 18:57:16 +0530 Subject: [PATCH 02/21] fix: adding component test cases --- src/v0/destinations/iterable/config.js | 2 - src/v0/destinations/iterable/util.js | 27 +- src/v0/destinations/iterable/util.test.js | 78 +--- .../destinations/iterable/networkHandler.js | 4 +- .../iterable/dataDelivery/business.ts | 434 ++++++++++++++++++ .../iterable/dataDelivery/data.ts | 3 + .../iterable/dataDelivery/network.ts | 375 +++++++++++++++ 7 files changed, 847 insertions(+), 76 deletions(-) create mode 100644 test/integrations/destinations/iterable/dataDelivery/business.ts create mode 100644 test/integrations/destinations/iterable/dataDelivery/data.ts create mode 100644 test/integrations/destinations/iterable/dataDelivery/network.ts diff --git a/src/v0/destinations/iterable/config.js b/src/v0/destinations/iterable/config.js index a91a471adb3..a2bce4f9d3f 100644 --- a/src/v0/destinations/iterable/config.js +++ b/src/v0/destinations/iterable/config.js @@ -85,8 +85,6 @@ const API_RESPONSE_PATHS = [ 'invalidEmails', 'invalidUserIds', 'disallowedEventNames', - 'filteredOutFields', - 'createdFields', 'failedUpdates.invalidEmails', 'failedUpdates.invalidUserIds', 'failedUpdates.notFoundEmails', diff --git a/src/v0/destinations/iterable/util.js b/src/v0/destinations/iterable/util.js index 7777f235a5a..ba0ce717316 100644 --- a/src/v0/destinations/iterable/util.js +++ b/src/v0/destinations/iterable/util.js @@ -745,16 +745,35 @@ const filterEventsAndPrepareBatchRequests = (transformedEvents) => { return prepareBatchRequests(filteredEvents); }; +/** + * Determines if an event should be aborted based on the response from a destination + * and extracts an error message if applicable. + * ref: + * 1) https://api.iterable.com/api/docs#users_updateEmail + * 2) https://api.iterable.com/api/docs#events_track + * 3) https://api.iterable.com/api/docs#users_bulkUpdateUser + * 4) https://api.iterable.com/api/docs#events_trackBulk + * + * @param {Object} event - The event object containing various event properties. + * @param {Object} destinationResponse - The response object from the destination. + * @returns {Object} An object containing a boolean `isAbortable` indicating if the event + * should be aborted, and an `errorMsg` string with the error message if applicable. + */ function checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse) { - const { failCount } = destinationResponse.response.data; + const { failCount } = destinationResponse.response; if (failCount === 0) { return { isAbortable: false, errorMsg: '' }; } + // Flatten dataFields values into a single array + const dataFieldsValues = event.dataFields ? Object.values(event.dataFields).flat() : []; + const eventValues = new Set( [ event.email, + event.preferUserId, + event.mergeNestedObjects, event.userId, event.eventName, event.id, @@ -762,14 +781,14 @@ function checkIfEventIsAbortableAndExtractErrorMessage(event, destinationRespons event.campaignId, event.templateId, event.createNewFields, - ...(event.dataFields ? Object.values(event.dataFields) : []), + ...dataFieldsValues, // Spread the flattened dataFields values ].filter((value) => value !== undefined), ); const matchingPath = API_RESPONSE_PATHS.find((path) => { const responseArray = path .split('.') - .reduce((obj, key) => obj?.[key], destinationResponse.response.data); + .reduce((obj, key) => obj?.[key], destinationResponse.response); return Array.isArray(responseArray) && responseArray.some((value) => eventValues.has(value)); }); @@ -777,7 +796,7 @@ function checkIfEventIsAbortableAndExtractErrorMessage(event, destinationRespons if (matchingPath) { const responseArray = matchingPath .split('.') - .reduce((obj, key) => obj?.[key], destinationResponse.response.data); + .reduce((obj, key) => obj?.[key], destinationResponse.response); const matchingValue = responseArray.find((value) => eventValues.has(value)); diff --git a/src/v0/destinations/iterable/util.test.js b/src/v0/destinations/iterable/util.test.js index 24a5bc19fc3..52032a38dc3 100644 --- a/src/v0/destinations/iterable/util.test.js +++ b/src/v0/destinations/iterable/util.test.js @@ -810,11 +810,10 @@ describe('iterable utils test', () => { }; const destinationResponse = { response: { - data: { - failCount: 0, - }, + failCount: 0, }, }; + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); expect(result).toEqual({ isAbortable: false, errorMsg: '' }); }); @@ -828,47 +827,14 @@ describe('iterable utils test', () => { }; const destinationResponse = { response: { - data: { - failCount: 1, - invalidEmails: ['test@example.com'], - }, + failCount: 1, + invalidEmails: ['test@example.com'], }, }; const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); expect(result).toEqual({ isAbortable: false, errorMsg: '' }); }); - // Correctly identifies abortable events with matching values in API_RESPONSE_PATHS - it('should return abortable true when event has matching values in API_RESPONSE_PATHS', () => { - const event = { - email: 'test@example.com', - userId: 'user123', - eventName: 'purchase', - id: 'event123', - createdAt: '2023-10-01T00:00:00Z', - campaignId: 'campaign123', - templateId: 'template123', - createNewFields: true, - dataFields: { field1: 'value1' }, - }; - - const destinationResponse = { - response: { - data: { - failCount: 1, - invalidEmails: ['test@example.com'], - }, - }, - }; - - const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); - - expect(result.isAbortable).toBe(true); - expect(result.errorMsg).toBe( - 'Request failed for value "test@example.com" because it is "invalidEmails".', - ); - }); - // Handles events with all expected fields present it('should handle events with all expected fields present and return non-abortable when no match', () => { const event = { @@ -885,10 +851,8 @@ describe('iterable utils test', () => { const destinationResponse = { response: { - data: { - failCount: 1, - invalidEmails: ['another@example.com'], - }, + failCount: 1, + invalidEmails: ['another@example.com'], }, }; @@ -898,27 +862,7 @@ describe('iterable utils test', () => { expect(result.errorMsg).toBe(''); }); - // Returns appropriate error message for abortable events - it('should return an error message when the event is abortable', () => { - const event = { - email: 'test@example.com', - userId: 'user123', - eventName: 'purchase', - }; - const destinationResponse = { - response: { - data: { - failCount: 1, - invalidEmails: ['test@example.com'], - }, - }, - }; - const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); - expect(result).toEqual({ - isAbortable: true, - errorMsg: 'Request failed for value "test@example.com" because it is "invalidEmails".', - }); - }); + // Returns appropriate error message for abortable event // Processes events with additional dataFields correctly it('should process events with additional dataFields correctly', () => { @@ -930,11 +874,9 @@ describe('iterable utils test', () => { }; const destinationResponse = { response: { - data: { - failCount: 1, - failedUpdates: { - invalidDataEmails: ['value1'], - }, + failCount: 1, + failedUpdates: { + invalidDataEmails: ['value1'], }, }, }; diff --git a/src/v1/destinations/iterable/networkHandler.js b/src/v1/destinations/iterable/networkHandler.js index b107dfe4efa..3fde013580a 100644 --- a/src/v1/destinations/iterable/networkHandler.js +++ b/src/v1/destinations/iterable/networkHandler.js @@ -40,8 +40,8 @@ const responseHandler = (responseParams) => { if (isHttpStatusSuccess(status)) { // check for Partial Event failures and Successes // eslint-disable-next-line @typescript-eslint/naming-convention - const { events } = destinationRequest.body.JSON; - const finalData = events; + const { events, users } = destinationRequest.body.JSON; + const finalData = events || users; finalData.forEach((event, idx) => { const proxyOutput = { statusCode: 200, diff --git a/test/integrations/destinations/iterable/dataDelivery/business.ts b/test/integrations/destinations/iterable/dataDelivery/business.ts new file mode 100644 index 00000000000..3754bca6d18 --- /dev/null +++ b/test/integrations/destinations/iterable/dataDelivery/business.ts @@ -0,0 +1,434 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; +import { + correctIdentifyData, + correctTrackData, + headerBlockWithCorrectAccessToken, + partiallyCorrectIdentifyData, + partiallyCorrectTrackData, + wrongIdentifyData, + wrongTrackData, +} from './network'; + +export const statTags = { + destType: 'ITERABLE', + errorCategory: 'network', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', +}; + +export const metadata = [ + { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: false, + }, + { + jobId: 2, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: false, + }, +]; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'ITERABLE_v1_other_scenario_1', + name: 'iterable', + description: + '[Proxy v1 API] :: Scenario to test correct Payload Response Handling from Destination', + successCriteria: 'Should return 200 status code with success', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: correctTrackData, + headers: headerBlockWithCorrectAccessToken, + endpoint: 'https://api.iterable.com/api/events/trackBulk', + }, + metadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[ITERABLE Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + status: 200, + response: { + createdFields: [], + disallowedEventNames: [], + failCount: 0, + failedUpdates: { + forgottenEmails: [], + forgottenUserIds: [], + invalidEmails: [], + invalidUserIds: [], + notFoundEmails: [], + notFoundUserIds: [], + }, + filteredOutFields: [], + invalidEmails: [], + invalidUserIds: [], + successCount: 2, + }, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + { + statusCode: 200, + metadata: generateMetadata(2), + error: 'success', + }, + ], + }, + }, + }, + }, + }, + { + id: 'ITERABLE_v1_other_scenario_2', + name: 'iterable', + description: + '[Proxy v1 API] :: Scenario to test Malformed Payload Response Handling from Destination', + successCriteria: 'Should return 400 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: wrongTrackData, + headers: headerBlockWithCorrectAccessToken, + endpoint: 'https://api.iterable.com/api/events/trackBulk', + }, + metadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags, + message: + 'ITERABLE: Error transformer proxy v1 during ITERABLE response transformation. {"obj.events[1].createdAt":"Number value expected"}', + response: [ + { + statusCode: 400, + metadata: generateMetadata(1), + error: + '{"msg":"[/api/events/trackBulk] Invalid JSON body","code":"BadJsonBody","params":{"obj.events[1].createdAt":"Number value expected"}}', + }, + { + statusCode: 400, + metadata: generateMetadata(2), + error: + '{"msg":"[/api/events/trackBulk] Invalid JSON body","code":"BadJsonBody","params":{"obj.events[1].createdAt":"Number value expected"}}', + }, + ], + }, + }, + }, + }, + }, + { + id: 'ITERABLE_v1_other_scenario_3', + name: 'iterable', + description: + '[Proxy v1 API] :: Scenario to test partially successful Response Handling from Destination', + successCriteria: 'Should return 400 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: partiallyCorrectTrackData, + headers: headerBlockWithCorrectAccessToken, + endpoint: 'https://api.iterable.com/api/events/trackBulk', + }, + metadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[ITERABLE Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + status: 200, + response: { + successCount: 1, + failCount: 1, + invalidEmails: ['sayan'], + invalidUserIds: [], + disallowedEventNames: [], + filteredOutFields: [], + createdFields: [], + failedUpdates: { + invalidEmails: ['sayan'], + invalidUserIds: [], + notFoundEmails: [], + notFoundUserIds: [], + forgottenEmails: [], + forgottenUserIds: [], + }, + + status: 200, + }, + }, + response: [ + { + statusCode: 400, + metadata: generateMetadata(1), + error: 'Request failed for value "sayan" because it is "invalidEmails".', + }, + { + statusCode: 200, + metadata: generateMetadata(2), + error: 'success', + }, + ], + }, + }, + }, + }, + }, + { + id: 'ITERABLE_v1_other_scenario_1', + name: 'iterable', + description: + '[Proxy v1 API] :: Scenario to test correct identify Payload Response Handling from Destination', + successCriteria: 'Should return 200 status code with success', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: correctIdentifyData, + headers: headerBlockWithCorrectAccessToken, + endpoint: 'https://api.iterable.com/api/users/bulkUpdate', + }, + metadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[ITERABLE Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + status: 200, + response: { + createdFields: [], + noOpEmails: [], + noOpUserIds: [], + failCount: 0, + failedUpdates: { + conflictEmails: [], + conflictUserIds: [], + forgottenEmails: [], + forgottenUserIds: [], + invalidEmails: [], + invalidUserIds: [], + notFoundEmails: [], + notFoundUserIds: [], + invalidDataEmails: [], + invalidDataUserIds: [], + }, + filteredOutFields: [], + invalidEmails: [], + invalidUserIds: [], + successCount: 2, + }, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + { + statusCode: 200, + metadata: generateMetadata(2), + error: 'success', + }, + ], + }, + }, + }, + }, + }, + { + id: 'ITERABLE_v1_other_scenario_2', + name: 'iterable', + description: + '[Proxy v1 API] :: Scenario to test Malformed identify Payload Response Handling from Destination', + successCriteria: 'Should return 400 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: wrongIdentifyData, + headers: headerBlockWithCorrectAccessToken, + endpoint: 'https://api.iterable.com/api/users/bulkUpdate', + }, + metadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags, + message: + 'ITERABLE: Error transformer proxy v1 during ITERABLE response transformation. {"obj.users[1].preferUserId":"Boolean value expected"}', + response: [ + { + statusCode: 400, + metadata: generateMetadata(1), + error: + '{"msg":"[/api/users/bulkUpdate] Invalid JSON body","code":"BadJsonBody","params":{"obj.users[1].preferUserId":"Boolean value expected"}}', + }, + { + statusCode: 400, + metadata: generateMetadata(2), + error: + '{"msg":"[/api/users/bulkUpdate] Invalid JSON body","code":"BadJsonBody","params":{"obj.users[1].preferUserId":"Boolean value expected"}}', + }, + ], + }, + }, + }, + }, + }, + { + id: 'ITERABLE_v1_other_scenario_3', + name: 'iterable', + description: + '[Proxy v1 API] :: Scenario to test partially successful identify Response Handling from Destination', + successCriteria: 'Should return 400 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: partiallyCorrectIdentifyData, + headers: headerBlockWithCorrectAccessToken, + endpoint: 'https://api.iterable.com/api/users/bulkUpdate', + }, + metadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[ITERABLE Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + status: 200, + response: { + successCount: 1, + failCount: 1, + invalidEmails: ['shrouti'], + invalidUserIds: [], + noOpEmails: [], + noOpUserIds: [], + filteredOutFields: [], + createdFields: [], + failedUpdates: { + invalidEmails: ['shrouti'], + conflictEmails: [], + conflictUserIds: [], + invalidUserIds: [], + notFoundEmails: [], + notFoundUserIds: [], + forgottenEmails: [], + forgottenUserIds: [], + invalidDataEmails: [], + invalidDataUserIds: [], + }, + }, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + { + statusCode: 400, + metadata: generateMetadata(2), + error: 'Request failed for value "shrouti" because it is "invalidEmails".', + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/iterable/dataDelivery/data.ts b/test/integrations/destinations/iterable/dataDelivery/data.ts new file mode 100644 index 00000000000..fc969bb8e1a --- /dev/null +++ b/test/integrations/destinations/iterable/dataDelivery/data.ts @@ -0,0 +1,3 @@ +import { testScenariosForV1API } from './business'; + +export const data = [...testScenariosForV1API]; diff --git a/test/integrations/destinations/iterable/dataDelivery/network.ts b/test/integrations/destinations/iterable/dataDelivery/network.ts new file mode 100644 index 00000000000..e14d8a76d6d --- /dev/null +++ b/test/integrations/destinations/iterable/dataDelivery/network.ts @@ -0,0 +1,375 @@ +export const headerBlockWithCorrectAccessToken = { + 'Content-Type': 'application/json', + api_key: 'DUMMY_API_KEY', +}; + +export const headerBlockWithWrongAccessToken = { + 'Content-Type': 'application/json', + api_key: 'DUMMY_WRONG_API_KEY', +}; +export const correctTrackData = { + events: [ + { + dataFields: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', + }, + email: 'sayan@gmail.com', + userId: 'abcdeeeeeeeexxxx102', + eventName: 'Email Opened', + createdAt: 1598631966468, + }, + { + dataFields: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'pradip@gmail.com', + }, + email: 'pradip@gmail.com', + userId: 'abcdeeeeeeeexxxx102', + eventName: 'Email Opened', + createdAt: 1598631966468, + }, + ], +}; + +export const wrongTrackData = { + events: [ + { + dataFields: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', + }, + email: 'sayan', + userId: 'abcdeeeeeeeexxxx102', + eventName: 'Email Opened', + createdAt: 'abc', + }, + { + dataFields: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'pradip@gmail.com', + }, + email: 'pradip@gmail.com', + userId: 'abcdeeeeeeeexxxx102', + eventName: 'Email Opened', + createdAt: 1598631966468, + }, + ], +}; + +export const partiallyCorrectTrackData = { + events: [ + { + dataFields: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', + }, + email: 'sayan', + userId: 'abcdeeeeeeeexxxx102', + eventName: 'Email Opened', + createdAt: 1598631966468, + }, + { + dataFields: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'pradip@gmail.com', + }, + email: 'pradip@gmail.com', + userId: 'abcdeeeeeeeexxxx102', + eventName: 'Email Opened', + createdAt: 1598631966468, + }, + ], +}; + +export const correctIdentifyData = { + users: [ + { + email: 'manashi@website.com', + dataFields: { + city: 'Bangalore', + name: 'manashi', + email: 'manashi@website.com', + country: 'India', + }, + userId: 'abcdeeeeeeeexxxx102', + preferUserId: true, + mergeNestedObjects: true, + }, + { + email: 'shrouti@website.com', + dataFields: { + city: 'Bangalore', + name: 'shrouti', + email: 'shrouti@website.com', + country: 'India', + }, + userId: 'abcdeeeegggggxxxx102', + preferUserId: true, + mergeNestedObjects: true, + }, + ], +}; + +export const wrongIdentifyData = { + users: [ + { + email: 'manashi@website.com', + dataFields: { + city: 'Bangalore', + name: 'manashi', + email: 'manashi@website.com', + country: 'India', + }, + userId: 'abcdeeeeeeeexxxx102', + preferUserId: true, + mergeNestedObjects: true, + }, + { + email: 'shrouti@website.com', + dataFields: { + city: 'Bangalore', + name: 'shrouti', + email: 'shrouti@website.com', + country: 'India', + }, + userId: 'abcdeeeegggggxxxx102', + preferUserId: 'abc', + mergeNestedObjects: true, + }, + ], +}; + +export const partiallyCorrectIdentifyData = { + users: [ + { + email: 'manashi@website.com', + dataFields: { + city: 'Bangalore', + name: 'manashi', + email: 'manashi@website.com', + country: 'India', + }, + userId: 'abcdeeeeeeeexxxx102', + preferUserId: true, + mergeNestedObjects: true, + }, + { + email: 'shrouti', + dataFields: { + city: 'Bangalore', + name: 'shrouti', + email: 'shrouti@website.com', + country: 'India', + }, + userId: 'abcdeeeegggggxxxx102', + preferUserId: true, + mergeNestedObjects: true, + }, + ], +}; + +// MOCK DATA +const businessMockData = [ + { + description: 'Mock response from destination depicting request with a correct track payload', + httpReq: { + method: 'POST', + url: 'https://api.iterable.com/api/events/trackBulk', + headers: headerBlockWithCorrectAccessToken, + data: correctTrackData, + }, + httpRes: { + data: { + successCount: 2, + failCount: 0, + invalidEmails: [], + invalidUserIds: [], + disallowedEventNames: [], + filteredOutFields: [], + createdFields: [], + failedUpdates: { + invalidEmails: [], + invalidUserIds: [], + notFoundEmails: [], + notFoundUserIds: [], + forgottenEmails: [], + forgottenUserIds: [], + }, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response from destination depicting request with a partially wrong track payload', + httpReq: { + method: 'POST', + url: 'https://api.iterable.com/api/events/trackBulk', + headers: headerBlockWithCorrectAccessToken, + data: partiallyCorrectTrackData, + }, + httpRes: { + data: { + successCount: 1, + failCount: 1, + invalidEmails: ['sayan'], + invalidUserIds: [], + disallowedEventNames: [], + filteredOutFields: [], + createdFields: [], + failedUpdates: { + invalidEmails: ['sayan'], + invalidUserIds: [], + notFoundEmails: [], + notFoundUserIds: [], + forgottenEmails: [], + forgottenUserIds: [], + }, + + status: 200, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response from destination depicting request with a wrong data', + httpReq: { + method: 'POST', + url: 'https://api.iterable.com/api/events/trackBulk', + headers: headerBlockWithCorrectAccessToken, + data: wrongTrackData, + }, + httpRes: { + data: { + msg: '[/api/events/trackBulk] Invalid JSON body', + code: 'BadJsonBody', + params: { + 'obj.events[1].createdAt': 'Number value expected', + }, + }, + status: 400, + }, + }, + { + description: + 'Mock response from destination depicting request with a correct track payload but wrong API key', + httpReq: { + method: 'POST', + url: 'https://api.iterable.com/api/events/trackBulk', + headers: { ...headerBlockWithCorrectAccessToken, api_key: 'WRONG_API_KEY' }, + data: correctTrackData, + }, + httpRes: { + data: { + msg: 'Invalid API key', + code: 'BadApiKey', + params: { + ip: '152.58.182.124', + endpoint: '/api/events/trackBulk', + }, + }, + status: 401, + }, + }, + { + description: 'Mock response from destination depicting request with a correct Identify payload', + httpReq: { + method: 'POST', + url: 'https://api.iterable.com/api/users/bulkUpdate', + headers: headerBlockWithCorrectAccessToken, + data: correctIdentifyData, + }, + httpRes: { + data: { + successCount: 2, + failCount: 0, + invalidEmails: [], + invalidUserIds: [], + filteredOutFields: [], + createdFields: [], + noOpEmails: [], + noOpUserIds: [], + failedUpdates: { + invalidEmails: [], + invalidUserIds: [], + notFoundEmails: [], + notFoundUserIds: [], + invalidDataEmails: [], + invalidDataUserIds: [], + conflictEmails: [], + conflictUserIds: [], + forgottenEmails: [], + forgottenUserIds: [], + }, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response from destination depicting identify request with a partially wrong track payload', + httpReq: { + method: 'POST', + url: 'https://api.iterable.com/api/users/bulkUpdate', + headers: headerBlockWithCorrectAccessToken, + data: partiallyCorrectIdentifyData, + }, + httpRes: { + data: { + successCount: 1, + failCount: 1, + invalidEmails: ['shrouti'], + invalidUserIds: [], + filteredOutFields: [], + createdFields: [], + noOpEmails: [], + noOpUserIds: [], + failedUpdates: { + invalidEmails: ['shrouti'], + invalidUserIds: [], + notFoundEmails: [], + notFoundUserIds: [], + invalidDataEmails: [], + invalidDataUserIds: [], + conflictEmails: [], + conflictUserIds: [], + forgottenEmails: [], + forgottenUserIds: [], + }, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response from destination depicting identify request with a wrong data', + httpReq: { + method: 'POST', + url: 'https://api.iterable.com/api/users/bulkUpdate', + headers: headerBlockWithCorrectAccessToken, + data: wrongIdentifyData, + }, + httpRes: { + data: { + msg: '[/api/users/bulkUpdate] Invalid JSON body', + code: 'BadJsonBody', + params: { + 'obj.users[1].preferUserId': 'Boolean value expected', + }, + }, + status: 400, + }, + }, +]; + +export const networkCallsData = [...businessMockData]; From 8742244981348ec8a65d876a0810ca6fe1864d54 Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Mon, 18 Nov 2024 09:38:17 +0530 Subject: [PATCH 03/21] fix: adding factory pattern --- .../destinations/iterable/networkHandler.js | 131 +++++++++++++++++- 1 file changed, 128 insertions(+), 3 deletions(-) diff --git a/src/v1/destinations/iterable/networkHandler.js b/src/v1/destinations/iterable/networkHandler.js index 3fde013580a..b0cc9d656a9 100644 --- a/src/v1/destinations/iterable/networkHandler.js +++ b/src/v1/destinations/iterable/networkHandler.js @@ -14,7 +14,7 @@ const { const responseHandler = (responseParams) => { const { destinationResponse, rudderJobMetadata, destinationRequest } = responseParams; - const message = `[ITERABLE Response V1 Handler] - Request Processed Successfully`; + const message = `[ITERABLE Response Handler] - Request Processed Successfully`; let responseWithIndividualEvents = []; const { response, status } = destinationResponse; @@ -26,7 +26,7 @@ const responseHandler = (responseParams) => { error: errorMessage, })); throw new TransformerProxyError( - `ITERABLE: Error transformer proxy v1 during ITERABLE response transformation. ${errorMessage}`, + `ITERABLE: Error transformer proxy during ITERABLE response transformation. ${errorMessage}`, status, { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), @@ -68,7 +68,7 @@ const responseHandler = (responseParams) => { } throw new TransformerProxyError( - `ITERABLE: Error transformer proxy v1 during ITERABLE response transformation`, + `ITERABLE: Error transformer proxy during ITERABLE response transformation`, status, { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), @@ -87,3 +87,128 @@ function networkHandler() { } module.exports = { networkHandler, checkIfEventIsAbortableAndExtractErrorMessage }; + +/** + * + + +class ResponseStrategy { + handleResponse(responseParams) { + const { destinationResponse, rudderJobMetadata } = responseParams; + const { status } = destinationResponse; + + if (!isHttpStatusSuccess(status)) { + return this.handleError(responseParams); + } + + return this.handleSuccess(responseParams); + } + + handleError(responseParams) { + const { destinationResponse, rudderJobMetadata } = responseParams; + const { response, status } = destinationResponse; + const errorMessage = JSON.stringify(response.params) || 'unknown error format'; + + const responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ + statusCode: status, + metadata, + error: errorMessage, + })); + + throw new TransformerProxyError( + `ITERABLE: Error transformer proxy during ITERABLE response transformation. ${errorMessage}`, + status, + { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status) }, + destinationResponse, + '', + responseWithIndividualEvents, + ); + } + + + handleSuccess(responseParams) { + throw new Error('handleSuccess must be implemented'); + } +} + + +class TrackIdentifyStrategy extends ResponseStrategy { + constructor(filterFn) { + super(); + this.filterFn = filterFn; + } + + handleSuccess(responseParams) { + const { destinationResponse, rudderJobMetadata, destinationRequest } = responseParams; + const { status } = destinationResponse; + const responseWithIndividualEvents = []; + + const { events, users } = destinationRequest.body.JSON; + const finalData = events || users; + + finalData.forEach((event, idx) => { + const proxyOutput = { + statusCode: 200, + metadata: rudderJobMetadata[idx], + error: 'success', + }; + + const { isAbortable, errorMsg } = this.filterFn(event, destinationResponse); + if (isAbortable) { + proxyOutput.statusCode = 400; + proxyOutput.error = errorMsg; + } + responseWithIndividualEvents.push(proxyOutput); + }); + + return { + status, + message: '[ITERABLE Response Handler] - Request Processed Successfully', + destinationResponse, + response: responseWithIndividualEvents, + }; + } +} + + +class SimpleStrategy extends ResponseStrategy { + handleSuccess(responseParams) { + const { destinationResponse, rudderJobMetadata } = responseParams; + const { status } = destinationResponse; + + const responseWithIndividualEvents = rudderJobMetadata.map(metadata => ({ + statusCode: status, + metadata, + error: 'success' + })); + + return { + status, + message: '[ITERABLE Response Handler] - Request Processed Successfully', + destinationResponse, + response: responseWithIndividualEvents, + }; + } +} + + +const getResponseStrategy = (endpoint) => { + switch (endpoint) { + case '/api/events/track': + return new TrackIdentifyStrategy(checkIfEventIsAbortableAndExtractErrorMessage); + case '/api/users/update': + return new TrackIdentifyStrategy(checkIfEventIsAbortableAndExtractErrorMessage); + default: + return new SimpleStrategy(); + } +}; + + +const responseHandler = (responseParams) => { + const { destinationRequest } = responseParams; + const strategy = getResponseStrategy(destinationRequest.body.endpoint); + return strategy.handleResponse(responseParams); +}; + +// ... rest of the code remains same ... + */ From 25a5ced9fcd70badc2b61a03371f86e802f8327d Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Wed, 20 Nov 2024 09:46:25 +0530 Subject: [PATCH 04/21] fix: code redesigning --- src/v0/destinations/iterable/config.js | 7 + src/v0/destinations/iterable/util.js | 18 +- .../destinations/iterable/commonStrategy.js | 23 ++ .../destinations/iterable/networkHandler.js | 212 ++---------------- .../destinations/iterable/responseStrategy.js | 44 ++++ .../iterable/trackIdentifyStrategy.js | 41 ++++ .../iterable/dataDelivery/business.ts | 24 +- 7 files changed, 153 insertions(+), 216 deletions(-) create mode 100644 src/v1/destinations/iterable/commonStrategy.js create mode 100644 src/v1/destinations/iterable/responseStrategy.js create mode 100644 src/v1/destinations/iterable/trackIdentifyStrategy.js diff --git a/src/v0/destinations/iterable/config.js b/src/v0/destinations/iterable/config.js index a2bce4f9d3f..576717504e9 100644 --- a/src/v0/destinations/iterable/config.js +++ b/src/v0/destinations/iterable/config.js @@ -20,6 +20,7 @@ const ConfigCategory = { name: 'IterableIdentifyConfig', action: 'identify', endpoint: `users/update`, + bulkEndpoint: 'users/bulkUpdate', }, PAGE: { name: 'IterablePageConfig', @@ -35,6 +36,7 @@ const ConfigCategory = { name: 'IterableTrackConfig', action: 'track', endpoint: `events/track`, + bulkEndpoint: 'events/trackBulk', }, TRACK_PURCHASE: { name: 'IterableTrackPurchaseConfig', @@ -76,6 +78,10 @@ const constructEndpoint = (dataCenter, category) => { return `${baseUrl}${category.endpoint}`; }; +const BULK_ENDPOINTS = Object.values(ConfigCategory) + .filter((config) => config.bulkEndpoint) + .map((config) => `/api/${config.bulkEndpoint}`); + const IDENTIFY_MAX_BATCH_SIZE = 1000; const IDENTIFY_MAX_BODY_SIZE_IN_BYTES = 4000000; @@ -105,4 +111,5 @@ module.exports = { IDENTIFY_MAX_BATCH_SIZE, IDENTIFY_MAX_BODY_SIZE_IN_BYTES, API_RESPONSE_PATHS, + BULK_ENDPOINTS, }; diff --git a/src/v0/destinations/iterable/util.js b/src/v0/destinations/iterable/util.js index ba0ce717316..2a604f9beaa 100644 --- a/src/v0/destinations/iterable/util.js +++ b/src/v0/destinations/iterable/util.js @@ -484,6 +484,7 @@ const batchUpdateUserEvents = (updateUserEvents, registerDeviceOrBrowserTokenEve /** * Processes chunks of catalog events, extracts the necessary data, and prepares batched requests for further processing + * ref : https://api.iterable.com/api/docs#catalogs_bulkUpdateCatalogItems * @param {*} catalogEventsChunks * @returns */ @@ -601,12 +602,12 @@ const batchTrackEvents = (trackEvents) => { */ const prepareBatchRequests = (filteredEvents) => { const { - trackEvents, - catalogEvents, - errorRespList, - updateUserEvents, - eventResponseList, - registerDeviceOrBrowserTokenEvents, + trackEvents, // track + catalogEvents, // identify + errorRespList, // track + updateUserEvents, // identify + eventResponseList, // track + registerDeviceOrBrowserTokenEvents, // identify } = filteredEvents; const updateUserBatchedResponseList = @@ -753,6 +754,11 @@ const filterEventsAndPrepareBatchRequests = (transformedEvents) => { * 2) https://api.iterable.com/api/docs#events_track * 3) https://api.iterable.com/api/docs#users_bulkUpdateUser * 4) https://api.iterable.com/api/docs#events_trackBulk + * 5) https://api.iterable.com/api/docs#catalogs_bulkUpdateCatalogItems + * 6) https://api.iterable.com/api/docs#users_registerDeviceToken + * 7) https://api.iterable.com/api/docs#users_registerBrowserToken + * 8) https://api.iterable.com/api/docs#commerce_trackPurchase + * 9) https://api.iterable.com/api/docs#commerce_updateCart * * @param {Object} event - The event object containing various event properties. * @param {Object} destinationResponse - The response object from the destination. diff --git a/src/v1/destinations/iterable/commonStrategy.js b/src/v1/destinations/iterable/commonStrategy.js new file mode 100644 index 00000000000..b98c0bac5ef --- /dev/null +++ b/src/v1/destinations/iterable/commonStrategy.js @@ -0,0 +1,23 @@ +const { ResponseStrategy } = require('./responseStrategy'); + +class CommonStrategy extends ResponseStrategy { + handleSuccess(responseParams) { + const { destinationResponse, rudderJobMetadata } = responseParams; + const { status } = destinationResponse; + + const responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ + statusCode: status, + metadata, + error: 'success', + })); + + return { + status, + message: '[ITERABLE Response Handler] - Request Processed Successfully', + destinationResponse, + response: responseWithIndividualEvents, + }; + } +} + +module.exports = { CommonStrategy }; diff --git a/src/v1/destinations/iterable/networkHandler.js b/src/v1/destinations/iterable/networkHandler.js index b0cc9d656a9..6a129c7802c 100644 --- a/src/v1/destinations/iterable/networkHandler.js +++ b/src/v1/destinations/iterable/networkHandler.js @@ -1,82 +1,23 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -const { TransformerProxyError } = require('../../../v0/util/errorTypes'); const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); -const { isHttpStatusSuccess } = require('../../../v0/util/index'); - -const { - processAxiosResponse, - getDynamicErrorType, -} = require('../../../adapters/utils/networkUtils'); -const tags = require('../../../v0/util/tags'); +const { processAxiosResponse } = require('../../../adapters/utils/networkUtils'); +const { BULK_ENDPOINTS } = require('../../../v0/destinations/iterable/config'); const { checkIfEventIsAbortableAndExtractErrorMessage, } = require('../../../v0/destinations/iterable/util'); +const { CommonStrategy } = require('./commonStrategy'); +const { TrackIdentifyStrategy } = require('./trackIdentifyStrategy'); -const responseHandler = (responseParams) => { - const { destinationResponse, rudderJobMetadata, destinationRequest } = responseParams; - const message = `[ITERABLE Response Handler] - Request Processed Successfully`; - let responseWithIndividualEvents = []; - const { response, status } = destinationResponse; - - if (!isHttpStatusSuccess(status)) { - const errorMessage = JSON.stringify(response.params) || 'unknown error format'; - responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ - statusCode: status, - metadata, - error: errorMessage, - })); - throw new TransformerProxyError( - `ITERABLE: Error transformer proxy during ITERABLE response transformation. ${errorMessage}`, - status, - { - [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), - }, - destinationResponse, - '', - responseWithIndividualEvents, - ); - } - - if (isHttpStatusSuccess(status)) { - // check for Partial Event failures and Successes - // eslint-disable-next-line @typescript-eslint/naming-convention - const { events, users } = destinationRequest.body.JSON; - const finalData = events || users; - finalData.forEach((event, idx) => { - const proxyOutput = { - statusCode: 200, - metadata: rudderJobMetadata[idx], - error: 'success', - }; - // update status of partial event if abortable - const { isAbortable, errorMsg } = checkIfEventIsAbortableAndExtractErrorMessage( - event, - destinationResponse, - ); - if (isAbortable) { - proxyOutput.statusCode = 400; - proxyOutput.error = errorMsg; - } - responseWithIndividualEvents.push(proxyOutput); - }); - return { - status, - message, - destinationResponse, - response: responseWithIndividualEvents, - }; +const getResponseStrategy = (endpoint) => { + if (BULK_ENDPOINTS.some((path) => endpoint.includes(path))) { + return new TrackIdentifyStrategy(checkIfEventIsAbortableAndExtractErrorMessage); } + return new CommonStrategy(); +}; - throw new TransformerProxyError( - `ITERABLE: Error transformer proxy during ITERABLE response transformation`, - status, - { - [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), - }, - destinationResponse, - '', - responseWithIndividualEvents, - ); +const responseHandler = (responseParams) => { + const { destinationRequest } = responseParams; + const strategy = getResponseStrategy(destinationRequest.endpoint); + return strategy.handleResponse(responseParams); }; function networkHandler() { @@ -86,129 +27,4 @@ function networkHandler() { this.responseHandler = responseHandler; } -module.exports = { networkHandler, checkIfEventIsAbortableAndExtractErrorMessage }; - -/** - * - - -class ResponseStrategy { - handleResponse(responseParams) { - const { destinationResponse, rudderJobMetadata } = responseParams; - const { status } = destinationResponse; - - if (!isHttpStatusSuccess(status)) { - return this.handleError(responseParams); - } - - return this.handleSuccess(responseParams); - } - - handleError(responseParams) { - const { destinationResponse, rudderJobMetadata } = responseParams; - const { response, status } = destinationResponse; - const errorMessage = JSON.stringify(response.params) || 'unknown error format'; - - const responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ - statusCode: status, - metadata, - error: errorMessage, - })); - - throw new TransformerProxyError( - `ITERABLE: Error transformer proxy during ITERABLE response transformation. ${errorMessage}`, - status, - { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status) }, - destinationResponse, - '', - responseWithIndividualEvents, - ); - } - - - handleSuccess(responseParams) { - throw new Error('handleSuccess must be implemented'); - } -} - - -class TrackIdentifyStrategy extends ResponseStrategy { - constructor(filterFn) { - super(); - this.filterFn = filterFn; - } - - handleSuccess(responseParams) { - const { destinationResponse, rudderJobMetadata, destinationRequest } = responseParams; - const { status } = destinationResponse; - const responseWithIndividualEvents = []; - - const { events, users } = destinationRequest.body.JSON; - const finalData = events || users; - - finalData.forEach((event, idx) => { - const proxyOutput = { - statusCode: 200, - metadata: rudderJobMetadata[idx], - error: 'success', - }; - - const { isAbortable, errorMsg } = this.filterFn(event, destinationResponse); - if (isAbortable) { - proxyOutput.statusCode = 400; - proxyOutput.error = errorMsg; - } - responseWithIndividualEvents.push(proxyOutput); - }); - - return { - status, - message: '[ITERABLE Response Handler] - Request Processed Successfully', - destinationResponse, - response: responseWithIndividualEvents, - }; - } -} - - -class SimpleStrategy extends ResponseStrategy { - handleSuccess(responseParams) { - const { destinationResponse, rudderJobMetadata } = responseParams; - const { status } = destinationResponse; - - const responseWithIndividualEvents = rudderJobMetadata.map(metadata => ({ - statusCode: status, - metadata, - error: 'success' - })); - - return { - status, - message: '[ITERABLE Response Handler] - Request Processed Successfully', - destinationResponse, - response: responseWithIndividualEvents, - }; - } -} - - -const getResponseStrategy = (endpoint) => { - switch (endpoint) { - case '/api/events/track': - return new TrackIdentifyStrategy(checkIfEventIsAbortableAndExtractErrorMessage); - case '/api/users/update': - return new TrackIdentifyStrategy(checkIfEventIsAbortableAndExtractErrorMessage); - default: - return new SimpleStrategy(); - } -}; - - -const responseHandler = (responseParams) => { - const { destinationRequest } = responseParams; - const strategy = getResponseStrategy(destinationRequest.body.endpoint); - return strategy.handleResponse(responseParams); -}; - -// ... rest of the code remains same ... - */ +module.exports = { networkHandler }; diff --git a/src/v1/destinations/iterable/responseStrategy.js b/src/v1/destinations/iterable/responseStrategy.js new file mode 100644 index 00000000000..7277898a1ec --- /dev/null +++ b/src/v1/destinations/iterable/responseStrategy.js @@ -0,0 +1,44 @@ +const { isHttpStatusSuccess } = require('../../../v0/util/index'); +const { TransformerProxyError } = require('../../../v0/util/errorTypes'); +const { getDynamicErrorType } = require('../../../adapters/utils/networkUtils'); +const tags = require('../../../v0/util/tags'); + +class ResponseStrategy { + handleResponse(responseParams) { + const { destinationResponse } = responseParams; + const { status } = destinationResponse; + + if (!isHttpStatusSuccess(status)) { + return this.handleError(responseParams); + } + + return this.handleSuccess(responseParams); + } + + handleError(responseParams) { + const { destinationResponse, rudderJobMetadata } = responseParams; + const { response, status } = destinationResponse; + const errorMessage = JSON.stringify(response.params) || 'unknown error format'; + + const responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ + statusCode: status, + metadata, + error: errorMessage, + })); + + throw new TransformerProxyError( + `ITERABLE: Error transformer proxy during ITERABLE response transformation. ${errorMessage}`, + status, + { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status) }, + destinationResponse, + '', + responseWithIndividualEvents, + ); + } + + handleSuccess(responseParams) { + throw new TransformerProxyError(`success response handling is not added:${responseParams}`); + } +} + +module.exports = { ResponseStrategy }; diff --git a/src/v1/destinations/iterable/trackIdentifyStrategy.js b/src/v1/destinations/iterable/trackIdentifyStrategy.js new file mode 100644 index 00000000000..b2ba1f8f92f --- /dev/null +++ b/src/v1/destinations/iterable/trackIdentifyStrategy.js @@ -0,0 +1,41 @@ +const { ResponseStrategy } = require('./responseStrategy'); + +class TrackIdentifyStrategy extends ResponseStrategy { + constructor(filterFn) { + super(); + this.filterFn = filterFn; + } + + handleSuccess(responseParams) { + const { destinationResponse, rudderJobMetadata, destinationRequest } = responseParams; + const { status } = destinationResponse; + const responseWithIndividualEvents = []; + + const { events, users } = destinationRequest.body.JSON; + const finalData = events || users; + + finalData.forEach((event, idx) => { + const proxyOutput = { + statusCode: 200, + metadata: rudderJobMetadata[idx], + error: 'success', + }; + + const { isAbortable, errorMsg } = this.filterFn(event, destinationResponse); + if (isAbortable) { + proxyOutput.statusCode = 400; + proxyOutput.error = errorMsg; + } + responseWithIndividualEvents.push(proxyOutput); + }); + + return { + status, + message: '[ITERABLE Response Handler] - Request Processed Successfully', + destinationResponse, + response: responseWithIndividualEvents, + }; + } +} + +module.exports = { TrackIdentifyStrategy }; diff --git a/test/integrations/destinations/iterable/dataDelivery/business.ts b/test/integrations/destinations/iterable/dataDelivery/business.ts index 3754bca6d18..6980442cc41 100644 --- a/test/integrations/destinations/iterable/dataDelivery/business.ts +++ b/test/integrations/destinations/iterable/dataDelivery/business.ts @@ -53,7 +53,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ id: 'ITERABLE_v1_other_scenario_1', name: 'iterable', description: - '[Proxy v1 API] :: Scenario to test correct Payload Response Handling from Destination', + '[Proxy API] :: Scenario to test correct Payload Response Handling from Destination', successCriteria: 'Should return 200 status code with success', scenario: 'Business', feature: 'dataDelivery', @@ -78,7 +78,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ body: { output: { status: 200, - message: '[ITERABLE Response V1 Handler] - Request Processed Successfully', + message: '[ITERABLE Response Handler] - Request Processed Successfully', destinationResponse: { status: 200, response: { @@ -120,7 +120,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ id: 'ITERABLE_v1_other_scenario_2', name: 'iterable', description: - '[Proxy v1 API] :: Scenario to test Malformed Payload Response Handling from Destination', + '[Proxy API] :: Scenario to test Malformed Payload Response Handling from Destination', successCriteria: 'Should return 400 status code with error message', scenario: 'Business', feature: 'dataDelivery', @@ -147,7 +147,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ status: 400, statTags, message: - 'ITERABLE: Error transformer proxy v1 during ITERABLE response transformation. {"obj.events[1].createdAt":"Number value expected"}', + 'ITERABLE: Error transformer proxy during ITERABLE response transformation. {"obj.events[1].createdAt":"Number value expected"}', response: [ { statusCode: 400, @@ -171,7 +171,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ id: 'ITERABLE_v1_other_scenario_3', name: 'iterable', description: - '[Proxy v1 API] :: Scenario to test partially successful Response Handling from Destination', + '[Proxy API] :: Scenario to test partially successful Response Handling from Destination', successCriteria: 'Should return 400 status code with error message', scenario: 'Business', feature: 'dataDelivery', @@ -196,7 +196,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ body: { output: { status: 200, - message: '[ITERABLE Response V1 Handler] - Request Processed Successfully', + message: '[ITERABLE Response Handler] - Request Processed Successfully', destinationResponse: { status: 200, response: { @@ -240,7 +240,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ id: 'ITERABLE_v1_other_scenario_1', name: 'iterable', description: - '[Proxy v1 API] :: Scenario to test correct identify Payload Response Handling from Destination', + '[Proxy API] :: Scenario to test correct identify Payload Response Handling from Destination', successCriteria: 'Should return 200 status code with success', scenario: 'Business', feature: 'dataDelivery', @@ -265,7 +265,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ body: { output: { status: 200, - message: '[ITERABLE Response V1 Handler] - Request Processed Successfully', + message: '[ITERABLE Response Handler] - Request Processed Successfully', destinationResponse: { status: 200, response: { @@ -312,7 +312,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ id: 'ITERABLE_v1_other_scenario_2', name: 'iterable', description: - '[Proxy v1 API] :: Scenario to test Malformed identify Payload Response Handling from Destination', + '[Proxy API] :: Scenario to test Malformed identify Payload Response Handling from Destination', successCriteria: 'Should return 400 status code with error message', scenario: 'Business', feature: 'dataDelivery', @@ -339,7 +339,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ status: 400, statTags, message: - 'ITERABLE: Error transformer proxy v1 during ITERABLE response transformation. {"obj.users[1].preferUserId":"Boolean value expected"}', + 'ITERABLE: Error transformer proxy during ITERABLE response transformation. {"obj.users[1].preferUserId":"Boolean value expected"}', response: [ { statusCode: 400, @@ -363,7 +363,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ id: 'ITERABLE_v1_other_scenario_3', name: 'iterable', description: - '[Proxy v1 API] :: Scenario to test partially successful identify Response Handling from Destination', + '[Proxy API] :: Scenario to test partially successful identify Response Handling from Destination', successCriteria: 'Should return 400 status code with error message', scenario: 'Business', feature: 'dataDelivery', @@ -388,7 +388,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ body: { output: { status: 200, - message: '[ITERABLE Response V1 Handler] - Request Processed Successfully', + message: '[ITERABLE Response Handler] - Request Processed Successfully', destinationResponse: { status: 200, response: { From c87c5ce30f075c70793851fc685182d8f9c71f49 Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Wed, 20 Nov 2024 13:19:32 +0530 Subject: [PATCH 05/21] fix: decoupling networkHandler and specific strategy --- src/v1/destinations/iterable/networkHandler.js | 5 +---- src/v1/destinations/iterable/trackIdentifyStrategy.js | 7 +++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/v1/destinations/iterable/networkHandler.js b/src/v1/destinations/iterable/networkHandler.js index 6a129c7802c..bbc30a4fd26 100644 --- a/src/v1/destinations/iterable/networkHandler.js +++ b/src/v1/destinations/iterable/networkHandler.js @@ -1,15 +1,12 @@ const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); const { processAxiosResponse } = require('../../../adapters/utils/networkUtils'); const { BULK_ENDPOINTS } = require('../../../v0/destinations/iterable/config'); -const { - checkIfEventIsAbortableAndExtractErrorMessage, -} = require('../../../v0/destinations/iterable/util'); const { CommonStrategy } = require('./commonStrategy'); const { TrackIdentifyStrategy } = require('./trackIdentifyStrategy'); const getResponseStrategy = (endpoint) => { if (BULK_ENDPOINTS.some((path) => endpoint.includes(path))) { - return new TrackIdentifyStrategy(checkIfEventIsAbortableAndExtractErrorMessage); + return new TrackIdentifyStrategy(); } return new CommonStrategy(); }; diff --git a/src/v1/destinations/iterable/trackIdentifyStrategy.js b/src/v1/destinations/iterable/trackIdentifyStrategy.js index b2ba1f8f92f..aabe0845ffa 100644 --- a/src/v1/destinations/iterable/trackIdentifyStrategy.js +++ b/src/v1/destinations/iterable/trackIdentifyStrategy.js @@ -1,9 +1,12 @@ const { ResponseStrategy } = require('./responseStrategy'); +const { + checkIfEventIsAbortableAndExtractErrorMessage, +} = require('../../../v0/destinations/iterable/util'); class TrackIdentifyStrategy extends ResponseStrategy { - constructor(filterFn) { + constructor() { super(); - this.filterFn = filterFn; + this.filterFn = checkIfEventIsAbortableAndExtractErrorMessage; } handleSuccess(responseParams) { From 1058ec11022e80368a42c72b3fc360880609093a Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Wed, 20 Nov 2024 13:29:32 +0530 Subject: [PATCH 06/21] fix: simplifying logic --- src/v1/destinations/iterable/trackIdentifyStrategy.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/v1/destinations/iterable/trackIdentifyStrategy.js b/src/v1/destinations/iterable/trackIdentifyStrategy.js index aabe0845ffa..e040f4a40db 100644 --- a/src/v1/destinations/iterable/trackIdentifyStrategy.js +++ b/src/v1/destinations/iterable/trackIdentifyStrategy.js @@ -4,11 +4,6 @@ const { } = require('../../../v0/destinations/iterable/util'); class TrackIdentifyStrategy extends ResponseStrategy { - constructor() { - super(); - this.filterFn = checkIfEventIsAbortableAndExtractErrorMessage; - } - handleSuccess(responseParams) { const { destinationResponse, rudderJobMetadata, destinationRequest } = responseParams; const { status } = destinationResponse; @@ -24,7 +19,10 @@ class TrackIdentifyStrategy extends ResponseStrategy { error: 'success', }; - const { isAbortable, errorMsg } = this.filterFn(event, destinationResponse); + const { isAbortable, errorMsg } = checkIfEventIsAbortableAndExtractErrorMessage( + event, + destinationResponse, + ); if (isAbortable) { proxyOutput.statusCode = 400; proxyOutput.error = errorMsg; From 909cad0ecfcccee3868d2471cc433d316171ffe0 Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Tue, 3 Dec 2024 15:24:17 +0530 Subject: [PATCH 07/21] fix: convert to registry pattern --- src/v1/destinations/iterable/networkHandler.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/v1/destinations/iterable/networkHandler.js b/src/v1/destinations/iterable/networkHandler.js index bbc30a4fd26..7b10b148bdd 100644 --- a/src/v1/destinations/iterable/networkHandler.js +++ b/src/v1/destinations/iterable/networkHandler.js @@ -4,11 +4,16 @@ const { BULK_ENDPOINTS } = require('../../../v0/destinations/iterable/config'); const { CommonStrategy } = require('./commonStrategy'); const { TrackIdentifyStrategy } = require('./trackIdentifyStrategy'); +const strategyRegistry = { + [TrackIdentifyStrategy.name]: new TrackIdentifyStrategy(), + [CommonStrategy.name]: new CommonStrategy(), +}; + const getResponseStrategy = (endpoint) => { if (BULK_ENDPOINTS.some((path) => endpoint.includes(path))) { - return new TrackIdentifyStrategy(); + return strategyRegistry[TrackIdentifyStrategy.name]; } - return new CommonStrategy(); + return strategyRegistry[CommonStrategy.name]; }; const responseHandler = (responseParams) => { From 2337c7bd07a9eabfc377450d4866f0010fb42867 Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Tue, 3 Dec 2024 15:46:02 +0530 Subject: [PATCH 08/21] fix: converting to typescript --- .../{commonStrategy.js => commonStrategy.ts} | 14 +++- .../destinations/iterable/networkHandler.js | 32 --------- .../destinations/iterable/networkHandler.ts | 38 +++++++++++ ...esponseStrategy.js => responseStrategy.ts} | 17 +++-- .../iterable/trackIdentifyStrategy.js | 42 ------------ .../iterable/trackIdentifyStrategy.ts | 65 +++++++++++++++++++ src/v1/destinations/iterable/type.ts | 26 ++++++++ 7 files changed, 153 insertions(+), 81 deletions(-) rename src/v1/destinations/iterable/{commonStrategy.js => commonStrategy.ts} (58%) delete mode 100644 src/v1/destinations/iterable/networkHandler.js create mode 100644 src/v1/destinations/iterable/networkHandler.ts rename src/v1/destinations/iterable/{responseStrategy.js => responseStrategy.ts} (77%) delete mode 100644 src/v1/destinations/iterable/trackIdentifyStrategy.js create mode 100644 src/v1/destinations/iterable/trackIdentifyStrategy.ts create mode 100644 src/v1/destinations/iterable/type.ts diff --git a/src/v1/destinations/iterable/commonStrategy.js b/src/v1/destinations/iterable/commonStrategy.ts similarity index 58% rename from src/v1/destinations/iterable/commonStrategy.js rename to src/v1/destinations/iterable/commonStrategy.ts index b98c0bac5ef..5d6aa2a8920 100644 --- a/src/v1/destinations/iterable/commonStrategy.js +++ b/src/v1/destinations/iterable/commonStrategy.ts @@ -1,7 +1,17 @@ +import { CommonResponse } from './type'; + const { ResponseStrategy } = require('./responseStrategy'); class CommonStrategy extends ResponseStrategy { - handleSuccess(responseParams) { + handleSuccess(responseParams: { + destinationResponse: { status: number; response: CommonResponse }; + rudderJobMetadata: any[]; + }): { + status: number; + message: string; + destinationResponse: { status: number; response: CommonResponse }; + response: { statusCode: number; metadata: any; error: string }[]; + } { const { destinationResponse, rudderJobMetadata } = responseParams; const { status } = destinationResponse; @@ -20,4 +30,4 @@ class CommonStrategy extends ResponseStrategy { } } -module.exports = { CommonStrategy }; +export { CommonStrategy }; diff --git a/src/v1/destinations/iterable/networkHandler.js b/src/v1/destinations/iterable/networkHandler.js deleted file mode 100644 index 7b10b148bdd..00000000000 --- a/src/v1/destinations/iterable/networkHandler.js +++ /dev/null @@ -1,32 +0,0 @@ -const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); -const { processAxiosResponse } = require('../../../adapters/utils/networkUtils'); -const { BULK_ENDPOINTS } = require('../../../v0/destinations/iterable/config'); -const { CommonStrategy } = require('./commonStrategy'); -const { TrackIdentifyStrategy } = require('./trackIdentifyStrategy'); - -const strategyRegistry = { - [TrackIdentifyStrategy.name]: new TrackIdentifyStrategy(), - [CommonStrategy.name]: new CommonStrategy(), -}; - -const getResponseStrategy = (endpoint) => { - if (BULK_ENDPOINTS.some((path) => endpoint.includes(path))) { - return strategyRegistry[TrackIdentifyStrategy.name]; - } - return strategyRegistry[CommonStrategy.name]; -}; - -const responseHandler = (responseParams) => { - const { destinationRequest } = responseParams; - const strategy = getResponseStrategy(destinationRequest.endpoint); - return strategy.handleResponse(responseParams); -}; - -function networkHandler() { - this.prepareProxy = prepareProxyRequest; - this.proxy = proxyRequest; - this.processAxiosResponse = processAxiosResponse; - this.responseHandler = responseHandler; -} - -module.exports = { networkHandler }; diff --git a/src/v1/destinations/iterable/networkHandler.ts b/src/v1/destinations/iterable/networkHandler.ts new file mode 100644 index 00000000000..b409facc4fe --- /dev/null +++ b/src/v1/destinations/iterable/networkHandler.ts @@ -0,0 +1,38 @@ +import { prepareProxyRequest, proxyRequest } from '../../../adapters/network'; +import { processAxiosResponse } from '../../../adapters/utils/networkUtils'; +import { BULK_ENDPOINTS } from '../../../v0/destinations/iterable/config'; +import { CommonStrategy } from './commonStrategy'; +import { TrackIdentifyStrategy } from './trackIdentifyStrategy'; + +interface ResponseParams { + destinationRequest: { + endpoint: string; + }; +} + +const strategyRegistry: { [key: string]: any } = { + [TrackIdentifyStrategy.name]: new TrackIdentifyStrategy(), + [CommonStrategy.name]: new CommonStrategy(), +}; + +const getResponseStrategy = (endpoint: string) => { + if (BULK_ENDPOINTS.some((path) => endpoint.includes(path))) { + return strategyRegistry[TrackIdentifyStrategy.name]; + } + return strategyRegistry[CommonStrategy.name]; +}; + +const responseHandler = (responseParams: ResponseParams) => { + const { destinationRequest } = responseParams; + const strategy = getResponseStrategy(destinationRequest.endpoint); + return strategy.handleResponse(responseParams); +}; + +function networkHandler(this: any) { + this.prepareProxy = prepareProxyRequest; + this.proxy = proxyRequest; + this.processAxiosResponse = processAxiosResponse; + this.responseHandler = responseHandler; +} + +export { networkHandler }; diff --git a/src/v1/destinations/iterable/responseStrategy.js b/src/v1/destinations/iterable/responseStrategy.ts similarity index 77% rename from src/v1/destinations/iterable/responseStrategy.js rename to src/v1/destinations/iterable/responseStrategy.ts index 7277898a1ec..cbbe305be55 100644 --- a/src/v1/destinations/iterable/responseStrategy.js +++ b/src/v1/destinations/iterable/responseStrategy.ts @@ -1,21 +1,28 @@ +import { CommonResponse } from './type'; + const { isHttpStatusSuccess } = require('../../../v0/util/index'); const { TransformerProxyError } = require('../../../v0/util/errorTypes'); const { getDynamicErrorType } = require('../../../adapters/utils/networkUtils'); const tags = require('../../../v0/util/tags'); class ResponseStrategy { - handleResponse(responseParams) { + handleResponse(responseParams: { + destinationResponse: { status: number; response: CommonResponse }; + }): void { const { destinationResponse } = responseParams; const { status } = destinationResponse; if (!isHttpStatusSuccess(status)) { - return this.handleError(responseParams); + return this.handleError({ + destinationResponse, + rudderJobMetadata: [], + }); } return this.handleSuccess(responseParams); } - handleError(responseParams) { + handleError(responseParams): void { const { destinationResponse, rudderJobMetadata } = responseParams; const { response, status } = destinationResponse; const errorMessage = JSON.stringify(response.params) || 'unknown error format'; @@ -36,9 +43,9 @@ class ResponseStrategy { ); } - handleSuccess(responseParams) { + handleSuccess(responseParams: any): void { throw new TransformerProxyError(`success response handling is not added:${responseParams}`); } } -module.exports = { ResponseStrategy }; +export { ResponseStrategy }; diff --git a/src/v1/destinations/iterable/trackIdentifyStrategy.js b/src/v1/destinations/iterable/trackIdentifyStrategy.js deleted file mode 100644 index e040f4a40db..00000000000 --- a/src/v1/destinations/iterable/trackIdentifyStrategy.js +++ /dev/null @@ -1,42 +0,0 @@ -const { ResponseStrategy } = require('./responseStrategy'); -const { - checkIfEventIsAbortableAndExtractErrorMessage, -} = require('../../../v0/destinations/iterable/util'); - -class TrackIdentifyStrategy extends ResponseStrategy { - handleSuccess(responseParams) { - const { destinationResponse, rudderJobMetadata, destinationRequest } = responseParams; - const { status } = destinationResponse; - const responseWithIndividualEvents = []; - - const { events, users } = destinationRequest.body.JSON; - const finalData = events || users; - - finalData.forEach((event, idx) => { - const proxyOutput = { - statusCode: 200, - metadata: rudderJobMetadata[idx], - error: 'success', - }; - - const { isAbortable, errorMsg } = checkIfEventIsAbortableAndExtractErrorMessage( - event, - destinationResponse, - ); - if (isAbortable) { - proxyOutput.statusCode = 400; - proxyOutput.error = errorMsg; - } - responseWithIndividualEvents.push(proxyOutput); - }); - - return { - status, - message: '[ITERABLE Response Handler] - Request Processed Successfully', - destinationResponse, - response: responseWithIndividualEvents, - }; - } -} - -module.exports = { TrackIdentifyStrategy }; diff --git a/src/v1/destinations/iterable/trackIdentifyStrategy.ts b/src/v1/destinations/iterable/trackIdentifyStrategy.ts new file mode 100644 index 00000000000..5ca5d4ae84d --- /dev/null +++ b/src/v1/destinations/iterable/trackIdentifyStrategy.ts @@ -0,0 +1,65 @@ +import { ResponseStrategy } from './responseStrategy'; +import { checkIfEventIsAbortableAndExtractErrorMessage } from '../../../v0/destinations/iterable/util'; +import { CommonResponse } from './type'; + +interface ResponseParams { + destinationResponse: { status: number; response: CommonResponse }; + rudderJobMetadata: any[]; + destinationRequest: { + body: { + JSON: { + events?: any[]; + users?: any[]; + }; + }; + }; +} + +class TrackIdentifyStrategy extends ResponseStrategy { + handleSuccess(responseParams: ResponseParams): { + status: number; + message: string; + destinationResponse: { status: number; response: CommonResponse }; + response: Array<{ statusCode: number; metadata: any; error: string }>; + } { + const { destinationResponse, rudderJobMetadata, destinationRequest } = responseParams; + const { status } = destinationResponse; + const responseWithIndividualEvents: Array<{ + statusCode: number; + metadata: any; + error: string; + }> = []; + + const { events, users } = destinationRequest.body.JSON; + const finalData = events || users; + + if (finalData) { + finalData.forEach((event, idx) => { + const proxyOutput = { + statusCode: 200, + metadata: rudderJobMetadata[idx], + error: 'success', + }; + + const { isAbortable, errorMsg } = checkIfEventIsAbortableAndExtractErrorMessage( + event, + destinationResponse, + ); + if (isAbortable) { + proxyOutput.statusCode = 400; + proxyOutput.error = errorMsg; + } + responseWithIndividualEvents.push(proxyOutput); + }); + } + + return { + status, + message: '[ITERABLE Response Handler] - Request Processed Successfully', + destinationResponse, + response: responseWithIndividualEvents, + }; + } +} + +export { TrackIdentifyStrategy }; diff --git a/src/v1/destinations/iterable/type.ts b/src/v1/destinations/iterable/type.ts new file mode 100644 index 00000000000..98d5d231902 --- /dev/null +++ b/src/v1/destinations/iterable/type.ts @@ -0,0 +1,26 @@ +interface FailedUpdates { + invalidEmails?: string[]; + invalidUserIds?: string[]; + notFoundEmails?: string[]; + notFoundUserIds?: string[]; + invalidDataEmails?: string[]; + invalidDataUserIds?: string[]; + conflictEmails?: string[]; + conflictUserIds?: string[]; + forgottenEmails?: string[]; + forgottenUserIds?: string[]; +} + +export interface CommonResponse { + msg?: string; // Optional since it's only in Response 1 + code?: string; // Optional since it's only in Response 1 + params?: Record; // Optional, specific to Response 1 + successCount?: number; // Shared by Response 2 and 3 + failCount?: number; // Shared by Response 2 and 3 + invalidEmails?: string[]; // Shared by Response 2 and 3 + invalidUserIds?: string[]; // Shared by Response 2 and 3 + filteredOutFields?: string[]; // Shared by Response 2 and 3 + createdFields?: string[]; // Shared by Response 2 and 3 + disallowedEventNames?: string[]; // Specific to Response 3 + failedUpdates?: FailedUpdates; // Nested object for failed updates +} From 9eb31bf7bf7b364b463502a6e839c6d80e49ce43 Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Tue, 3 Dec 2024 15:48:41 +0530 Subject: [PATCH 09/21] fix: removing unnecessary comments --- src/v1/destinations/iterable/type.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/v1/destinations/iterable/type.ts b/src/v1/destinations/iterable/type.ts index 98d5d231902..e29e6236e66 100644 --- a/src/v1/destinations/iterable/type.ts +++ b/src/v1/destinations/iterable/type.ts @@ -12,15 +12,15 @@ interface FailedUpdates { } export interface CommonResponse { - msg?: string; // Optional since it's only in Response 1 - code?: string; // Optional since it's only in Response 1 - params?: Record; // Optional, specific to Response 1 - successCount?: number; // Shared by Response 2 and 3 - failCount?: number; // Shared by Response 2 and 3 - invalidEmails?: string[]; // Shared by Response 2 and 3 - invalidUserIds?: string[]; // Shared by Response 2 and 3 - filteredOutFields?: string[]; // Shared by Response 2 and 3 - createdFields?: string[]; // Shared by Response 2 and 3 - disallowedEventNames?: string[]; // Specific to Response 3 - failedUpdates?: FailedUpdates; // Nested object for failed updates + msg?: string; + code?: string; + params?: Record; + successCount?: number; + failCount?: number; + invalidEmails?: string[]; + invalidUserIds?: string[]; + filteredOutFields?: string[]; + createdFields?: string[]; + disallowedEventNames?: string[]; + failedUpdates?: FailedUpdates; } From 97d531fb92708516d696ec39e1f23abfb8263640 Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Fri, 6 Dec 2024 16:51:05 +0530 Subject: [PATCH 10/21] fix: adding data delivery test cases for all endpoints --- .../destinations/iterable/responseStrategy.ts | 6 +- .../iterable/dataDelivery/business.ts | 396 +++++++++++++++++- .../iterable/dataDelivery/network.ts | 200 +++++++++ 3 files changed, 598 insertions(+), 4 deletions(-) diff --git a/src/v1/destinations/iterable/responseStrategy.ts b/src/v1/destinations/iterable/responseStrategy.ts index cbbe305be55..9c2d1eeb406 100644 --- a/src/v1/destinations/iterable/responseStrategy.ts +++ b/src/v1/destinations/iterable/responseStrategy.ts @@ -25,7 +25,11 @@ class ResponseStrategy { handleError(responseParams): void { const { destinationResponse, rudderJobMetadata } = responseParams; const { response, status } = destinationResponse; - const errorMessage = JSON.stringify(response.params) || 'unknown error format'; + const errorMessage = + JSON.stringify(response.params) || + JSON.stringify(response.msg) || + JSON.stringify(response.message) || + 'unknown error format'; const responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ statusCode: status, diff --git a/test/integrations/destinations/iterable/dataDelivery/business.ts b/test/integrations/destinations/iterable/dataDelivery/business.ts index 6980442cc41..ec40c29b40f 100644 --- a/test/integrations/destinations/iterable/dataDelivery/business.ts +++ b/test/integrations/destinations/iterable/dataDelivery/business.ts @@ -48,6 +48,43 @@ export const metadata = [ }, ]; +export const singleMetadata = [ + { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: false, + }, +]; + +export const singleTrackPayload = { + email: 'sayan@gmail.com', + userId: 'abcdeeeeeeeexxxx102', + eventName: 'Email Opened', + id: '1234', + createdAt: 1598631966468, + dataFields: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', + }, + campaignId: 0, + templateId: 0, + createNewFields: true, +}; + +export const updateEmailData = { + currentEmail: 'sayan', + currentUserId: 'abcdeeeeeeeexxxx102', + newEmail: 'sayan@gmail.com', +}; + export const testScenariosForV1API: ProxyV1TestData[] = [ { id: 'ITERABLE_v1_other_scenario_1', @@ -237,7 +274,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ }, }, { - id: 'ITERABLE_v1_other_scenario_1', + id: 'ITERABLE_v1_other_scenario_4', name: 'iterable', description: '[Proxy API] :: Scenario to test correct identify Payload Response Handling from Destination', @@ -309,7 +346,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ }, }, { - id: 'ITERABLE_v1_other_scenario_2', + id: 'ITERABLE_v1_other_scenario_5', name: 'iterable', description: '[Proxy API] :: Scenario to test Malformed identify Payload Response Handling from Destination', @@ -360,7 +397,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ }, }, { - id: 'ITERABLE_v1_other_scenario_3', + id: 'ITERABLE_v1_other_scenario_6', name: 'iterable', description: '[Proxy API] :: Scenario to test partially successful identify Response Handling from Destination', @@ -431,4 +468,357 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ }, }, }, + { + id: 'ITERABLE_v1_other_scenario_7', + name: 'iterable', + description: + '[Proxy API] :: Scenario to test partially unsuccessful updateEmail Response Handling from Destination', + successCriteria: 'Should return 400 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: updateEmailData, + headers: headerBlockWithCorrectAccessToken, + endpoint: 'https://api.iterable.com/api/users/updateEmail', + }, + singleMetadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags, + message: + 'ITERABLE: Error transformer proxy during ITERABLE response transformation. null', + response: [ + { + statusCode: 400, + metadata: generateMetadata(1), + error: + '{"msg":"Invalid currentEmail sayan","code":"InvalidEmailAddressError","params":null}', + }, + ], + }, + }, + }, + }, + }, + { + id: 'ITERABLE_v1_other_scenario_8', + name: 'iterable', + description: + '[Proxy API] :: Scenario to test single track correct Payload Response Handling from Destination', + successCriteria: 'Should return 200 status code with success', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: singleTrackPayload, + headers: headerBlockWithCorrectAccessToken, + endpoint: 'https://api.iterable.com/api/events/track', + }, + singleMetadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[ITERABLE Response Handler] - Request Processed Successfully', + destinationResponse: { + status: 200, + response: { + msg: 'Event with id: 1234 tracked.', + code: 'Success', + params: { + id: '1234', + }, + }, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + ], + }, + }, + }, + }, + }, + { + id: 'ITERABLE_v1_other_scenario_9', + name: 'iterable', + description: + '[Proxy API] :: Scenario to wrong sinle track event Response Handling from Destination', + successCriteria: 'Should return 400 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: { ...singleTrackPayload, email: 'sayan' }, + headers: headerBlockWithCorrectAccessToken, + endpoint: 'https://api.iterable.com/api/events/track', + }, + singleMetadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags, + message: + 'ITERABLE: Error transformer proxy during ITERABLE response transformation. null', + response: [ + { + statusCode: 400, + metadata: generateMetadata(1), + error: + '{"msg":"Invalid email: sayan","code":"InvalidEmailAddressError","params":null}', + }, + ], + }, + }, + }, + }, + }, + { + id: 'ITERABLE_v1_other_scenario_10', + name: 'iterable', + description: + '[Proxy API] :: Scenario to wrong single track event for catalogs Response Handling from Destination', + successCriteria: 'Should return 400 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: { + documents: { + Tiffany: { ruchira: 'donaldbaker@ellis.com', new_field2: 'GB' }, + ABC: { ruchira: 'abc@ellis.com', new_field2: 'GB1' }, + }, + replaceUploadedFieldsOnly: true, + }, + headers: headerBlockWithCorrectAccessToken, + endpoint: 'https://api.iterable.com/api/catalogs/rudder-test/items', + }, + singleMetadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 404, + statTags, + message: + 'ITERABLE: Error transformer proxy during ITERABLE response transformation. "Catalog not found: rudder-test"', + response: [ + { + statusCode: 404, + metadata: generateMetadata(1), + error: + '{"error":"NotFound","message":"Catalog not found: rudder-test","code":"error.catalogs.notFound","data":{"args":["rudder-test"]}}', + }, + ], + }, + }, + }, + }, + }, + { + id: 'ITERABLE_v1_other_scenario_11', + name: 'iterable', + description: + '[Proxy API] :: Scenario to test catalog track correct Payload Response Handling from Destination', + successCriteria: 'Should return 200 status code with success', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: { + documents: { + Tiffany: { ruchira: 'donaldbaker@ellis.com', new_field2: 'GB' }, + ABC: { ruchira: 'abc@ellis.com', new_field2: 'GB1' }, + }, + replaceUploadedFieldsOnly: true, + }, + headers: headerBlockWithCorrectAccessToken, + endpoint: 'https://api.iterable.com/api/catalogs/test-ruchira/items', + }, + singleMetadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[ITERABLE Response Handler] - Request Processed Successfully', + destinationResponse: { + status: 200, + response: { + code: 'Success', + msg: 'Request to bulk-upload documents into test-ruchira processed successfully', + params: null, + }, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + ], + }, + }, + }, + }, + }, + { + id: 'ITERABLE_v1_other_scenario_12', + name: 'iterable', + description: + '[Proxy API] :: Scenario to correct device token registration event Response Handling from Destination with wrong permission', + successCriteria: 'Should return 400 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: { + email: 'sayan@gmail.com', + device: { + token: '1234', + platform: 'APNS', + applicationName: 'rudder', + dataFields: {}, + }, + userId: 'abcdeeeeeeeexxxx102', + preferUserId: true, + }, + headers: headerBlockWithCorrectAccessToken, + endpoint: 'https://api.iterable.com/api/users/registerDeviceToken', + }, + singleMetadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 401, + statTags, + message: + 'ITERABLE: Error transformer proxy during ITERABLE response transformation. {"ip":"103.189.130.133","endpoint":"/api/users/registerDeviceToken","apiKeyIdentifier":"af831922","apiKeyType":"ServerSide"}', + response: [ + { + statusCode: 401, + metadata: generateMetadata(1), + error: + '{"msg":"Disabled API key or insufficient privileges","code":"BadApiKey","params":{"ip":"103.189.130.133","endpoint":"/api/users/registerDeviceToken","apiKeyIdentifier":"af831922","apiKeyType":"ServerSide"}}', + }, + ], + }, + }, + }, + }, + }, + { + id: 'ITERABLE_v1_other_scenario_13', + name: 'iterable', + description: + '[Proxy API] :: Scenario to correct browser token registration event Response Handling from Destination with wrong permission', + successCriteria: 'Should return 400 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: { + email: 'sayan@gmail.com', + browserToken: '1234567', + userId: 'abcdeeeeeeeexxxx102', + }, + headers: headerBlockWithCorrectAccessToken, + endpoint: 'https://api.iterable.com/api/users/registerBrowserToken', + }, + singleMetadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 401, + statTags, + message: + 'ITERABLE: Error transformer proxy during ITERABLE response transformation. {"ip":"103.189.130.129","endpoint":"/api/users/registerBrowserToken","apiKeyIdentifier":"af831922","apiKeyType":"ServerSide"}', + response: [ + { + statusCode: 401, + metadata: generateMetadata(1), + error: + '{"msg":"Disabled API key or insufficient privileges","code":"BadApiKey","params":{"ip":"103.189.130.129","endpoint":"/api/users/registerBrowserToken","apiKeyIdentifier":"af831922","apiKeyType":"ServerSide"}}', + }, + ], + }, + }, + }, + }, + }, ]; diff --git a/test/integrations/destinations/iterable/dataDelivery/network.ts b/test/integrations/destinations/iterable/dataDelivery/network.ts index e14d8a76d6d..72189581be9 100644 --- a/test/integrations/destinations/iterable/dataDelivery/network.ts +++ b/test/integrations/destinations/iterable/dataDelivery/network.ts @@ -370,6 +370,206 @@ const businessMockData = [ status: 400, }, }, + { + description: 'Mock response from destination depicting update email request with a wrong data', + httpReq: { + method: 'POST', + url: 'https://api.iterable.com/api/users/updateEmail', + headers: headerBlockWithCorrectAccessToken, + data: { + currentEmail: 'sayan', + currentUserId: 'abcdeeeeeeeexxxx102', + newEmail: 'sayan@gmail.com', + }, + }, + httpRes: { + data: { + msg: 'Invalid currentEmail sayan', + code: 'InvalidEmailAddressError', + params: null, + }, + status: 400, + }, + }, + { + description: + 'Mock response from destination depicting request with a correct single track payload', + httpReq: { + method: 'POST', + url: 'https://api.iterable.com/api/events/track', + headers: headerBlockWithCorrectAccessToken, + data: { + email: 'sayan@gmail.com', + userId: 'abcdeeeeeeeexxxx102', + eventName: 'Email Opened', + id: '1234', + createdAt: 1598631966468, + dataFields: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', + }, + campaignId: 0, + templateId: 0, + createNewFields: true, + }, + }, + httpRes: { + data: { + msg: 'Event with id: 1234 tracked.', + code: 'Success', + params: { + id: '1234', + }, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response from destination depicting request with a wrong email single track payload', + httpReq: { + method: 'POST', + url: 'https://api.iterable.com/api/events/track', + headers: headerBlockWithCorrectAccessToken, + data: { + email: 'sayan', + userId: 'abcdeeeeeeeexxxx102', + eventName: 'Email Opened', + id: '1234', + createdAt: 1598631966468, + dataFields: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', + }, + campaignId: 0, + templateId: 0, + createNewFields: true, + }, + }, + httpRes: { + data: { + msg: 'Invalid email: sayan', + code: 'InvalidEmailAddressError', + params: null, + }, + status: 400, + }, + }, + { + description: + 'Mock response from destination depicting request with a correct catalog bulk payload', + httpReq: { + method: 'POST', + url: 'https://api.iterable.com/api/catalogs/rudder-test/items', + headers: headerBlockWithCorrectAccessToken, + data: { + documents: { + Tiffany: { ruchira: 'donaldbaker@ellis.com', new_field2: 'GB' }, + ABC: { ruchira: 'abc@ellis.com', new_field2: 'GB1' }, + }, + replaceUploadedFieldsOnly: true, + }, + }, + httpRes: { + data: { + error: 'NotFound', + message: 'Catalog not found: rudder-test', + code: 'error.catalogs.notFound', + data: { + args: ['rudder-test'], + }, + }, + status: 404, + }, + }, + { + description: + 'Mock response from destination depicting request with a correct catalog track payload', + httpReq: { + method: 'POST', + url: 'https://api.iterable.com/api/catalogs/test-ruchira/items', + headers: headerBlockWithCorrectAccessToken, + data: { + documents: { + Tiffany: { ruchira: 'donaldbaker@ellis.com', new_field2: 'GB' }, + ABC: { ruchira: 'abc@ellis.com', new_field2: 'GB1' }, + }, + replaceUploadedFieldsOnly: true, + }, + }, + httpRes: { + data: { + msg: 'Request to bulk-upload documents into test-ruchira processed successfully', + code: 'Success', + params: null, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response from destination depicting request with a correct register device token payload with insufficient permission', + httpReq: { + method: 'POST', + url: 'https://api.iterable.com/api/users/registerDeviceToken', + headers: headerBlockWithCorrectAccessToken, + data: { + email: 'sayan@gmail.com', + device: { + token: '1234', + platform: 'APNS', + applicationName: 'rudder', + dataFields: {}, + }, + userId: 'abcdeeeeeeeexxxx102', + preferUserId: true, + }, + }, + httpRes: { + data: { + msg: 'Disabled API key or insufficient privileges', + code: 'BadApiKey', + params: { + ip: '103.189.130.133', + endpoint: '/api/users/registerDeviceToken', + apiKeyIdentifier: 'af831922', + apiKeyType: 'ServerSide', + }, + }, + status: 401, + }, + }, + { + description: + 'Mock response from destination depicting request with a correct registerbrowswer token payload with insufficient permission', + httpReq: { + method: 'POST', + url: 'https://api.iterable.com/api/users/registerBrowserToken', + headers: headerBlockWithCorrectAccessToken, + data: { + email: 'sayan@gmail.com', + browserToken: '1234567', + userId: 'abcdeeeeeeeexxxx102', + }, + }, + httpRes: { + data: { + msg: 'Disabled API key or insufficient privileges', + code: 'BadApiKey', + params: { + ip: '103.189.130.129', + endpoint: '/api/users/registerBrowserToken', + apiKeyIdentifier: 'af831922', + apiKeyType: 'ServerSide', + }, + }, + status: 401, + }, + }, ]; export const networkCallsData = [...businessMockData]; From 3789652fbe252124ab730afe64bfe6cf84ed4bb9 Mon Sep 17 00:00:00 2001 From: Sankeerth Date: Sat, 7 Dec 2024 11:18:11 +0530 Subject: [PATCH 11/21] chore: improve iterable network handler (#3918) * chore: improve iterable network handler * chore: add comment in principal strategy class * chore: rename from PrincipalStrategy to BaseStrategy * chore: update expect-error comment --------- Co-authored-by: Sai Sankeerth --- .../destinations/iterable/networkHandler.ts | 12 ++-- .../base.ts} | 34 +++++------ .../generic.ts} | 18 ++---- .../track-identify.ts} | 33 +++-------- src/v1/destinations/iterable/type.ts | 26 --------- src/v1/destinations/iterable/types.ts | 57 +++++++++++++++++++ .../iterable/dataDelivery/business.ts | 4 +- 7 files changed, 94 insertions(+), 90 deletions(-) rename src/v1/destinations/iterable/{responseStrategy.ts => strategies/base.ts} (51%) rename src/v1/destinations/iterable/{commonStrategy.ts => strategies/generic.ts} (54%) rename src/v1/destinations/iterable/{trackIdentifyStrategy.ts => strategies/track-identify.ts} (58%) delete mode 100644 src/v1/destinations/iterable/type.ts create mode 100644 src/v1/destinations/iterable/types.ts diff --git a/src/v1/destinations/iterable/networkHandler.ts b/src/v1/destinations/iterable/networkHandler.ts index b409facc4fe..06ab77b3170 100644 --- a/src/v1/destinations/iterable/networkHandler.ts +++ b/src/v1/destinations/iterable/networkHandler.ts @@ -1,25 +1,25 @@ import { prepareProxyRequest, proxyRequest } from '../../../adapters/network'; import { processAxiosResponse } from '../../../adapters/utils/networkUtils'; import { BULK_ENDPOINTS } from '../../../v0/destinations/iterable/config'; -import { CommonStrategy } from './commonStrategy'; -import { TrackIdentifyStrategy } from './trackIdentifyStrategy'; +import { GenericStrategy } from './strategies/generic'; +import { TrackIdentifyStrategy } from './strategies/track-identify'; -interface ResponseParams { +type ResponseParams = { destinationRequest: { endpoint: string; }; -} +}; const strategyRegistry: { [key: string]: any } = { [TrackIdentifyStrategy.name]: new TrackIdentifyStrategy(), - [CommonStrategy.name]: new CommonStrategy(), + [GenericStrategy.name]: new GenericStrategy(), }; const getResponseStrategy = (endpoint: string) => { if (BULK_ENDPOINTS.some((path) => endpoint.includes(path))) { return strategyRegistry[TrackIdentifyStrategy.name]; } - return strategyRegistry[CommonStrategy.name]; + return strategyRegistry[GenericStrategy.name]; }; const responseHandler = (responseParams: ResponseParams) => { diff --git a/src/v1/destinations/iterable/responseStrategy.ts b/src/v1/destinations/iterable/strategies/base.ts similarity index 51% rename from src/v1/destinations/iterable/responseStrategy.ts rename to src/v1/destinations/iterable/strategies/base.ts index 9c2d1eeb406..348bfe7fc7a 100644 --- a/src/v1/destinations/iterable/responseStrategy.ts +++ b/src/v1/destinations/iterable/strategies/base.ts @@ -1,14 +1,12 @@ -import { CommonResponse } from './type'; - -const { isHttpStatusSuccess } = require('../../../v0/util/index'); -const { TransformerProxyError } = require('../../../v0/util/errorTypes'); -const { getDynamicErrorType } = require('../../../adapters/utils/networkUtils'); -const tags = require('../../../v0/util/tags'); - -class ResponseStrategy { - handleResponse(responseParams: { - destinationResponse: { status: number; response: CommonResponse }; - }): void { +import { TAG_NAMES } from '@rudderstack/integrations-lib'; +import { getDynamicErrorType } from '../../../../adapters/utils/networkUtils'; +import { isHttpStatusSuccess } from '../../../../v0/util'; +import { TransformerProxyError } from '../../../../v0/util/errorTypes'; +import { DestinationResponse, ResponseParams } from '../types'; + +// Base strategy is the base class for all strategies in Iterable destination +class BaseStrategy { + handleResponse(responseParams: { destinationResponse: DestinationResponse }): void { const { destinationResponse } = responseParams; const { status } = destinationResponse; @@ -22,14 +20,12 @@ class ResponseStrategy { return this.handleSuccess(responseParams); } - handleError(responseParams): void { + handleError(responseParams: ResponseParams): void { const { destinationResponse, rudderJobMetadata } = responseParams; const { response, status } = destinationResponse; - const errorMessage = - JSON.stringify(response.params) || - JSON.stringify(response.msg) || - JSON.stringify(response.message) || - 'unknown error format'; + // @ts-expect-error: not sure if `response.message` is correct or needed + const responseMessage = response.params || response.msg || response.message; + const errorMessage = JSON.stringify(responseMessage) || 'unknown error format'; const responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ statusCode: status, @@ -40,7 +36,7 @@ class ResponseStrategy { throw new TransformerProxyError( `ITERABLE: Error transformer proxy during ITERABLE response transformation. ${errorMessage}`, status, - { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status) }, + { [TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status) }, destinationResponse, '', responseWithIndividualEvents, @@ -52,4 +48,4 @@ class ResponseStrategy { } } -export { ResponseStrategy }; +export { BaseStrategy }; diff --git a/src/v1/destinations/iterable/commonStrategy.ts b/src/v1/destinations/iterable/strategies/generic.ts similarity index 54% rename from src/v1/destinations/iterable/commonStrategy.ts rename to src/v1/destinations/iterable/strategies/generic.ts index 5d6aa2a8920..27028756108 100644 --- a/src/v1/destinations/iterable/commonStrategy.ts +++ b/src/v1/destinations/iterable/strategies/generic.ts @@ -1,17 +1,11 @@ -import { CommonResponse } from './type'; +import { BaseStrategy } from './base'; +import { DestinationResponse, SuccessResponse } from '../types'; -const { ResponseStrategy } = require('./responseStrategy'); - -class CommonStrategy extends ResponseStrategy { +class GenericStrategy extends BaseStrategy { handleSuccess(responseParams: { - destinationResponse: { status: number; response: CommonResponse }; + destinationResponse: DestinationResponse; rudderJobMetadata: any[]; - }): { - status: number; - message: string; - destinationResponse: { status: number; response: CommonResponse }; - response: { statusCode: number; metadata: any; error: string }[]; - } { + }): SuccessResponse { const { destinationResponse, rudderJobMetadata } = responseParams; const { status } = destinationResponse; @@ -30,4 +24,4 @@ class CommonStrategy extends ResponseStrategy { } } -export { CommonStrategy }; +export { GenericStrategy }; diff --git a/src/v1/destinations/iterable/trackIdentifyStrategy.ts b/src/v1/destinations/iterable/strategies/track-identify.ts similarity index 58% rename from src/v1/destinations/iterable/trackIdentifyStrategy.ts rename to src/v1/destinations/iterable/strategies/track-identify.ts index 5ca5d4ae84d..7c242f3ac79 100644 --- a/src/v1/destinations/iterable/trackIdentifyStrategy.ts +++ b/src/v1/destinations/iterable/strategies/track-identify.ts @@ -1,36 +1,19 @@ -import { ResponseStrategy } from './responseStrategy'; -import { checkIfEventIsAbortableAndExtractErrorMessage } from '../../../v0/destinations/iterable/util'; -import { CommonResponse } from './type'; +import { BaseStrategy } from './base'; +import { DestinationResponse, ResponseParams, Response } from '../types'; +import { checkIfEventIsAbortableAndExtractErrorMessage } from '../../../../v0/destinations/iterable/util'; -interface ResponseParams { - destinationResponse: { status: number; response: CommonResponse }; - rudderJobMetadata: any[]; - destinationRequest: { - body: { - JSON: { - events?: any[]; - users?: any[]; - }; - }; - }; -} - -class TrackIdentifyStrategy extends ResponseStrategy { +class TrackIdentifyStrategy extends BaseStrategy { handleSuccess(responseParams: ResponseParams): { status: number; message: string; - destinationResponse: { status: number; response: CommonResponse }; - response: Array<{ statusCode: number; metadata: any; error: string }>; + destinationResponse: DestinationResponse; + response: Response[]; } { const { destinationResponse, rudderJobMetadata, destinationRequest } = responseParams; const { status } = destinationResponse; - const responseWithIndividualEvents: Array<{ - statusCode: number; - metadata: any; - error: string; - }> = []; + const responseWithIndividualEvents: Response[] = []; - const { events, users } = destinationRequest.body.JSON; + const { events, users } = destinationRequest?.body.JSON || {}; const finalData = events || users; if (finalData) { diff --git a/src/v1/destinations/iterable/type.ts b/src/v1/destinations/iterable/type.ts deleted file mode 100644 index e29e6236e66..00000000000 --- a/src/v1/destinations/iterable/type.ts +++ /dev/null @@ -1,26 +0,0 @@ -interface FailedUpdates { - invalidEmails?: string[]; - invalidUserIds?: string[]; - notFoundEmails?: string[]; - notFoundUserIds?: string[]; - invalidDataEmails?: string[]; - invalidDataUserIds?: string[]; - conflictEmails?: string[]; - conflictUserIds?: string[]; - forgottenEmails?: string[]; - forgottenUserIds?: string[]; -} - -export interface CommonResponse { - msg?: string; - code?: string; - params?: Record; - successCount?: number; - failCount?: number; - invalidEmails?: string[]; - invalidUserIds?: string[]; - filteredOutFields?: string[]; - createdFields?: string[]; - disallowedEventNames?: string[]; - failedUpdates?: FailedUpdates; -} diff --git a/src/v1/destinations/iterable/types.ts b/src/v1/destinations/iterable/types.ts new file mode 100644 index 00000000000..65f5a3416f2 --- /dev/null +++ b/src/v1/destinations/iterable/types.ts @@ -0,0 +1,57 @@ +type FailedUpdates = { + invalidEmails?: string[]; + invalidUserIds?: string[]; + notFoundEmails?: string[]; + notFoundUserIds?: string[]; + invalidDataEmails?: string[]; + invalidDataUserIds?: string[]; + conflictEmails?: string[]; + conflictUserIds?: string[]; + forgottenEmails?: string[]; + forgottenUserIds?: string[]; +}; + +export type GeneralApiResponse = { + msg?: string; + code?: string; + params?: Record; + successCount?: number; + failCount?: number; + invalidEmails?: string[]; + invalidUserIds?: string[]; + filteredOutFields?: string[]; + createdFields?: string[]; + disallowedEventNames?: string[]; + failedUpdates?: FailedUpdates; +}; + +export type DestinationResponse = { + status: number; + response: GeneralApiResponse; +}; + +export type ResponseParams = { + destinationResponse: DestinationResponse; + rudderJobMetadata: any[]; + destinationRequest?: { + body: { + JSON: { + events?: any[]; + users?: any[]; + }; + }; + }; +}; + +export type Response = { + statusCode: number; + metadata: any; + error: string; +}; + +export type SuccessResponse = { + status: number; + message: string; + destinationResponse: DestinationResponse; + response: Response[]; +}; diff --git a/test/integrations/destinations/iterable/dataDelivery/business.ts b/test/integrations/destinations/iterable/dataDelivery/business.ts index ec40c29b40f..df75c8fd1d5 100644 --- a/test/integrations/destinations/iterable/dataDelivery/business.ts +++ b/test/integrations/destinations/iterable/dataDelivery/business.ts @@ -499,7 +499,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ status: 400, statTags, message: - 'ITERABLE: Error transformer proxy during ITERABLE response transformation. null', + 'ITERABLE: Error transformer proxy during ITERABLE response transformation. "Invalid currentEmail sayan"', response: [ { statusCode: 400, @@ -596,7 +596,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ status: 400, statTags, message: - 'ITERABLE: Error transformer proxy during ITERABLE response transformation. null', + 'ITERABLE: Error transformer proxy during ITERABLE response transformation. "Invalid email: sayan"', response: [ { statusCode: 400, From 9ee284721d10d05db82bfcc6b431ea24940025fe Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Thu, 12 Dec 2024 18:14:56 +0530 Subject: [PATCH 12/21] fix: review comments addressed --- src/v0/destinations/iterable/config.js | 19 ++++---- src/v0/destinations/iterable/util.js | 56 +++++++++++++---------- src/v0/destinations/iterable/util.test.js | 30 +++++++++--- 3 files changed, 66 insertions(+), 39 deletions(-) diff --git a/src/v0/destinations/iterable/config.js b/src/v0/destinations/iterable/config.js index 576717504e9..a0be8544aa5 100644 --- a/src/v0/destinations/iterable/config.js +++ b/src/v0/destinations/iterable/config.js @@ -87,19 +87,21 @@ const IDENTIFY_MAX_BODY_SIZE_IN_BYTES = 4000000; const TRACK_MAX_BATCH_SIZE = 8000; -const API_RESPONSE_PATHS = [ - 'invalidEmails', +const ITERABLE_RESPONSE_USER_ID_PATHS = [ 'invalidUserIds', - 'disallowedEventNames', - 'failedUpdates.invalidEmails', 'failedUpdates.invalidUserIds', - 'failedUpdates.notFoundEmails', 'failedUpdates.notFoundUserIds', - 'failedUpdates.forgottenEmails', 'failedUpdates.forgottenUserIds', 'failedUpdates.conflictUserIds', - 'failedUpdates.conflictEmails', 'failedUpdates.invalidDataUserIds', +]; + +const ITERABLE_RESPONSE_EMAIL_PATHS = [ + 'invalidEmails', + 'failedUpdates.invalidEmails', + 'failedUpdates.notFoundEmails', + 'failedUpdates.forgottenEmails', + 'failedUpdates.conflictEmails', 'failedUpdates.invalidDataEmails', ]; @@ -110,6 +112,7 @@ module.exports = { TRACK_MAX_BATCH_SIZE, IDENTIFY_MAX_BATCH_SIZE, IDENTIFY_MAX_BODY_SIZE_IN_BYTES, - API_RESPONSE_PATHS, + ITERABLE_RESPONSE_USER_ID_PATHS, + ITERABLE_RESPONSE_EMAIL_PATHS, BULK_ENDPOINTS, }; diff --git a/src/v0/destinations/iterable/util.js b/src/v0/destinations/iterable/util.js index 2a604f9beaa..05f60750963 100644 --- a/src/v0/destinations/iterable/util.js +++ b/src/v0/destinations/iterable/util.js @@ -16,8 +16,10 @@ const { TRACK_MAX_BATCH_SIZE, IDENTIFY_MAX_BATCH_SIZE, IDENTIFY_MAX_BODY_SIZE_IN_BYTES, - API_RESPONSE_PATHS, + // API_RESPONSE_PATHS, constructEndpoint, + ITERABLE_RESPONSE_USER_ID_PATHS, + ITERABLE_RESPONSE_EMAIL_PATHS, } = require('./config'); const { JSON_MIME_TYPE } = require('../../util/constant'); const { EventType, MappedToDestinationKey } = require('../../../constants'); @@ -772,39 +774,44 @@ function checkIfEventIsAbortableAndExtractErrorMessage(event, destinationRespons return { isAbortable: false, errorMsg: '' }; } - // Flatten dataFields values into a single array - const dataFieldsValues = event.dataFields ? Object.values(event.dataFields).flat() : []; - - const eventValues = new Set( - [ - event.email, - event.preferUserId, - event.mergeNestedObjects, - event.userId, - event.eventName, - event.id, - event.createdAt, - event.campaignId, - event.templateId, - event.createNewFields, - ...dataFieldsValues, // Spread the flattened dataFields values - ].filter((value) => value !== undefined), - ); + const eventValues = { + email: event.email, + userId: event.userId, + eventName: event.eventName, + }; - const matchingPath = API_RESPONSE_PATHS.find((path) => { + const isValueInResponseArray = (path, value) => { const responseArray = path .split('.') .reduce((obj, key) => obj?.[key], destinationResponse.response); + return Array.isArray(responseArray) && responseArray.includes(value); + }; - return Array.isArray(responseArray) && responseArray.some((value) => eventValues.has(value)); - }); + const matchingPath = + ITERABLE_RESPONSE_USER_ID_PATHS.find((userIdPath) => + isValueInResponseArray(userIdPath, eventValues.userId), + ) || + ITERABLE_RESPONSE_EMAIL_PATHS.find((emailPath) => + isValueInResponseArray(emailPath, eventValues.email), + ) || + isValueInResponseArray('disallowedEventNames', eventValues.eventName); if (matchingPath) { const responseArray = matchingPath .split('.') .reduce((obj, key) => obj?.[key], destinationResponse.response); - - const matchingValue = responseArray.find((value) => eventValues.has(value)); + const matchingValue = responseArray.find((value) => { + if (ITERABLE_RESPONSE_EMAIL_PATHS.some((emailPath) => matchingPath.includes(emailPath))) { + return value === eventValues.email; + } + if (ITERABLE_RESPONSE_USER_ID_PATHS.some((userIdPath) => matchingPath.includes(userIdPath))) { + return value === eventValues.userId; + } + if (matchingPath.includes('disallowedEventNames')) { + return value === eventValues.eventName; + } + return false; + }); return { isAbortable: true, @@ -812,7 +819,6 @@ function checkIfEventIsAbortableAndExtractErrorMessage(event, destinationRespons }; } - // Return false and an empty error message if no error is found return { isAbortable: false, errorMsg: '' }; } diff --git a/src/v0/destinations/iterable/util.test.js b/src/v0/destinations/iterable/util.test.js index 52032a38dc3..ea3d7ef6173 100644 --- a/src/v0/destinations/iterable/util.test.js +++ b/src/v0/destinations/iterable/util.test.js @@ -864,10 +864,9 @@ describe('iterable utils test', () => { // Returns appropriate error message for abortable event - // Processes events with additional dataFields correctly - it('should process events with additional dataFields correctly', () => { + it('should find the right value for which it should fail and passes otherwise', () => { const event = { - email: 'test@example.com', + email: 'test', userId: 'user123', eventName: 'purchase', dataFields: { customField1: 'value1', customField2: 'value2' }, @@ -876,16 +875,35 @@ describe('iterable utils test', () => { response: { failCount: 1, failedUpdates: { - invalidDataEmails: ['value1'], + invalidEmails: ['test'], }, }, }; const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); expect(result).toEqual({ isAbortable: true, - errorMsg: - 'Request failed for value "value1" because it is "failedUpdates.invalidDataEmails".', + errorMsg: 'Request failed for value "test" because it is "failedUpdates.invalidEmails".', }); }); + + it('should find the right value for which it should fail', () => { + const event = { + email: 'test@gmail.com', + userId: 'user123', + eventName: 'purchase', + dataFields: { customField1: 'test', customField2: 'value2' }, + }; + const destinationResponse = { + response: { + failCount: 1, + failedUpdates: { + invalidEmails: ['test'], + }, + }, + }; + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + expect(result.isAbortable).toBe(false); + expect(result.errorMsg).toBe(''); + }); }); }); From a5ba58c63eb0b9e87e76720f2a24ffacbcd666d8 Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Thu, 12 Dec 2024 19:06:07 +0530 Subject: [PATCH 13/21] fix: small refactoring --- src/v0/destinations/iterable/util.js | 43 +++++++++++++++++----------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/src/v0/destinations/iterable/util.js b/src/v0/destinations/iterable/util.js index 05f60750963..b61687536dd 100644 --- a/src/v0/destinations/iterable/util.js +++ b/src/v0/destinations/iterable/util.js @@ -748,6 +748,22 @@ const filterEventsAndPrepareBatchRequests = (transformedEvents) => { return prepareBatchRequests(filteredEvents); }; +// Helper function to get value from response using a path +const getValueFromResponse = (path, response) => + path.split('.').reduce((obj, key) => obj?.[key], response); + +/** + * Checks if a value is present in a response array based on a given path. + * @param {string} path - The path to the response array. + * @param {any} value - The value to check for in the array. + * @param {Object} response - The response object to search within. + * @returns {boolean} - True if the value is in the array, otherwise false. + */ +const isValueInResponseArray = (path, value, response) => { + const responseValueArray = getValueFromResponse(path, response); + return Array.isArray(responseValueArray) && responseValueArray.includes(value); +}; + /** * Determines if an event should be aborted based on the response from a destination * and extracts an error message if applicable. @@ -767,7 +783,7 @@ const filterEventsAndPrepareBatchRequests = (transformedEvents) => { * @returns {Object} An object containing a boolean `isAbortable` indicating if the event * should be aborted, and an `errorMsg` string with the error message if applicable. */ -function checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse) { +const checkIfEventIsAbortableAndExtractErrorMessage = (event, destinationResponse) => { const { failCount } = destinationResponse.response; if (failCount === 0) { @@ -780,27 +796,22 @@ function checkIfEventIsAbortableAndExtractErrorMessage(event, destinationRespons eventName: event.eventName, }; - const isValueInResponseArray = (path, value) => { - const responseArray = path - .split('.') - .reduce((obj, key) => obj?.[key], destinationResponse.response); - return Array.isArray(responseArray) && responseArray.includes(value); - }; - const matchingPath = ITERABLE_RESPONSE_USER_ID_PATHS.find((userIdPath) => - isValueInResponseArray(userIdPath, eventValues.userId), + isValueInResponseArray(userIdPath, eventValues.userId, destinationResponse.response), ) || ITERABLE_RESPONSE_EMAIL_PATHS.find((emailPath) => - isValueInResponseArray(emailPath, eventValues.email), + isValueInResponseArray(emailPath, eventValues.email, destinationResponse.response), ) || - isValueInResponseArray('disallowedEventNames', eventValues.eventName); + isValueInResponseArray( + 'disallowedEventNames', + eventValues.eventName, + destinationResponse.response, + ); if (matchingPath) { - const responseArray = matchingPath - .split('.') - .reduce((obj, key) => obj?.[key], destinationResponse.response); - const matchingValue = responseArray.find((value) => { + const responseValueArray = getValueFromResponse(matchingPath, destinationResponse.response); + const matchingValue = responseValueArray.find((value) => { if (ITERABLE_RESPONSE_EMAIL_PATHS.some((emailPath) => matchingPath.includes(emailPath))) { return value === eventValues.email; } @@ -820,7 +831,7 @@ function checkIfEventIsAbortableAndExtractErrorMessage(event, destinationRespons } return { isAbortable: false, errorMsg: '' }; -} +}; module.exports = { getCatalogEndpoint, From d00f31f93590fc99d2c69dbb678efbd54259c4f3 Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Thu, 12 Dec 2024 19:54:04 +0530 Subject: [PATCH 14/21] fix: update for supporting disallowed events --- src/v0/destinations/iterable/util.js | 8 ++-- src/v0/destinations/iterable/util.test.js | 48 ++++++++++++++++++++++- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/v0/destinations/iterable/util.js b/src/v0/destinations/iterable/util.js index b61687536dd..8e8cf5aff37 100644 --- a/src/v0/destinations/iterable/util.js +++ b/src/v0/destinations/iterable/util.js @@ -803,11 +803,13 @@ const checkIfEventIsAbortableAndExtractErrorMessage = (event, destinationRespons ITERABLE_RESPONSE_EMAIL_PATHS.find((emailPath) => isValueInResponseArray(emailPath, eventValues.email, destinationResponse.response), ) || - isValueInResponseArray( + (isValueInResponseArray( 'disallowedEventNames', eventValues.eventName, destinationResponse.response, - ); + ) + ? 'disallowedEventNames' + : null); if (matchingPath) { const responseValueArray = getValueFromResponse(matchingPath, destinationResponse.response); @@ -818,7 +820,7 @@ const checkIfEventIsAbortableAndExtractErrorMessage = (event, destinationRespons if (ITERABLE_RESPONSE_USER_ID_PATHS.some((userIdPath) => matchingPath.includes(userIdPath))) { return value === eventValues.userId; } - if (matchingPath.includes('disallowedEventNames')) { + if (matchingPath === 'disallowedEventNames') { return value === eventValues.eventName; } return false; diff --git a/src/v0/destinations/iterable/util.test.js b/src/v0/destinations/iterable/util.test.js index ea3d7ef6173..6a9881027ae 100644 --- a/src/v0/destinations/iterable/util.test.js +++ b/src/v0/destinations/iterable/util.test.js @@ -864,7 +864,7 @@ describe('iterable utils test', () => { // Returns appropriate error message for abortable event - it('should find the right value for which it should fail and passes otherwise', () => { + it('should find the right value for which it should fail and passes otherwise for emails', () => { const event = { email: 'test', userId: 'user123', @@ -905,5 +905,51 @@ describe('iterable utils test', () => { expect(result.isAbortable).toBe(false); expect(result.errorMsg).toBe(''); }); + + it('should find the right value for which it should fail and passes otherwise for userIds', () => { + const event = { + email: 'test', + userId: 'user123', + eventName: 'purchase', + dataFields: { customField1: 'value1', customField2: 'value2' }, + }; + const destinationResponse = { + response: { + failCount: 1, + failedUpdates: { + invalidUserIds: ['user123'], + }, + }, + }; + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + expect(result).toEqual({ + isAbortable: true, + errorMsg: + 'Request failed for value "user123" because it is "failedUpdates.invalidUserIds".', + }); + }); + + it('should find the right value for which it should fail and passes otherwise for disallowed events', () => { + const event = { + email: 'test', + userId: 'user123', + eventName: 'purchase', + dataFields: { customField1: 'value1', customField2: 'value2' }, + }; + const destinationResponse = { + response: { + failCount: 1, + disallowedEventNames: ['purchase'], + failedUpdates: { + invalidUserIds: [], + }, + }, + }; + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + expect(result).toEqual({ + isAbortable: true, + errorMsg: 'Request failed for value "purchase" because it is "disallowedEventNames".', + }); + }); }); }); From 707dfe8220101bf7cd773f36262b08584bcd00d6 Mon Sep 17 00:00:00 2001 From: shrouti1507 <60211312+shrouti1507@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:42:37 +0530 Subject: [PATCH 15/21] fix: code review suggestion Co-authored-by: Sankeerth --- src/v0/destinations/iterable/util.js | 59 ++++++++++------------------ 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/src/v0/destinations/iterable/util.js b/src/v0/destinations/iterable/util.js index 8e8cf5aff37..9849bfadc20 100644 --- a/src/v0/destinations/iterable/util.js +++ b/src/v0/destinations/iterable/util.js @@ -748,20 +748,16 @@ const filterEventsAndPrepareBatchRequests = (transformedEvents) => { return prepareBatchRequests(filteredEvents); }; -// Helper function to get value from response using a path -const getValueFromResponse = (path, response) => - path.split('.').reduce((obj, key) => obj?.[key], response); - /** * Checks if a value is present in a response array based on a given path. + * @param {Object} response - The response object to search within. * @param {string} path - The path to the response array. * @param {any} value - The value to check for in the array. - * @param {Object} response - The response object to search within. * @returns {boolean} - True if the value is in the array, otherwise false. */ -const isValueInResponseArray = (path, value, response) => { - const responseValueArray = getValueFromResponse(path, response); - return Array.isArray(responseValueArray) && responseValueArray.includes(value); +const isValueInResponseArray = (response, path, value) => { + const respArr = get(response, path); + return Array.isArray(respArr) && respArr.includes(value); }; /** @@ -796,39 +792,26 @@ const checkIfEventIsAbortableAndExtractErrorMessage = (event, destinationRespons eventName: event.eventName, }; - const matchingPath = - ITERABLE_RESPONSE_USER_ID_PATHS.find((userIdPath) => - isValueInResponseArray(userIdPath, eventValues.userId, destinationResponse.response), - ) || - ITERABLE_RESPONSE_EMAIL_PATHS.find((emailPath) => - isValueInResponseArray(emailPath, eventValues.email, destinationResponse.response), - ) || - (isValueInResponseArray( - 'disallowedEventNames', - eventValues.eventName, - destinationResponse.response, - ) - ? 'disallowedEventNames' - : null); - - if (matchingPath) { - const responseValueArray = getValueFromResponse(matchingPath, destinationResponse.response); - const matchingValue = responseValueArray.find((value) => { - if (ITERABLE_RESPONSE_EMAIL_PATHS.some((emailPath) => matchingPath.includes(emailPath))) { - return value === eventValues.email; - } - if (ITERABLE_RESPONSE_USER_ID_PATHS.some((userIdPath) => matchingPath.includes(userIdPath))) { - return value === eventValues.userId; - } - if (matchingPath === 'disallowedEventNames') { - return value === eventValues.eventName; - } - return false; - }); + let errorMsg = ''; + const userIdMatchPath = ITERABLE_RESPONSE_USER_ID_PATHS.filter((userIdPath) => + isValueInResponseArray(destinationResponse.response, userIdPath, eventValues.userId), + ); + errorMsg += `userId error:"${eventValues.userId}" in "${userIdMatchPath}".`; + + const emailMatchPath = ITERABLE_RESPONSE_EMAIL_PATHS.filter((emailPath) => + isValueInResponseArray(destinationResponse.response, emailPath, eventValues.email), + ); + errorMsg += `email error:"${eventValues.email}" in "${emailMatchPath}".`; + + const eventNameMatchPath = ['disallowedEventNames'].filter((eventNamePath) => + isValueInResponseArray(destinationResponse.response, eventNamePath, eventValues.eventName), + ); + errorMsg += `eventName error:"${eventValues.eventName}" in "${eventNameMatchPath}".`; + if (errorMsg) { return { isAbortable: true, - errorMsg: `Request failed for value "${matchingValue}" because it is "${matchingPath}".`, + errorMsg, }; } From 8e5d6ab9d5deb393a6746a468e9ac929dc71d632 Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Mon, 16 Dec 2024 12:03:40 +0530 Subject: [PATCH 16/21] fix: fixing test cases --- src/v0/destinations/iterable/util.js | 14 +++++++++++--- src/v0/destinations/iterable/util.test.js | 7 +++---- .../destinations/iterable/dataDelivery/business.ts | 4 ++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/v0/destinations/iterable/util.js b/src/v0/destinations/iterable/util.js index 9849bfadc20..f7d2758948f 100644 --- a/src/v0/destinations/iterable/util.js +++ b/src/v0/destinations/iterable/util.js @@ -796,17 +796,25 @@ const checkIfEventIsAbortableAndExtractErrorMessage = (event, destinationRespons const userIdMatchPath = ITERABLE_RESPONSE_USER_ID_PATHS.filter((userIdPath) => isValueInResponseArray(destinationResponse.response, userIdPath, eventValues.userId), ); - errorMsg += `userId error:"${eventValues.userId}" in "${userIdMatchPath}".`; + if (userIdMatchPath.length > 0) { + errorMsg += `userId error:"${eventValues.userId}" in "${userIdMatchPath}".`; + } const emailMatchPath = ITERABLE_RESPONSE_EMAIL_PATHS.filter((emailPath) => isValueInResponseArray(destinationResponse.response, emailPath, eventValues.email), ); - errorMsg += `email error:"${eventValues.email}" in "${emailMatchPath}".`; + + if (emailMatchPath.length > 0) { + errorMsg += `email error:"${eventValues.email}" in "${emailMatchPath}".`; + } const eventNameMatchPath = ['disallowedEventNames'].filter((eventNamePath) => isValueInResponseArray(destinationResponse.response, eventNamePath, eventValues.eventName), ); - errorMsg += `eventName error:"${eventValues.eventName}" in "${eventNameMatchPath}".`; + + if (eventNameMatchPath.length > 0) { + errorMsg += `eventName error:"${eventValues.eventName}" in "${eventNameMatchPath}".`; + } if (errorMsg) { return { diff --git a/src/v0/destinations/iterable/util.test.js b/src/v0/destinations/iterable/util.test.js index 6a9881027ae..13db07873c3 100644 --- a/src/v0/destinations/iterable/util.test.js +++ b/src/v0/destinations/iterable/util.test.js @@ -882,7 +882,7 @@ describe('iterable utils test', () => { const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); expect(result).toEqual({ isAbortable: true, - errorMsg: 'Request failed for value "test" because it is "failedUpdates.invalidEmails".', + errorMsg: 'email error:"test" in "failedUpdates.invalidEmails".', }); }); @@ -924,8 +924,7 @@ describe('iterable utils test', () => { const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); expect(result).toEqual({ isAbortable: true, - errorMsg: - 'Request failed for value "user123" because it is "failedUpdates.invalidUserIds".', + errorMsg: 'userId error:"user123" in "failedUpdates.invalidUserIds".', }); }); @@ -948,7 +947,7 @@ describe('iterable utils test', () => { const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); expect(result).toEqual({ isAbortable: true, - errorMsg: 'Request failed for value "purchase" because it is "disallowedEventNames".', + errorMsg: 'eventName error:"purchase" in "disallowedEventNames".', }); }); }); diff --git a/test/integrations/destinations/iterable/dataDelivery/business.ts b/test/integrations/destinations/iterable/dataDelivery/business.ts index df75c8fd1d5..4e0c76e3f7c 100644 --- a/test/integrations/destinations/iterable/dataDelivery/business.ts +++ b/test/integrations/destinations/iterable/dataDelivery/business.ts @@ -260,7 +260,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ { statusCode: 400, metadata: generateMetadata(1), - error: 'Request failed for value "sayan" because it is "invalidEmails".', + error: 'email error:"sayan" in "invalidEmails,failedUpdates.invalidEmails".', }, { statusCode: 200, @@ -460,7 +460,7 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ { statusCode: 400, metadata: generateMetadata(2), - error: 'Request failed for value "shrouti" because it is "invalidEmails".', + error: 'email error:"shrouti" in "invalidEmails,failedUpdates.invalidEmails".', }, ], }, From 9ce457588bb7e1c766d585193f8f15f49a2add1d Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Mon, 16 Dec 2024 14:16:39 +0530 Subject: [PATCH 17/21] fix: review comment addressed --- src/v0/destinations/iterable/util.test.js | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/v0/destinations/iterable/util.test.js b/src/v0/destinations/iterable/util.test.js index 13db07873c3..39077f4850d 100644 --- a/src/v0/destinations/iterable/util.test.js +++ b/src/v0/destinations/iterable/util.test.js @@ -906,6 +906,30 @@ describe('iterable utils test', () => { expect(result.errorMsg).toBe(''); }); + it('should find all the matching paths it failed for and curate error message', () => { + const event = { + email: 'test', + userId: 'user123', + eventName: 'purchase', + dataFields: { customField1: 'test', customField2: 'value2' }, + }; + const destinationResponse = { + response: { + failCount: 1, + invalidEmails: ['test'], + failedUpdates: { + invalidEmails: ['test'], + conflictEmails: ['test'], + }, + }, + }; + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + expect(result.isAbortable).toBe(true); + expect(result.errorMsg).toBe( + 'email error:"test" in "invalidEmails,failedUpdates.invalidEmails,failedUpdates.conflictEmails".', + ); + }); + it('should find the right value for which it should fail and passes otherwise for userIds', () => { const event = { email: 'test', From de1bb5c3a9da7f881ce361ba1ce046626a62e42b Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Fri, 3 Jan 2025 17:47:11 +0530 Subject: [PATCH 18/21] fix: adding type definitions --- src/v0/destinations/iterable/config.js | 6 +- src/v0/destinations/iterable/util.js | 82 -------- src/v0/destinations/iterable/util.test.js | 177 ----------------- .../destinations/iterable/networkHandler.ts | 9 +- .../destinations/iterable/strategies/base.ts | 20 +- .../iterable/strategies/generic.ts | 9 +- .../iterable/strategies/track-identify.ts | 22 +-- src/v1/destinations/iterable/types.ts | 32 ++- src/v1/destinations/iterable/utils.test.ts | 184 ++++++++++++++++++ src/v1/destinations/iterable/utils.ts | 92 +++++++++ 10 files changed, 327 insertions(+), 306 deletions(-) create mode 100644 src/v1/destinations/iterable/utils.test.ts create mode 100644 src/v1/destinations/iterable/utils.ts diff --git a/src/v0/destinations/iterable/config.js b/src/v0/destinations/iterable/config.js index a0be8544aa5..e60e444c18a 100644 --- a/src/v0/destinations/iterable/config.js +++ b/src/v0/destinations/iterable/config.js @@ -20,7 +20,6 @@ const ConfigCategory = { name: 'IterableIdentifyConfig', action: 'identify', endpoint: `users/update`, - bulkEndpoint: 'users/bulkUpdate', }, PAGE: { name: 'IterablePageConfig', @@ -36,7 +35,6 @@ const ConfigCategory = { name: 'IterableTrackConfig', action: 'track', endpoint: `events/track`, - bulkEndpoint: 'events/trackBulk', }, TRACK_PURCHASE: { name: 'IterableTrackPurchaseConfig', @@ -78,9 +76,7 @@ const constructEndpoint = (dataCenter, category) => { return `${baseUrl}${category.endpoint}`; }; -const BULK_ENDPOINTS = Object.values(ConfigCategory) - .filter((config) => config.bulkEndpoint) - .map((config) => `/api/${config.bulkEndpoint}`); +const BULK_ENDPOINTS = ['/api/users/bulkUpdate', '/api/events/trackBulk']; const IDENTIFY_MAX_BATCH_SIZE = 1000; const IDENTIFY_MAX_BODY_SIZE_IN_BYTES = 4000000; diff --git a/src/v0/destinations/iterable/util.js b/src/v0/destinations/iterable/util.js index f7d2758948f..764d76f8825 100644 --- a/src/v0/destinations/iterable/util.js +++ b/src/v0/destinations/iterable/util.js @@ -16,10 +16,7 @@ const { TRACK_MAX_BATCH_SIZE, IDENTIFY_MAX_BATCH_SIZE, IDENTIFY_MAX_BODY_SIZE_IN_BYTES, - // API_RESPONSE_PATHS, constructEndpoint, - ITERABLE_RESPONSE_USER_ID_PATHS, - ITERABLE_RESPONSE_EMAIL_PATHS, } = require('./config'); const { JSON_MIME_TYPE } = require('../../util/constant'); const { EventType, MappedToDestinationKey } = require('../../../constants'); @@ -748,84 +745,6 @@ const filterEventsAndPrepareBatchRequests = (transformedEvents) => { return prepareBatchRequests(filteredEvents); }; -/** - * Checks if a value is present in a response array based on a given path. - * @param {Object} response - The response object to search within. - * @param {string} path - The path to the response array. - * @param {any} value - The value to check for in the array. - * @returns {boolean} - True if the value is in the array, otherwise false. - */ -const isValueInResponseArray = (response, path, value) => { - const respArr = get(response, path); - return Array.isArray(respArr) && respArr.includes(value); -}; - -/** - * Determines if an event should be aborted based on the response from a destination - * and extracts an error message if applicable. - * ref: - * 1) https://api.iterable.com/api/docs#users_updateEmail - * 2) https://api.iterable.com/api/docs#events_track - * 3) https://api.iterable.com/api/docs#users_bulkUpdateUser - * 4) https://api.iterable.com/api/docs#events_trackBulk - * 5) https://api.iterable.com/api/docs#catalogs_bulkUpdateCatalogItems - * 6) https://api.iterable.com/api/docs#users_registerDeviceToken - * 7) https://api.iterable.com/api/docs#users_registerBrowserToken - * 8) https://api.iterable.com/api/docs#commerce_trackPurchase - * 9) https://api.iterable.com/api/docs#commerce_updateCart - * - * @param {Object} event - The event object containing various event properties. - * @param {Object} destinationResponse - The response object from the destination. - * @returns {Object} An object containing a boolean `isAbortable` indicating if the event - * should be aborted, and an `errorMsg` string with the error message if applicable. - */ -const checkIfEventIsAbortableAndExtractErrorMessage = (event, destinationResponse) => { - const { failCount } = destinationResponse.response; - - if (failCount === 0) { - return { isAbortable: false, errorMsg: '' }; - } - - const eventValues = { - email: event.email, - userId: event.userId, - eventName: event.eventName, - }; - - let errorMsg = ''; - const userIdMatchPath = ITERABLE_RESPONSE_USER_ID_PATHS.filter((userIdPath) => - isValueInResponseArray(destinationResponse.response, userIdPath, eventValues.userId), - ); - if (userIdMatchPath.length > 0) { - errorMsg += `userId error:"${eventValues.userId}" in "${userIdMatchPath}".`; - } - - const emailMatchPath = ITERABLE_RESPONSE_EMAIL_PATHS.filter((emailPath) => - isValueInResponseArray(destinationResponse.response, emailPath, eventValues.email), - ); - - if (emailMatchPath.length > 0) { - errorMsg += `email error:"${eventValues.email}" in "${emailMatchPath}".`; - } - - const eventNameMatchPath = ['disallowedEventNames'].filter((eventNamePath) => - isValueInResponseArray(destinationResponse.response, eventNamePath, eventValues.eventName), - ); - - if (eventNameMatchPath.length > 0) { - errorMsg += `eventName error:"${eventValues.eventName}" in "${eventNameMatchPath}".`; - } - - if (errorMsg) { - return { - isAbortable: true, - errorMsg, - }; - } - - return { isAbortable: false, errorMsg: '' }; -}; - module.exports = { getCatalogEndpoint, hasMultipleResponses, @@ -840,6 +759,5 @@ module.exports = { filterEventsAndPrepareBatchRequests, registerDeviceTokenEventPayloadBuilder, registerBrowserTokenEventPayloadBuilder, - checkIfEventIsAbortableAndExtractErrorMessage, getCategoryWithEndpoint, }; diff --git a/src/v0/destinations/iterable/util.test.js b/src/v0/destinations/iterable/util.test.js index 39077f4850d..6bbf00ba085 100644 --- a/src/v0/destinations/iterable/util.test.js +++ b/src/v0/destinations/iterable/util.test.js @@ -7,9 +7,7 @@ const { updateUserEventPayloadBuilder, registerDeviceTokenEventPayloadBuilder, registerBrowserTokenEventPayloadBuilder, - checkIfEventIsAbortableAndExtractErrorMessage, } = require('./util'); - const { ConfigCategory } = require('./config'); const getTestMessage = () => { @@ -800,179 +798,4 @@ describe('iterable utils test', () => { ); }); }); - describe('checkIfEventIsAbortableAndExtractErrorMessage', () => { - // Returns non-abortable and empty error message when failCount is 0 - it('should return non-abortable and empty error message when failCount is 0', () => { - const event = { - email: 'test@example.com', - userId: 'user123', - eventName: 'testEvent', - }; - const destinationResponse = { - response: { - failCount: 0, - }, - }; - - const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); - expect(result).toEqual({ isAbortable: false, errorMsg: '' }); - }); - - // Handles undefined or null event fields gracefully - it('should handle undefined or null event fields gracefully', () => { - const event = { - email: null, - userId: undefined, - eventName: 'testEvent', - }; - const destinationResponse = { - response: { - failCount: 1, - invalidEmails: ['test@example.com'], - }, - }; - const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); - expect(result).toEqual({ isAbortable: false, errorMsg: '' }); - }); - - // Handles events with all expected fields present - it('should handle events with all expected fields present and return non-abortable when no match', () => { - const event = { - email: 'test@example.com', - userId: 'user123', - eventName: 'purchase', - id: 'event123', - createdAt: '2023-10-01T00:00:00Z', - campaignId: 'campaign123', - templateId: 'template123', - createNewFields: true, - dataFields: { field1: 'value1' }, - }; - - const destinationResponse = { - response: { - failCount: 1, - invalidEmails: ['another@example.com'], - }, - }; - - const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); - - expect(result.isAbortable).toBe(false); - expect(result.errorMsg).toBe(''); - }); - - // Returns appropriate error message for abortable event - - it('should find the right value for which it should fail and passes otherwise for emails', () => { - const event = { - email: 'test', - userId: 'user123', - eventName: 'purchase', - dataFields: { customField1: 'value1', customField2: 'value2' }, - }; - const destinationResponse = { - response: { - failCount: 1, - failedUpdates: { - invalidEmails: ['test'], - }, - }, - }; - const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); - expect(result).toEqual({ - isAbortable: true, - errorMsg: 'email error:"test" in "failedUpdates.invalidEmails".', - }); - }); - - it('should find the right value for which it should fail', () => { - const event = { - email: 'test@gmail.com', - userId: 'user123', - eventName: 'purchase', - dataFields: { customField1: 'test', customField2: 'value2' }, - }; - const destinationResponse = { - response: { - failCount: 1, - failedUpdates: { - invalidEmails: ['test'], - }, - }, - }; - const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); - expect(result.isAbortable).toBe(false); - expect(result.errorMsg).toBe(''); - }); - - it('should find all the matching paths it failed for and curate error message', () => { - const event = { - email: 'test', - userId: 'user123', - eventName: 'purchase', - dataFields: { customField1: 'test', customField2: 'value2' }, - }; - const destinationResponse = { - response: { - failCount: 1, - invalidEmails: ['test'], - failedUpdates: { - invalidEmails: ['test'], - conflictEmails: ['test'], - }, - }, - }; - const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); - expect(result.isAbortable).toBe(true); - expect(result.errorMsg).toBe( - 'email error:"test" in "invalidEmails,failedUpdates.invalidEmails,failedUpdates.conflictEmails".', - ); - }); - - it('should find the right value for which it should fail and passes otherwise for userIds', () => { - const event = { - email: 'test', - userId: 'user123', - eventName: 'purchase', - dataFields: { customField1: 'value1', customField2: 'value2' }, - }; - const destinationResponse = { - response: { - failCount: 1, - failedUpdates: { - invalidUserIds: ['user123'], - }, - }, - }; - const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); - expect(result).toEqual({ - isAbortable: true, - errorMsg: 'userId error:"user123" in "failedUpdates.invalidUserIds".', - }); - }); - - it('should find the right value for which it should fail and passes otherwise for disallowed events', () => { - const event = { - email: 'test', - userId: 'user123', - eventName: 'purchase', - dataFields: { customField1: 'value1', customField2: 'value2' }, - }; - const destinationResponse = { - response: { - failCount: 1, - disallowedEventNames: ['purchase'], - failedUpdates: { - invalidUserIds: [], - }, - }, - }; - const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); - expect(result).toEqual({ - isAbortable: true, - errorMsg: 'eventName error:"purchase" in "disallowedEventNames".', - }); - }); - }); }); diff --git a/src/v1/destinations/iterable/networkHandler.ts b/src/v1/destinations/iterable/networkHandler.ts index 06ab77b3170..e3edb5daab7 100644 --- a/src/v1/destinations/iterable/networkHandler.ts +++ b/src/v1/destinations/iterable/networkHandler.ts @@ -3,12 +3,7 @@ import { processAxiosResponse } from '../../../adapters/utils/networkUtils'; import { BULK_ENDPOINTS } from '../../../v0/destinations/iterable/config'; import { GenericStrategy } from './strategies/generic'; import { TrackIdentifyStrategy } from './strategies/track-identify'; - -type ResponseParams = { - destinationRequest: { - endpoint: string; - }; -}; +import { GenericProxyHandlerInput } from './types'; const strategyRegistry: { [key: string]: any } = { [TrackIdentifyStrategy.name]: new TrackIdentifyStrategy(), @@ -22,7 +17,7 @@ const getResponseStrategy = (endpoint: string) => { return strategyRegistry[GenericStrategy.name]; }; -const responseHandler = (responseParams: ResponseParams) => { +const responseHandler = (responseParams: GenericProxyHandlerInput) => { const { destinationRequest } = responseParams; const strategy = getResponseStrategy(destinationRequest.endpoint); return strategy.handleResponse(responseParams); diff --git a/src/v1/destinations/iterable/strategies/base.ts b/src/v1/destinations/iterable/strategies/base.ts index 348bfe7fc7a..b8b28d12c8b 100644 --- a/src/v1/destinations/iterable/strategies/base.ts +++ b/src/v1/destinations/iterable/strategies/base.ts @@ -2,28 +2,24 @@ import { TAG_NAMES } from '@rudderstack/integrations-lib'; import { getDynamicErrorType } from '../../../../adapters/utils/networkUtils'; import { isHttpStatusSuccess } from '../../../../v0/util'; import { TransformerProxyError } from '../../../../v0/util/errorTypes'; -import { DestinationResponse, ResponseParams } from '../types'; +import { GenericProxyHandlerInput } from '../types'; // Base strategy is the base class for all strategies in Iterable destination class BaseStrategy { - handleResponse(responseParams: { destinationResponse: DestinationResponse }): void { + handleResponse(responseParams: GenericProxyHandlerInput): void { const { destinationResponse } = responseParams; const { status } = destinationResponse; if (!isHttpStatusSuccess(status)) { - return this.handleError({ - destinationResponse, - rudderJobMetadata: [], - }); + return this.handleError(responseParams); } return this.handleSuccess(responseParams); } - handleError(responseParams: ResponseParams): void { + handleError(responseParams: GenericProxyHandlerInput): void { const { destinationResponse, rudderJobMetadata } = responseParams; const { response, status } = destinationResponse; - // @ts-expect-error: not sure if `response.message` is correct or needed const responseMessage = response.params || response.msg || response.message; const errorMessage = JSON.stringify(responseMessage) || 'unknown error format'; @@ -49,3 +45,11 @@ class BaseStrategy { } export { BaseStrategy }; + +// TODO: +/** + * 1) fix return types appropriately + * 2) use pre-declared types, rather than adding our own types + * 3) no need for unnecessary refactors + * 4) add different implementations for handle Errors + */ diff --git a/src/v1/destinations/iterable/strategies/generic.ts b/src/v1/destinations/iterable/strategies/generic.ts index 27028756108..654ac96a23a 100644 --- a/src/v1/destinations/iterable/strategies/generic.ts +++ b/src/v1/destinations/iterable/strategies/generic.ts @@ -1,11 +1,12 @@ import { BaseStrategy } from './base'; -import { DestinationResponse, SuccessResponse } from '../types'; +import { IterableBulkApiResponse, IterableSuccessResponse } from '../types'; +import { ProxyMetdata } from '../../../../types'; class GenericStrategy extends BaseStrategy { handleSuccess(responseParams: { - destinationResponse: DestinationResponse; - rudderJobMetadata: any[]; - }): SuccessResponse { + destinationResponse: IterableBulkApiResponse; + rudderJobMetadata: ProxyMetdata[]; + }): IterableSuccessResponse { const { destinationResponse, rudderJobMetadata } = responseParams; const { status } = destinationResponse; diff --git a/src/v1/destinations/iterable/strategies/track-identify.ts b/src/v1/destinations/iterable/strategies/track-identify.ts index 7c242f3ac79..bb6c0c54f20 100644 --- a/src/v1/destinations/iterable/strategies/track-identify.ts +++ b/src/v1/destinations/iterable/strategies/track-identify.ts @@ -1,24 +1,20 @@ import { BaseStrategy } from './base'; -import { DestinationResponse, ResponseParams, Response } from '../types'; -import { checkIfEventIsAbortableAndExtractErrorMessage } from '../../../../v0/destinations/iterable/util'; +import { IterableBulkProxyInput } from '../types'; +import { checkIfEventIsAbortableAndExtractErrorMessage } from '../utils'; +import { DeliveryJobState, DeliveryV1Response } from '../../../../types'; class TrackIdentifyStrategy extends BaseStrategy { - handleSuccess(responseParams: ResponseParams): { - status: number; - message: string; - destinationResponse: DestinationResponse; - response: Response[]; - } { + handleSuccess(responseParams: IterableBulkProxyInput): DeliveryV1Response { const { destinationResponse, rudderJobMetadata, destinationRequest } = responseParams; const { status } = destinationResponse; - const responseWithIndividualEvents: Response[] = []; + const responseWithIndividualEvents: DeliveryJobState[] = []; const { events, users } = destinationRequest?.body.JSON || {}; const finalData = events || users; if (finalData) { finalData.forEach((event, idx) => { - const proxyOutput = { + const parsedOutput = { statusCode: 200, metadata: rudderJobMetadata[idx], error: 'success', @@ -29,10 +25,10 @@ class TrackIdentifyStrategy extends BaseStrategy { destinationResponse, ); if (isAbortable) { - proxyOutput.statusCode = 400; - proxyOutput.error = errorMsg; + parsedOutput.statusCode = 400; + parsedOutput.error = errorMsg; } - responseWithIndividualEvents.push(proxyOutput); + responseWithIndividualEvents.push(parsedOutput); }); } diff --git a/src/v1/destinations/iterable/types.ts b/src/v1/destinations/iterable/types.ts index 65f5a3416f2..ff495271acf 100644 --- a/src/v1/destinations/iterable/types.ts +++ b/src/v1/destinations/iterable/types.ts @@ -1,3 +1,5 @@ +import { ProxyMetdata, ProxyV1Request } from '../../../types'; + type FailedUpdates = { invalidEmails?: string[]; invalidUserIds?: string[]; @@ -25,33 +27,43 @@ export type GeneralApiResponse = { failedUpdates?: FailedUpdates; }; -export type DestinationResponse = { +export type IterableBulkApiResponse = { status: number; response: GeneralApiResponse; }; -export type ResponseParams = { - destinationResponse: DestinationResponse; - rudderJobMetadata: any[]; +type IterableBulkRequestBody = { + events?: any[]; + users?: any[]; +}; + +export type IterableBulkProxyInput = { + destinationResponse: IterableBulkApiResponse; + rudderJobMetadata: ProxyMetdata; + destType: string; destinationRequest?: { body: { - JSON: { - events?: any[]; - users?: any[]; - }; + JSON: IterableBulkRequestBody; }; }; }; +export type GenericProxyHandlerInput = { + destinationResponse: any; + rudderJobMetadata: ProxyMetdata[]; + destType: string; + destinationRequest: ProxyV1Request; +}; + export type Response = { statusCode: number; metadata: any; error: string; }; -export type SuccessResponse = { +export type IterableSuccessResponse = { status: number; message: string; - destinationResponse: DestinationResponse; + destinationResponse: IterableBulkApiResponse; response: Response[]; }; diff --git a/src/v1/destinations/iterable/utils.test.ts b/src/v1/destinations/iterable/utils.test.ts new file mode 100644 index 00000000000..a556e3704f8 --- /dev/null +++ b/src/v1/destinations/iterable/utils.test.ts @@ -0,0 +1,184 @@ +import { checkIfEventIsAbortableAndExtractErrorMessage } from './utils'; +describe('checkIfEventIsAbortableAndExtractErrorMessage', () => { + // Returns non-abortable and empty error message when failCount is 0 + it('should return non-abortable and empty error message when failCount is 0', () => { + const event = { + email: 'test@example.com', + userId: 'user123', + eventName: 'testEvent', + }; + const destinationResponse = { + status: 200, + response: { + failCount: 0, + }, + }; + + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + expect(result).toEqual({ isAbortable: false, errorMsg: '' }); + }); + + // Handles undefined or null event fields gracefully + it('should handle undefined or null event fields gracefully', () => { + const event = { + email: null, + userId: undefined, + eventName: 'testEvent', + }; + const destinationResponse = { + status: 200, + response: { + failCount: 1, + invalidEmails: ['test@example.com'], + }, + }; + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + expect(result).toEqual({ isAbortable: false, errorMsg: '' }); + }); + + // Handles events with all expected fields present + it('should handle events with all expected fields present and return non-abortable when no match', () => { + const event = { + email: 'test@example.com', + userId: 'user123', + eventName: 'purchase', + id: 'event123', + createdAt: '2023-10-01T00:00:00Z', + campaignId: 'campaign123', + templateId: 'template123', + createNewFields: true, + dataFields: { field1: 'value1' }, + }; + + const destinationResponse = { + status: 200, + response: { + failCount: 1, + invalidEmails: ['another@example.com'], + }, + }; + + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + + expect(result.isAbortable).toBe(false); + expect(result.errorMsg).toBe(''); + }); + + // Returns appropriate error message for abortable event + + it('should find the right value for which it should fail and passes otherwise for emails', () => { + const event = { + email: 'test', + userId: 'user123', + eventName: 'purchase', + dataFields: { customField1: 'value1', customField2: 'value2' }, + }; + const destinationResponse = { + status: 200, + response: { + failCount: 1, + failedUpdates: { + invalidEmails: ['test'], + }, + }, + }; + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + expect(result).toEqual({ + isAbortable: true, + errorMsg: 'email error:"test" in "failedUpdates.invalidEmails".', + }); + }); + + it('should find the right value for which it should fail', () => { + const event = { + email: 'test@gmail.com', + userId: 'user123', + eventName: 'purchase', + dataFields: { customField1: 'test', customField2: 'value2' }, + }; + const destinationResponse = { + status: 200, + response: { + failCount: 1, + failedUpdates: { + invalidEmails: ['test'], + }, + }, + }; + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + expect(result.isAbortable).toBe(false); + expect(result.errorMsg).toBe(''); + }); + + it('should find all the matching paths it failed for and curate error message', () => { + const event = { + email: 'test', + userId: 'user123', + eventName: 'purchase', + dataFields: { customField1: 'test', customField2: 'value2' }, + }; + const destinationResponse = { + status: 200, + response: { + failCount: 1, + invalidEmails: ['test'], + failedUpdates: { + invalidEmails: ['test'], + conflictEmails: ['test'], + }, + }, + }; + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + expect(result.isAbortable).toBe(true); + expect(result.errorMsg).toBe( + 'email error:"test" in "invalidEmails,failedUpdates.invalidEmails,failedUpdates.conflictEmails".', + ); + }); + + it('should find the right value for which it should fail and passes otherwise for userIds', () => { + const event = { + email: 'test', + userId: 'user123', + eventName: 'purchase', + dataFields: { customField1: 'value1', customField2: 'value2' }, + }; + const destinationResponse = { + status: 200, + response: { + failCount: 1, + failedUpdates: { + invalidUserIds: ['user123'], + }, + }, + }; + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + expect(result).toEqual({ + isAbortable: true, + errorMsg: 'userId error:"user123" in "failedUpdates.invalidUserIds".', + }); + }); + + it('should find the right value for which it should fail and passes otherwise for disallowed events', () => { + const event = { + email: 'test', + userId: 'user123', + eventName: 'purchase', + dataFields: { customField1: 'value1', customField2: 'value2' }, + }; + const destinationResponse = { + status: 200, + response: { + failCount: 1, + disallowedEventNames: ['purchase'], + failedUpdates: { + invalidUserIds: [], + }, + }, + }; + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse); + expect(result).toEqual({ + isAbortable: true, + errorMsg: 'eventName error:"purchase" in "disallowedEventNames".', + }); + }); +}); diff --git a/src/v1/destinations/iterable/utils.ts b/src/v1/destinations/iterable/utils.ts new file mode 100644 index 00000000000..90985a8e949 --- /dev/null +++ b/src/v1/destinations/iterable/utils.ts @@ -0,0 +1,92 @@ +import { + ITERABLE_RESPONSE_EMAIL_PATHS, + ITERABLE_RESPONSE_USER_ID_PATHS, +} from '../../../v0/destinations/iterable/config'; +import { IterableBulkApiResponse } from './types'; + +const get = require('get-value'); + +/** + * Checks if a value is present in a response array based on a given path. + * @param {Object} response - The response object to search within. + * @param {string} path - The path to the response array. + * @param {any} value - The value to check for in the array. + * @returns {boolean} - True if the value is in the array, otherwise false. + */ +const isValueInResponseArray = (destinationResponse, path, value) => { + const respArr = get(destinationResponse, path); + return Array.isArray(respArr) && respArr.includes(value); +}; + +/** + * Determines if an event should be aborted based on the response from a destination + * and extracts an error message if applicable. + * ref: + * 1) https://api.iterable.com/api/docs#users_updateEmail + * 2) https://api.iterable.com/api/docs#events_track + * 3) https://api.iterable.com/api/docs#users_bulkUpdateUser + * 4) https://api.iterable.com/api/docs#events_trackBulk + * 5) https://api.iterable.com/api/docs#catalogs_bulkUpdateCatalogItems + * 6) https://api.iterable.com/api/docs#users_registerDeviceToken + * 7) https://api.iterable.com/api/docs#users_registerBrowserToken + * 8) https://api.iterable.com/api/docs#commerce_trackPurchase + * 9) https://api.iterable.com/api/docs#commerce_updateCart + * + * @param {Object} event - The event object containing various event properties. + * @param {Object} destinationResponse - The response object from the destination. + * @returns {Object} An object containing a boolean `isAbortable` indicating if the event + * should be aborted, and an `errorMsg` string with the error message if applicable. + */ + +export const checkIfEventIsAbortableAndExtractErrorMessage = ( + event: any, + destinationResponse: IterableBulkApiResponse, +): { + isAbortable: boolean; + errorMsg: string; +} => { + const { failCount } = destinationResponse.response; + + if (failCount === 0) { + return { isAbortable: false, errorMsg: '' }; + } + + const eventValues = { + email: event.email, + userId: event.userId, + eventName: event.eventName, + }; + + let errorMsg = ''; + const userIdMatchPath = ITERABLE_RESPONSE_USER_ID_PATHS.filter((userIdPath) => + isValueInResponseArray(destinationResponse.response, userIdPath, eventValues.userId), + ); + if (userIdMatchPath.length > 0) { + errorMsg += `userId error:"${eventValues.userId}" in "${userIdMatchPath}".`; + } + + const emailMatchPath = ITERABLE_RESPONSE_EMAIL_PATHS.filter((emailPath) => + isValueInResponseArray(destinationResponse.response, emailPath, eventValues.email), + ); + + if (emailMatchPath.length > 0) { + errorMsg += `email error:"${eventValues.email}" in "${emailMatchPath}".`; + } + + const eventNameMatchPath = ['disallowedEventNames'].filter((eventNamePath) => + isValueInResponseArray(destinationResponse.response, eventNamePath, eventValues.eventName), + ); + + if (eventNameMatchPath.length > 0) { + errorMsg += `eventName error:"${eventValues.eventName}" in "${eventNameMatchPath}".`; + } + + if (errorMsg) { + return { + isAbortable: true, + errorMsg, + }; + } + + return { isAbortable: false, errorMsg: '' }; +}; From 85b1166c4bc92427334ebee56fa6b513948e05cd Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Fri, 3 Jan 2025 18:07:24 +0530 Subject: [PATCH 19/21] fix: separating handle error functions --- .../destinations/iterable/strategies/base.ts | 39 ++----------------- .../iterable/strategies/generic.ts | 31 ++++++++++++++- .../iterable/strategies/track-identify.ts | 27 ++++++++++++- 3 files changed, 59 insertions(+), 38 deletions(-) diff --git a/src/v1/destinations/iterable/strategies/base.ts b/src/v1/destinations/iterable/strategies/base.ts index b8b28d12c8b..dfde1e92253 100644 --- a/src/v1/destinations/iterable/strategies/base.ts +++ b/src/v1/destinations/iterable/strategies/base.ts @@ -1,11 +1,8 @@ -import { TAG_NAMES } from '@rudderstack/integrations-lib'; -import { getDynamicErrorType } from '../../../../adapters/utils/networkUtils'; import { isHttpStatusSuccess } from '../../../../v0/util'; -import { TransformerProxyError } from '../../../../v0/util/errorTypes'; import { GenericProxyHandlerInput } from '../types'; // Base strategy is the base class for all strategies in Iterable destination -class BaseStrategy { +abstract class BaseStrategy { handleResponse(responseParams: GenericProxyHandlerInput): void { const { destinationResponse } = responseParams; const { status } = destinationResponse; @@ -17,39 +14,9 @@ class BaseStrategy { return this.handleSuccess(responseParams); } - handleError(responseParams: GenericProxyHandlerInput): void { - const { destinationResponse, rudderJobMetadata } = responseParams; - const { response, status } = destinationResponse; - const responseMessage = response.params || response.msg || response.message; - const errorMessage = JSON.stringify(responseMessage) || 'unknown error format'; + abstract handleError(responseParams: GenericProxyHandlerInput): void; - const responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ - statusCode: status, - metadata, - error: errorMessage, - })); - - throw new TransformerProxyError( - `ITERABLE: Error transformer proxy during ITERABLE response transformation. ${errorMessage}`, - status, - { [TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status) }, - destinationResponse, - '', - responseWithIndividualEvents, - ); - } - - handleSuccess(responseParams: any): void { - throw new TransformerProxyError(`success response handling is not added:${responseParams}`); - } + abstract handleSuccess(responseParams: any): void; } export { BaseStrategy }; - -// TODO: -/** - * 1) fix return types appropriately - * 2) use pre-declared types, rather than adding our own types - * 3) no need for unnecessary refactors - * 4) add different implementations for handle Errors - */ diff --git a/src/v1/destinations/iterable/strategies/generic.ts b/src/v1/destinations/iterable/strategies/generic.ts index 654ac96a23a..e43eb7623b5 100644 --- a/src/v1/destinations/iterable/strategies/generic.ts +++ b/src/v1/destinations/iterable/strategies/generic.ts @@ -1,6 +1,13 @@ import { BaseStrategy } from './base'; -import { IterableBulkApiResponse, IterableSuccessResponse } from '../types'; +import { + GenericProxyHandlerInput, + IterableBulkApiResponse, + IterableSuccessResponse, +} from '../types'; import { ProxyMetdata } from '../../../../types'; +import { TransformerProxyError } from '../../../../v0/util/errorTypes'; +import { TAG_NAMES } from '../../../../v0/util/tags'; +import { getDynamicErrorType } from '../../../../adapters/utils/networkUtils'; class GenericStrategy extends BaseStrategy { handleSuccess(responseParams: { @@ -23,6 +30,28 @@ class GenericStrategy extends BaseStrategy { response: responseWithIndividualEvents, }; } + + handleError(responseParams: GenericProxyHandlerInput): void { + const { destinationResponse, rudderJobMetadata } = responseParams; + const { response, status } = destinationResponse; + const responseMessage = response.params || response.msg || response.message; + const errorMessage = JSON.stringify(responseMessage) || 'unknown error format'; + + const responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ + statusCode: status, + metadata, + error: errorMessage, + })); + + throw new TransformerProxyError( + `ITERABLE: Error transformer proxy during ITERABLE response transformation. ${errorMessage}`, + status, + { [TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status) }, + destinationResponse, + '', + responseWithIndividualEvents, + ); + } } export { GenericStrategy }; diff --git a/src/v1/destinations/iterable/strategies/track-identify.ts b/src/v1/destinations/iterable/strategies/track-identify.ts index bb6c0c54f20..a8a08682514 100644 --- a/src/v1/destinations/iterable/strategies/track-identify.ts +++ b/src/v1/destinations/iterable/strategies/track-identify.ts @@ -1,7 +1,10 @@ import { BaseStrategy } from './base'; -import { IterableBulkProxyInput } from '../types'; +import { GenericProxyHandlerInput, IterableBulkProxyInput } from '../types'; import { checkIfEventIsAbortableAndExtractErrorMessage } from '../utils'; import { DeliveryJobState, DeliveryV1Response } from '../../../../types'; +import { TransformerProxyError } from '../../../../v0/util/errorTypes'; +import { getDynamicErrorType } from '../../../../adapters/utils/networkUtils'; +import { TAG_NAMES } from '../../../../v0/util/tags'; class TrackIdentifyStrategy extends BaseStrategy { handleSuccess(responseParams: IterableBulkProxyInput): DeliveryV1Response { @@ -39,6 +42,28 @@ class TrackIdentifyStrategy extends BaseStrategy { response: responseWithIndividualEvents, }; } + + handleError(responseParams: GenericProxyHandlerInput): void { + const { destinationResponse, rudderJobMetadata } = responseParams; + const { response, status } = destinationResponse; + const responseMessage = response.params || response.msg || response.message; + const errorMessage = JSON.stringify(responseMessage) || 'unknown error format'; + + const responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ + statusCode: status, + metadata, + error: errorMessage, + })); + + throw new TransformerProxyError( + `ITERABLE: Error transformer proxy during ITERABLE response transformation. ${errorMessage}`, + status, + { [TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status) }, + destinationResponse, + '', + responseWithIndividualEvents, + ); + } } export { TrackIdentifyStrategy }; From ebfeffc7411465d8305eabff3c6e07870ba4a2fd Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Mon, 6 Jan 2025 10:04:38 +0530 Subject: [PATCH 20/21] fix: migrating processor and router test cases --- .../iterable/dataDelivery/business.ts | 27 +- .../iterable/processor/aliasTestData.ts | 151 +- .../iterable/processor/identifyTestData.ts | 781 ++++++--- .../iterable/processor/pageScreenTestData.ts | 735 ++++++--- .../iterable/processor/trackTestData.ts | 1423 ++++++++++++----- .../iterable/processor/validationTestData.ts | 199 ++- .../destinations/iterable/router/data.ts | 515 +++++- 7 files changed, 2830 insertions(+), 1001 deletions(-) diff --git a/test/integrations/destinations/iterable/dataDelivery/business.ts b/test/integrations/destinations/iterable/dataDelivery/business.ts index 4e0c76e3f7c..0d6059806e1 100644 --- a/test/integrations/destinations/iterable/dataDelivery/business.ts +++ b/test/integrations/destinations/iterable/dataDelivery/business.ts @@ -21,32 +21,7 @@ export const statTags = { module: 'destination', }; -export const metadata = [ - { - jobId: 1, - attemptNum: 1, - userId: 'default-userId', - destinationId: 'default-destinationId', - workspaceId: 'default-workspaceId', - sourceId: 'default-sourceId', - secret: { - accessToken: 'default-accessToken', - }, - dontBatch: false, - }, - { - jobId: 2, - attemptNum: 1, - userId: 'default-userId', - destinationId: 'default-destinationId', - workspaceId: 'default-workspaceId', - sourceId: 'default-sourceId', - secret: { - accessToken: 'default-accessToken', - }, - dontBatch: false, - }, -]; +export const metadata = [generateMetadata(1), generateMetadata(2)]; export const singleMetadata = [ { diff --git a/test/integrations/destinations/iterable/processor/aliasTestData.ts b/test/integrations/destinations/iterable/processor/aliasTestData.ts index e6893d5510c..48f92f5ac22 100644 --- a/test/integrations/destinations/iterable/processor/aliasTestData.ts +++ b/test/integrations/destinations/iterable/processor/aliasTestData.ts @@ -1,10 +1,40 @@ -import { - generateMetadata, - overrideDestination, - transformResultBuilder, -} from './../../../testUtils'; -import { Destination } from '../../../../../src/types'; import { ProcessorTestData } from '../../../testTypes'; +import { Destination, Metadata } from '../../../../../src/types'; +import { overrideDestination } from '../../../testUtils'; + +const baseMetadata: Metadata = { + sourceId: 'default-sourceId', + workspaceId: 'default-workspaceId', + namespace: 'default-namespace', + instanceId: 'default-instance', + sourceType: 'default-source-type', + sourceCategory: 'default-category', + trackingPlanId: 'default-tracking-plan', + trackingPlanVersion: 1, + sourceTpConfig: {}, + mergedTpConfig: {}, + destinationId: 'default-destinationId', + jobRunId: 'default-job-run', + jobId: 1, + sourceBatchId: 'default-batch', + sourceJobId: 'default-source-job', + sourceJobRunId: 'default-source-job-run', + sourceTaskId: 'default-task', + sourceTaskRunId: 'default-task-run', + recordId: {}, + destinationType: 'default-destination-type', + messageId: 'default-message-id', + oauthAccessToken: 'default-token', + messageIds: ['default-message-id'], + rudderId: 'default-rudder-id', + receivedAt: '2025-01-06T04:12:38.713Z', + eventName: 'default-event', + eventType: 'default-type', + sourceDefinitionId: 'default-source-def', + destinationDefinitionId: 'default-dest-def', + transformationId: 'default-transform', + dontBatch: false, +}; const destination: Destination = { ID: '123', @@ -29,23 +59,6 @@ const destination: Destination = { Enabled: true, }; -const headers = { - api_key: 'testApiKey', - 'Content-Type': 'application/json', -}; - -const properties = { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', -}; - -const sentAt = '2020-08-28T16:26:16.473Z'; -const originalTimestamp = '2020-08-28T16:26:06.468Z'; - export const aliasTestData: ProcessorTestData[] = [ { id: 'iterable-alias-test-1', @@ -59,24 +72,31 @@ export const aliasTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, message: { anonymousId: 'anonId', userId: 'new@email.com', previousId: 'old@email.com', name: 'ApplicationLoaded', context: {}, - properties, + properties: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + }, type: 'alias', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', }, - metadata: generateMetadata(1), + metadata: baseMetadata, + destination, }, ], - method: 'POST', }, }, output: { @@ -84,17 +104,30 @@ export const aliasTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, + method: 'POST', endpoint: 'https://api.iterable.com/api/users/updateEmail', - JSON: { - currentEmail: 'old@email.com', - newEmail: 'new@email.com', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + currentEmail: 'old@email.com', + newEmail: 'new@email.com', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, - }), + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -112,24 +145,31 @@ export const aliasTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination: overrideDestination(destination, { dataCenter: 'EUDC' }), message: { anonymousId: 'anonId', userId: 'new@email.com', previousId: 'old@email.com', name: 'ApplicationLoaded', context: {}, - properties, + properties: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + }, type: 'alias', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', }, - metadata: generateMetadata(1), + metadata: baseMetadata, + destination: overrideDestination(destination, { dataCenter: 'EUDC' }), }, ], - method: 'POST', }, }, output: { @@ -137,17 +177,30 @@ export const aliasTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, + method: 'POST', endpoint: 'https://api.eu.iterable.com/api/users/updateEmail', - JSON: { - currentEmail: 'old@email.com', - newEmail: 'new@email.com', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', }, - }), + params: {}, + body: { + JSON: { + currentEmail: 'old@email.com', + newEmail: 'new@email.com', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, diff --git a/test/integrations/destinations/iterable/processor/identifyTestData.ts b/test/integrations/destinations/iterable/processor/identifyTestData.ts index 4c6729fc80a..792e16566c7 100644 --- a/test/integrations/destinations/iterable/processor/identifyTestData.ts +++ b/test/integrations/destinations/iterable/processor/identifyTestData.ts @@ -1,64 +1,40 @@ -import { - generateMetadata, - transformResultBuilder, - generateIndentifyPayload, - overrideDestination, -} from './../../../testUtils'; -import { Destination } from '../../../../../src/types'; import { ProcessorTestData } from '../../../testTypes'; +import { Metadata } from '../../../../../src/types'; -const destination: Destination = { - ID: '123', - Name: 'iterable', - DestinationDefinition: { - ID: '123', - Name: 'iterable', - DisplayName: 'Iterable', - Config: {}, - }, - WorkspaceID: '123', - Transformations: [], - Config: { - apiKey: 'testApiKey', - dataCenter: 'USDC', - preferUserId: false, - trackAllPages: true, - trackNamedPages: false, - mapToSingleEvent: false, - trackCategorisedPages: false, - }, - Enabled: true, -}; - -const headers = { - api_key: 'testApiKey', - 'Content-Type': 'application/json', -}; - -const user1Traits = { - name: 'manashi', - country: 'India', - city: 'Bangalore', - email: 'manashi@website.com', -}; - -const user2Traits = { - am_pm: 'AM', - pPower: 'AM', - boolean: true, - userId: 'Jacqueline', - firstname: 'Jacqueline', - administrative_unit: 'Minnesota', +const baseMetadata: Metadata = { + sourceId: 'default-sourceId', + workspaceId: 'default-workspaceId', + namespace: 'default-namespace', + instanceId: 'default-instance', + sourceType: 'default-source-type', + sourceCategory: 'default-category', + trackingPlanId: 'default-tracking-plan', + trackingPlanVersion: 1, + sourceTpConfig: {}, + mergedTpConfig: {}, + destinationId: 'default-destinationId', + jobRunId: 'default-job-run', + jobId: 1, + sourceBatchId: 'default-batch', + sourceJobId: 'default-source-job', + sourceJobRunId: 'default-source-job-run', + sourceTaskId: 'default-task', + sourceTaskRunId: 'default-task-run', + recordId: {}, + destinationType: 'default-destination-type', + messageId: 'default-message-id', + oauthAccessToken: 'default-token', + messageIds: ['default-message-id'], + rudderId: 'default-rudder-id', + receivedAt: '2025-01-06T03:57:13.523Z', + eventName: 'default-event', + eventType: 'default-type', + sourceDefinitionId: 'default-source-def', + destinationDefinitionId: 'default-dest-def', + transformationId: 'default-transform', + dontBatch: false, }; -const userId = 'userId'; -const anonymousId = 'anonId'; -const sentAt = '2020-08-28T16:26:16.473Z'; -const originalTimestamp = '2020-08-28T16:26:06.468Z'; - -const updateUserEndpoint = 'https://api.iterable.com/api/users/update'; -const updateUserEndpointEUDC = 'https://api.eu.iterable.com/api/users/update'; - export const identifyTestData: ProcessorTestData[] = [ { id: 'iterable-identify-test-1', @@ -72,23 +48,57 @@ export const identifyTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, message: { - anonymousId, + anonymousId: 'anonId', context: { - traits: user1Traits, + traits: { + name: 'manashi', + country: 'India', + city: 'Bangalore', + email: 'manashi@website.com', + }, + }, + traits: { + name: 'manashi', + country: 'India', + city: 'Bangalore', + email: 'manashi@website.com', }, - traits: user1Traits, type: 'identify', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - metadata: generateMetadata(1), }, ], - method: 'POST', }, }, output: { @@ -96,20 +106,38 @@ export const identifyTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: updateUserEndpoint, - JSON: { - email: user1Traits.email, - userId: anonymousId, - dataFields: user1Traits, - preferUserId: false, - mergeNestedObjects: true, + method: 'POST', + endpoint: 'https://api.iterable.com/api/users/update', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + email: 'manashi@website.com', + userId: 'anonId', + dataFields: { + name: 'manashi', + country: 'India', + city: 'Bangalore', + email: 'manashi@website.com', + }, + preferUserId: false, + mergeNestedObjects: true, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, - }), + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -127,23 +155,78 @@ export const identifyTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, - message: generateIndentifyPayload({ - userId, - anonymousId, + message: { + type: 'identify', + sentAt: '2020-08-28T16:26:16.473Z', + userId: 'userId', + channel: 'web', context: { - traits: { email: 'ruchira@rudderlabs.com' }, + os: { + name: '', + version: '1.12.3', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.11', + namespace: 'com.rudderlabs.javascript', + }, + traits: { + email: 'ruchira@rudderlabs.com', + }, + locale: 'en-US', + device: { + token: 'token', + id: 'id', + type: 'ios', + }, + screen: { + density: 2, + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.1.11', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', }, - type: 'identify', - sentAt, - originalTimestamp, - }), - metadata: generateMetadata(1), + rudderId: '62amo6xzksaeyupr4y0pfaucwj0upzs6g7yx', + messageId: 'hk02avz2xijdkid4i0mvncbm478g9lybdpgc', + anonymousId: 'anonId', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, + }, }, ], - method: 'POST', }, }, output: { @@ -151,22 +234,35 @@ export const identifyTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: updateUserEndpoint, - JSON: { - email: 'ruchira@rudderlabs.com', - userId, - dataFields: { + method: 'POST', + endpoint: 'https://api.iterable.com/api/users/update', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { email: 'ruchira@rudderlabs.com', + userId: 'userId', + dataFields: { + email: 'ruchira@rudderlabs.com', + }, + preferUserId: false, + mergeNestedObjects: true, }, - preferUserId: false, - mergeNestedObjects: true, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, - }), + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -184,23 +280,78 @@ export const identifyTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination: { ...destination, Config: { ...destination.Config, preferUserId: true } }, - message: generateIndentifyPayload({ - userId, - anonymousId, + message: { + type: 'identify', + sentAt: '2020-08-28T16:26:16.473Z', + userId: 'userId', + channel: 'web', context: { - traits: { email: 'ruchira@rudderlabs.com' }, + os: { + name: '', + version: '1.12.3', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.11', + namespace: 'com.rudderlabs.javascript', + }, + traits: { + email: 'ruchira@rudderlabs.com', + }, + locale: 'en-US', + device: { + token: 'token', + id: 'id', + type: 'ios', + }, + screen: { + density: 2, + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.1.11', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', }, - type: 'identify', - sentAt, - originalTimestamp, - }), - metadata: generateMetadata(1), + rudderId: '1bbuv14fd7e8ogmsx7prcmw6ob37aq1zj6mo', + messageId: '1y56axyob5fp3lg3b1y1pij50kp15pyc2ubj', + anonymousId: 'anonId', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: true, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, + }, }, ], - method: 'POST', }, }, output: { @@ -208,22 +359,35 @@ export const identifyTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: updateUserEndpoint, - JSON: { - email: 'ruchira@rudderlabs.com', - userId, - dataFields: { + method: 'POST', + endpoint: 'https://api.iterable.com/api/users/update', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { email: 'ruchira@rudderlabs.com', + userId: 'userId', + dataFields: { + email: 'ruchira@rudderlabs.com', + }, + preferUserId: true, + mergeNestedObjects: true, }, - preferUserId: true, - mergeNestedObjects: true, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, - }), + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -242,24 +406,79 @@ export const identifyTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination: { ...destination, Config: { ...destination.Config, preferUserId: true } }, - message: generateIndentifyPayload({ - userId, - anonymousId, + message: { + type: 'identify', + sentAt: '2020-08-28T16:26:16.473Z', + userId: 'userId', + channel: 'web', context: { + os: { + name: '', + version: '1.12.3', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.11', + namespace: 'com.rudderlabs.javascript', + }, traits: {}, + locale: 'en-US', + device: { + token: 'token', + id: 'id', + type: 'ios', + }, + screen: { + density: 2, + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.1.11', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', }, - traits: { email: 'ruchira@rudderlabs.com' }, - type: 'identify', - sentAt, - originalTimestamp, - }), - metadata: generateMetadata(1), + traits: { + email: 'ruchira@rudderlabs.com', + }, + rudderId: 'iakido48935yw0kmw2swvjldsqoaophjzlhe', + messageId: 'hzycemnjaxr9cuqyyh003x9zlwfqnvbgzv4n', + anonymousId: 'anonId', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: true, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, + }, }, ], - method: 'POST', }, }, output: { @@ -267,22 +486,35 @@ export const identifyTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: updateUserEndpoint, - JSON: { - email: 'ruchira@rudderlabs.com', - userId, - dataFields: { + method: 'POST', + endpoint: 'https://api.iterable.com/api/users/update', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { email: 'ruchira@rudderlabs.com', + userId: 'userId', + dataFields: { + email: 'ruchira@rudderlabs.com', + }, + preferUserId: true, + mergeNestedObjects: true, }, - preferUserId: true, - mergeNestedObjects: true, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, - }), + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -300,12 +532,12 @@ export const identifyTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination: { ...destination, Config: { ...destination.Config, preferUserId: true } }, message: { - userId, - anonymousId, + userId: 'userId', + anonymousId: 'anonId', context: { externalId: [ { @@ -316,15 +548,46 @@ export const identifyTestData: ProcessorTestData[] = [ ], mappedToDestination: 'true', }, - traits: user2Traits, + traits: { + am_pm: 'AM', + pPower: 'AM', + boolean: true, + userId: 'Jacqueline', + firstname: 'Jacqueline', + administrative_unit: 'Minnesota', + }, type: 'identify', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: true, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - metadata: generateMetadata(1), }, ], - method: 'POST', }, }, output: { @@ -332,20 +595,41 @@ export const identifyTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: updateUserEndpoint, - JSON: { - email: 'lynnanderson@smith.net', - userId, - dataFields: { ...user2Traits, email: 'lynnanderson@smith.net' }, - preferUserId: true, - mergeNestedObjects: true, + method: 'POST', + endpoint: 'https://api.iterable.com/api/users/update', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + email: 'lynnanderson@smith.net', + userId: 'userId', + dataFields: { + am_pm: 'AM', + pPower: 'AM', + boolean: true, + userId: 'Jacqueline', + firstname: 'Jacqueline', + administrative_unit: 'Minnesota', + email: 'lynnanderson@smith.net', + }, + preferUserId: true, + mergeNestedObjects: true, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, - }), + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -363,12 +647,12 @@ export const identifyTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, message: { userId: 'Matthew', - anonymousId, + anonymousId: 'anonId', context: { externalId: [ { @@ -379,15 +663,46 @@ export const identifyTestData: ProcessorTestData[] = [ ], mappedToDestination: 'true', }, - traits: user2Traits, + traits: { + am_pm: 'AM', + pPower: 'AM', + boolean: true, + userId: 'Jacqueline', + firstname: 'Jacqueline', + administrative_unit: 'Minnesota', + }, type: 'identify', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - metadata: generateMetadata(1), }, ], - method: 'POST', }, }, output: { @@ -395,19 +710,39 @@ export const identifyTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: updateUserEndpoint, - JSON: { - userId: 'Matthew', - dataFields: { ...user2Traits, userId: 'Matthew' }, - preferUserId: false, - mergeNestedObjects: true, + method: 'POST', + endpoint: 'https://api.iterable.com/api/users/update', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', }, - }), + params: {}, + body: { + JSON: { + userId: 'Matthew', + dataFields: { + am_pm: 'AM', + pPower: 'AM', + boolean: true, + userId: 'Matthew', + firstname: 'Jacqueline', + administrative_unit: 'Minnesota', + }, + preferUserId: false, + mergeNestedObjects: true, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -425,23 +760,57 @@ export const identifyTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination: overrideDestination(destination, { dataCenter: 'EUDC' }), message: { - anonymousId, + anonymousId: 'anonId', context: { - traits: user1Traits, + traits: { + name: 'manashi', + country: 'India', + city: 'Bangalore', + email: 'manashi@website.com', + }, + }, + traits: { + name: 'manashi', + country: 'India', + city: 'Bangalore', + email: 'manashi@website.com', }, - traits: user1Traits, type: 'identify', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'EUDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - metadata: generateMetadata(1), }, ], - method: 'POST', }, }, output: { @@ -449,20 +818,38 @@ export const identifyTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: updateUserEndpointEUDC, - JSON: { - email: user1Traits.email, - userId: anonymousId, - dataFields: user1Traits, - preferUserId: false, - mergeNestedObjects: true, + method: 'POST', + endpoint: 'https://api.eu.iterable.com/api/users/update', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', }, - }), + params: {}, + body: { + JSON: { + email: 'manashi@website.com', + userId: 'anonId', + dataFields: { + name: 'manashi', + country: 'India', + city: 'Bangalore', + email: 'manashi@website.com', + }, + preferUserId: false, + mergeNestedObjects: true, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, diff --git a/test/integrations/destinations/iterable/processor/pageScreenTestData.ts b/test/integrations/destinations/iterable/processor/pageScreenTestData.ts index bc644c7f80d..6fd35cf8fb8 100644 --- a/test/integrations/destinations/iterable/processor/pageScreenTestData.ts +++ b/test/integrations/destinations/iterable/processor/pageScreenTestData.ts @@ -1,55 +1,40 @@ -import { - generateMetadata, - overrideDestination, - transformResultBuilder, -} from './../../../testUtils'; -import { Destination } from '../../../../../src/types'; import { ProcessorTestData } from '../../../testTypes'; +import { Metadata } from '../../../../../src/types'; -const destination: Destination = { - ID: '123', - Name: 'iterable', - DestinationDefinition: { - ID: '123', - Name: 'iterable', - DisplayName: 'Iterable', - Config: {}, - }, - WorkspaceID: '123', - Transformations: [], - Config: { - apiKey: 'testApiKey', - dataCenter: 'USDC', - preferUserId: false, - trackAllPages: true, - trackNamedPages: false, - mapToSingleEvent: false, - trackCategorisedPages: false, - }, - Enabled: true, -}; - -const headers = { - api_key: 'testApiKey', - 'Content-Type': 'application/json', +const baseMetadata: Metadata = { + sourceId: 'default-sourceId', + workspaceId: 'default-workspaceId', + namespace: 'default-namespace', + instanceId: 'default-instance', + sourceType: 'default-source-type', + sourceCategory: 'default-category', + trackingPlanId: 'default-tracking-plan', + trackingPlanVersion: 1, + sourceTpConfig: {}, + mergedTpConfig: {}, + destinationId: 'default-destinationId', + jobRunId: 'default-job-run', + jobId: 1, + sourceBatchId: 'default-batch', + sourceJobId: 'default-source-job', + sourceJobRunId: 'default-source-job-run', + sourceTaskId: 'default-task', + sourceTaskRunId: 'default-task-run', + recordId: {}, + destinationType: 'default-destination-type', + messageId: 'default-message-id', + oauthAccessToken: 'default-token', + messageIds: ['default-message-id'], + rudderId: 'default-rudder-id', + receivedAt: '2025-01-06T04:03:53.932Z', + eventName: 'default-event', + eventType: 'default-type', + sourceDefinitionId: 'default-source-def', + destinationDefinitionId: 'default-dest-def', + transformationId: 'default-transform', + dontBatch: false, }; -const properties = { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', -}; - -const anonymousId = 'anonId'; -const sentAt = '2020-08-28T16:26:16.473Z'; -const originalTimestamp = '2020-08-28T16:26:06.468Z'; - -const pageEndpoint = 'https://api.iterable.com/api/events/track'; -const pageEndpointEUDC = 'https://api.eu.iterable.com/api/events/track'; - export const pageScreenTestData: ProcessorTestData[] = [ { id: 'iterable-page-test-1', @@ -63,26 +48,57 @@ export const pageScreenTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, message: { - anonymousId, + anonymousId: 'anonId', name: 'ApplicationLoaded', context: { traits: { email: 'sayan@gmail.com', }, }, - properties, + properties: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + }, type: 'page', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - metadata: generateMetadata(1), }, ], - method: 'POST', }, }, output: { @@ -90,20 +106,40 @@ export const pageScreenTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: pageEndpoint, - JSON: { - userId: anonymousId, - dataFields: properties, - email: 'sayan@gmail.com', - createdAt: 1598631966468, - eventName: 'ApplicationLoaded page', - }, - }), + method: 'POST', + endpoint: 'https://api.iterable.com/api/events/track', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + userId: 'anonId', + dataFields: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + }, + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'ApplicationLoaded page', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -121,29 +157,59 @@ export const pageScreenTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination: { - ...destination, - Config: { ...destination.Config, mapToSingleEvent: true }, - }, message: { - anonymousId, + anonymousId: 'anonId', name: 'ApplicationLoaded', context: { traits: { email: 'sayan@gmail.com', }, }, - properties: { ...properties, campaignId: '123456', templateId: '1213458' }, + properties: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + campaignId: '123456', + templateId: '1213458', + }, type: 'page', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: true, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - metadata: generateMetadata(1), }, ], - method: 'POST', }, }, output: { @@ -151,22 +217,44 @@ export const pageScreenTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: pageEndpoint, - JSON: { - campaignId: 123456, - templateId: 1213458, - userId: anonymousId, - email: 'sayan@gmail.com', - createdAt: 1598631966468, - eventName: 'Loaded a Page', - dataFields: { ...properties, campaignId: '123456', templateId: '1213458' }, - }, - }), + method: 'POST', + endpoint: 'https://api.iterable.com/api/events/track', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + campaignId: 123456, + templateId: 1213458, + userId: 'anonId', + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'Loaded a Page', + dataFields: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + campaignId: '123456', + templateId: '1213458', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -184,29 +272,57 @@ export const pageScreenTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination: { - ...destination, - Config: { ...destination.Config, trackNamedPages: true, trackAllPages: false }, - }, message: { - anonymousId, + anonymousId: 'anonId', name: 'ApplicationLoaded', context: { traits: { email: 'sayan@gmail.com', }, }, - properties, + properties: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + }, type: 'page', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: false, + trackNamedPages: true, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - metadata: generateMetadata(1), }, ], - method: 'POST', }, }, output: { @@ -214,20 +330,40 @@ export const pageScreenTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: pageEndpoint, - JSON: { - userId: anonymousId, - email: 'sayan@gmail.com', - createdAt: 1598631966468, - eventName: 'ApplicationLoaded page', - dataFields: properties, - }, - }), + method: 'POST', + endpoint: 'https://api.iterable.com/api/events/track', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + userId: 'anonId', + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'ApplicationLoaded page', + dataFields: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -245,29 +381,57 @@ export const pageScreenTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination: { - ...destination, - Config: { ...destination.Config, trackCategorisedPages: true, trackAllPages: false }, - }, message: { - anonymousId, + anonymousId: 'anonId', name: 'ApplicationLoaded', context: { traits: { email: 'sayan@gmail.com', }, }, - properties, + properties: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + }, type: 'screen', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: false, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: true, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - metadata: generateMetadata(1), }, ], - method: 'POST', }, }, output: { @@ -275,20 +439,40 @@ export const pageScreenTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: pageEndpoint, - JSON: { - userId: anonymousId, - dataFields: properties, - email: 'sayan@gmail.com', - createdAt: 1598631966468, - eventName: 'ApplicationLoaded screen', - }, - }), + method: 'POST', + endpoint: 'https://api.iterable.com/api/events/track', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + userId: 'anonId', + dataFields: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + }, + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'ApplicationLoaded screen', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -306,29 +490,59 @@ export const pageScreenTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination: { - ...destination, - Config: { ...destination.Config, mapToSingleEvent: true }, - }, message: { - anonymousId, + anonymousId: 'anonId', name: 'ApplicationLoaded', context: { traits: { email: 'sayan@gmail.com', }, }, - properties: { ...properties, campaignId: '123456', templateId: '1213458' }, + properties: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + campaignId: '123456', + templateId: '1213458', + }, type: 'screen', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: true, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - metadata: generateMetadata(1), }, ], - method: 'POST', }, }, output: { @@ -336,22 +550,44 @@ export const pageScreenTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: pageEndpoint, - JSON: { - campaignId: 123456, - templateId: 1213458, - userId: anonymousId, - email: 'sayan@gmail.com', - createdAt: 1598631966468, - eventName: 'Loaded a Screen', - dataFields: { ...properties, campaignId: '123456', templateId: '1213458' }, - }, - }), + method: 'POST', + endpoint: 'https://api.iterable.com/api/events/track', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + campaignId: 123456, + templateId: 1213458, + userId: 'anonId', + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'Loaded a Screen', + dataFields: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + campaignId: '123456', + templateId: '1213458', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -369,29 +605,57 @@ export const pageScreenTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination: { - ...destination, - Config: { ...destination.Config, trackNamedPages: true, trackAllPages: false }, - }, message: { - anonymousId, + anonymousId: 'anonId', name: 'ApplicationLoaded', context: { traits: { email: 'sayan@gmail.com', }, }, - properties, + properties: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + }, type: 'screen', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: false, + trackNamedPages: true, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - metadata: generateMetadata(1), }, ], - method: 'POST', }, }, output: { @@ -399,20 +663,40 @@ export const pageScreenTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: pageEndpoint, - JSON: { - userId: anonymousId, - email: 'sayan@gmail.com', - createdAt: 1598631966468, - eventName: 'ApplicationLoaded screen', - dataFields: properties, - }, - }), + method: 'POST', + endpoint: 'https://api.iterable.com/api/events/track', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + userId: 'anonId', + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'ApplicationLoaded screen', + dataFields: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -430,26 +714,57 @@ export const pageScreenTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination: overrideDestination(destination, { dataCenter: 'EUDC' }), message: { - anonymousId, + anonymousId: 'anonId', name: 'ApplicationLoaded', context: { traits: { email: 'sayan@gmail.com', }, }, - properties, + properties: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + }, type: 'page', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'EUDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - metadata: generateMetadata(1), }, ], - method: 'POST', }, }, output: { @@ -457,20 +772,40 @@ export const pageScreenTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: pageEndpointEUDC, - JSON: { - userId: anonymousId, - dataFields: properties, - email: 'sayan@gmail.com', - createdAt: 1598631966468, - eventName: 'ApplicationLoaded page', - }, - }), + method: 'POST', + endpoint: 'https://api.eu.iterable.com/api/events/track', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + userId: 'anonId', + dataFields: { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', + }, + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'ApplicationLoaded page', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, diff --git a/test/integrations/destinations/iterable/processor/trackTestData.ts b/test/integrations/destinations/iterable/processor/trackTestData.ts index 7f53cd6d407..91271097a8a 100644 --- a/test/integrations/destinations/iterable/processor/trackTestData.ts +++ b/test/integrations/destinations/iterable/processor/trackTestData.ts @@ -1,137 +1,40 @@ -import { - generateMetadata, - generateTrackPayload, - overrideDestination, - transformResultBuilder, -} from './../../../testUtils'; -import { Destination } from '../../../../../src/types'; import { ProcessorTestData } from '../../../testTypes'; +import { Metadata } from '../../../../../src/types'; -const destination: Destination = { - ID: '123', - Name: 'iterable', - DestinationDefinition: { - ID: '123', - Name: 'iterable', - DisplayName: 'Iterable', - Config: {}, - }, - WorkspaceID: '123', - Transformations: [], - Config: { - apiKey: 'testApiKey', - dataCenter: 'USDC', - preferUserId: false, - trackAllPages: true, - trackNamedPages: false, - mapToSingleEvent: false, - trackCategorisedPages: false, - }, - Enabled: true, -}; - -const headers = { - api_key: 'testApiKey', - 'Content-Type': 'application/json', -}; - -const properties = { - subject: 'resume validate', - sendtime: '2020-01-01', - sendlocation: 'akashdeep@gmail.com', -}; - -const customEventProperties = { - campaignId: '1', - templateId: '0', - user_actual_id: 12345, - category: 'test-category', - email: 'ruchira@rudderlabs.com', - user_actual_role: 'system_admin, system_user', +const baseMetadata: Metadata = { + sourceId: 'default-sourceId', + workspaceId: 'default-workspaceId', + namespace: 'default-namespace', + instanceId: 'default-instance', + sourceType: 'default-source-type', + sourceCategory: 'default-category', + trackingPlanId: 'default-tracking-plan', + trackingPlanVersion: 1, + sourceTpConfig: {}, + mergedTpConfig: {}, + destinationId: 'default-destinationId', + jobRunId: 'default-job-run', + jobId: 1, + sourceBatchId: 'default-batch', + sourceJobId: 'default-source-job', + sourceJobRunId: 'default-source-job-run', + sourceTaskId: 'default-task', + sourceTaskRunId: 'default-task-run', + recordId: {}, + destinationType: 'default-destination-type', + messageId: 'default-message-id', + oauthAccessToken: 'default-token', + messageIds: ['default-message-id'], + rudderId: 'default-rudder-id', + receivedAt: '2025-01-06T04:00:49.698Z', + eventName: 'default-event', + eventType: 'default-type', + sourceDefinitionId: 'default-source-def', + destinationDefinitionId: 'default-dest-def', + transformationId: 'default-transform', + dontBatch: false, }; -const productInfo = { - price: 797, - variant: 'Oak', - quantity: 1, - quickship: true, - full_price: 1328, - product_id: 10606, - non_interaction: 1, - sku: 'JB24691400-W05', - name: 'Vira Console Cabinet', - cart_id: 'bd9b8dbf4ef8ee01d4206b04fe2ee6ae', -}; - -const orderCompletedProductInfo = { - price: 45, - quantity: 1, - total: '1000', - name: 'Shoes', - orderId: 10000, - product_id: 1234, - campaignId: '123456', - templateId: '1213458', -}; - -const products = [ - { - product_id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - price: '19', - position: '1', - category: 'cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - quantity: '2', - }, - { - product_id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - price: '192', - quantity: 22, - position: '12', - category: 'Cars2', - url: 'https://www.example.com/product/path2', - image_url: 'https://www.example.com/product/path.jpg2', - }, -]; - -const items = [ - { - id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - categories: ['cars'], - price: 19, - quantity: 2, - imageUrl: 'https://www.example.com/product/path.jpg', - url: 'https://www.example.com/product/path', - }, - { - id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - categories: ['Cars2'], - price: 192, - quantity: 22, - imageUrl: 'https://www.example.com/product/path.jpg2', - url: 'https://www.example.com/product/path2', - }, -]; - -const userId = 'userId'; -const anonymousId = 'anonId'; -const sentAt = '2020-08-28T16:26:16.473Z'; -const originalTimestamp = '2020-08-28T16:26:06.468Z'; - -const endpoint = 'https://api.iterable.com/api/events/track'; -const endpointEUDC = 'https://api.eu.iterable.com/api/events/track'; -const updateCartEndpoint = 'https://api.iterable.com/api/commerce/updateCart'; -const trackPurchaseEndpoint = 'https://api.iterable.com/api/commerce/trackPurchase'; - export const trackTestData: ProcessorTestData[] = [ { id: 'iterable-track-test-1', @@ -145,22 +48,50 @@ export const trackTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, message: { - anonymousId, + anonymousId: 'anonId', event: 'Email Opened', type: 'track', context: {}, - properties, - sentAt, - originalTimestamp, + properties: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', + }, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - metadata: generateMetadata(1), }, ], - method: 'POST', }, }, output: { @@ -168,19 +99,36 @@ export const trackTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint, - JSON: { - userId: 'anonId', - createdAt: 1598631966468, - eventName: 'Email Opened', - dataFields: properties, - }, - }), + method: 'POST', + endpoint: 'https://api.iterable.com/api/events/track', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + userId: 'anonId', + createdAt: 1598631966468, + eventName: 'Email Opened', + dataFields: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -198,32 +146,109 @@ export const trackTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, - message: generateTrackPayload({ - userId, - anonymousId, - event: 'product added', + message: { + type: 'track', + sentAt: '2020-08-28T16:26:16.473Z', + userId: 'userId', + channel: 'web', context: { + os: { + name: '', + version: '1.12.3', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.11', + namespace: 'com.rudderlabs.javascript', + }, traits: { email: 'sayan@gmail.com', }, + locale: 'en-US', + device: { + token: 'token', + id: 'id', + type: 'ios', + }, + screen: { + density: 2, + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.1.11', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', }, + rudderId: '227egb53wnacyz7f5hopt3jwriuwwk8n2y8i', + messageId: '40q64xrajd4kqt5174iy8889da8kjij55u85', + anonymousId: 'anonId', + originalTimestamp: '2020-08-28T16:26:06.468Z', + event: 'product added', properties: { campaignId: '1', templateId: '0', orderId: 10000, total: 1000, - products, + products: [ + { + product_id: '507f1f77bcf86cd799439011', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + price: '19', + position: '1', + category: 'cars', + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg', + quantity: '2', + }, + { + product_id: '507f1f77bcf86cd7994390112', + sku: '45790-322', + name: 'Monopoly: 3rd Edition2', + price: '192', + quantity: 22, + position: '12', + category: 'Cars2', + url: 'https://www.example.com/product/path2', + image_url: 'https://www.example.com/product/path.jpg2', + }, + ], }, - sentAt, - originalTimestamp, - }), - metadata: generateMetadata(1), + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, + }, }, ], - method: 'POST', }, }, output: { @@ -231,25 +256,59 @@ export const trackTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: updateCartEndpoint, - JSON: { - user: { - email: 'sayan@gmail.com', - dataFields: { + method: 'POST', + endpoint: 'https://api.iterable.com/api/commerce/updateCart', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + user: { email: 'sayan@gmail.com', + dataFields: { + email: 'sayan@gmail.com', + }, + userId: 'userId', + preferUserId: false, + mergeNestedObjects: true, }, - userId, - preferUserId: false, - mergeNestedObjects: true, + items: [ + { + id: '507f1f77bcf86cd799439011', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + categories: ['cars'], + price: 19, + quantity: 2, + imageUrl: 'https://www.example.com/product/path.jpg', + url: 'https://www.example.com/product/path', + }, + { + id: '507f1f77bcf86cd7994390112', + sku: '45790-322', + name: 'Monopoly: 3rd Edition2', + categories: ['Cars2'], + price: 192, + quantity: 22, + imageUrl: 'https://www.example.com/product/path.jpg2', + url: 'https://www.example.com/product/path2', + }, + ], }, - items, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, - }), + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -267,32 +326,109 @@ export const trackTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, - message: generateTrackPayload({ - userId, - anonymousId, - event: 'order completed', + message: { + type: 'track', + sentAt: '2020-08-28T16:26:16.473Z', + userId: 'userId', + channel: 'web', context: { + os: { + name: '', + version: '1.12.3', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.11', + namespace: 'com.rudderlabs.javascript', + }, traits: { email: 'sayan@gmail.com', }, + locale: 'en-US', + device: { + token: 'token', + id: 'id', + type: 'ios', + }, + screen: { + density: 2, + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.1.11', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', }, + rudderId: 'npe99e7yc7apntgd9roobt2n8i7262rsg6vr', + messageId: '68ygodw5mj88lmjm5sm765tatvscrucqo6kx', + anonymousId: 'anonId', + originalTimestamp: '2020-08-28T16:26:06.468Z', + event: 'order completed', properties: { orderId: 10000, total: '1000', campaignId: '123456', templateId: '1213458', - products, + products: [ + { + product_id: '507f1f77bcf86cd799439011', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + price: '19', + position: '1', + category: 'cars', + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg', + quantity: '2', + }, + { + product_id: '507f1f77bcf86cd7994390112', + sku: '45790-322', + name: 'Monopoly: 3rd Edition2', + price: '192', + quantity: 22, + position: '12', + category: 'Cars2', + url: 'https://www.example.com/product/path2', + image_url: 'https://www.example.com/product/path.jpg2', + }, + ], + }, + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, }, - sentAt, - originalTimestamp, - }), - metadata: generateMetadata(1), + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, + }, }, ], - method: 'POST', }, }, output: { @@ -300,37 +436,94 @@ export const trackTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: trackPurchaseEndpoint, - JSON: { - dataFields: { - orderId: 10000, - total: '1000', - campaignId: '123456', - templateId: '1213458', - products, - }, - id: '10000', - createdAt: 1598631966468, - campaignId: 123456, - templateId: 1213458, - total: 1000, - user: { - email: 'sayan@gmail.com', + method: 'POST', + endpoint: 'https://api.iterable.com/api/commerce/trackPurchase', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { dataFields: { + orderId: 10000, + total: '1000', + campaignId: '123456', + templateId: '1213458', + products: [ + { + product_id: '507f1f77bcf86cd799439011', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + price: '19', + position: '1', + category: 'cars', + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg', + quantity: '2', + }, + { + product_id: '507f1f77bcf86cd7994390112', + sku: '45790-322', + name: 'Monopoly: 3rd Edition2', + price: '192', + quantity: 22, + position: '12', + category: 'Cars2', + url: 'https://www.example.com/product/path2', + image_url: 'https://www.example.com/product/path.jpg2', + }, + ], + }, + id: '10000', + createdAt: 1598631966468, + campaignId: 123456, + templateId: 1213458, + total: 1000, + user: { email: 'sayan@gmail.com', + dataFields: { + email: 'sayan@gmail.com', + }, + userId: 'userId', + preferUserId: false, + mergeNestedObjects: true, }, - userId, - preferUserId: false, - mergeNestedObjects: true, + items: [ + { + id: '507f1f77bcf86cd799439011', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + categories: ['cars'], + price: 19, + quantity: 2, + imageUrl: 'https://www.example.com/product/path.jpg', + url: 'https://www.example.com/product/path', + }, + { + id: '507f1f77bcf86cd7994390112', + sku: '45790-322', + name: 'Monopoly: 3rd Edition2', + categories: ['Cars2'], + price: 192, + quantity: 22, + imageUrl: 'https://www.example.com/product/path.jpg2', + url: 'https://www.example.com/product/path2', + }, + ], }, - items, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, - }), + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -348,26 +541,87 @@ export const trackTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, - message: generateTrackPayload({ - userId, - anonymousId, - event: 'test track event GA3', + message: { + type: 'track', + sentAt: '2020-08-28T16:26:16.473Z', + userId: 'userId', + channel: 'web', context: { + os: { + name: '', + version: '1.12.3', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.11', + namespace: 'com.rudderlabs.javascript', + }, traits: { email: 'sayan@gmail.com', }, + locale: 'en-US', + device: { + token: 'token', + id: 'id', + type: 'ios', + }, + screen: { + density: 2, + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.1.11', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + rudderId: 'id2nbnto38rw1v5wiqyc81xe61c7t420zpjb', + messageId: '28pdlaljhp7i1woa7b0fhj47rlz3g4z2pvdw', + anonymousId: 'anonId', + originalTimestamp: '2020-08-28T16:26:06.468Z', + event: 'test track event GA3', + properties: { + campaignId: '1', + templateId: '0', + user_actual_id: 12345, + category: 'test-category', + email: 'ruchira@rudderlabs.com', + user_actual_role: 'system_admin, system_user', + }, + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, }, - properties: customEventProperties, - sentAt, - originalTimestamp, - }), - metadata: generateMetadata(1), + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, + }, }, ], - method: 'POST', }, }, output: { @@ -375,22 +629,42 @@ export const trackTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint, - JSON: { - email: 'ruchira@rudderlabs.com', - dataFields: customEventProperties, - userId, - eventName: 'test track event GA3', - createdAt: 1598631966468, - campaignId: 1, - templateId: 0, - }, - }), + method: 'POST', + endpoint: 'https://api.iterable.com/api/events/track', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + email: 'ruchira@rudderlabs.com', + dataFields: { + campaignId: '1', + templateId: '0', + user_actual_id: 12345, + category: 'test-category', + email: 'ruchira@rudderlabs.com', + user_actual_role: 'system_admin, system_user', + }, + userId: 'userId', + eventName: 'test track event GA3', + createdAt: 1598631966468, + campaignId: 1, + templateId: 0, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -408,26 +682,91 @@ export const trackTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, - message: generateTrackPayload({ - userId, - anonymousId, - event: 'product added', + message: { + type: 'track', + sentAt: '2020-08-28T16:26:16.473Z', + userId: 'userId', + channel: 'web', context: { + os: { + name: '', + version: '1.12.3', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.11', + namespace: 'com.rudderlabs.javascript', + }, traits: { email: 'jessica@jlpdesign.net', }, + locale: 'en-US', + device: { + token: 'token', + id: 'id', + type: 'ios', + }, + screen: { + density: 2, + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.1.11', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + rudderId: 'gm8wm4385x4kfzl464h7tjtob474i17anotc', + messageId: 'jslc0490479rziix9h0ya6z6qpn2taqmryro', + anonymousId: 'anonId', + originalTimestamp: '2020-08-28T16:26:06.468Z', + event: 'product added', + properties: { + price: 797, + variant: 'Oak', + quantity: 1, + quickship: true, + full_price: 1328, + product_id: 10606, + non_interaction: 1, + sku: 'JB24691400-W05', + name: 'Vira Console Cabinet', + cart_id: 'bd9b8dbf4ef8ee01d4206b04fe2ee6ae', + }, + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, }, - properties: productInfo, - sentAt, - originalTimestamp, - }), - metadata: generateMetadata(1), + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, + }, }, ], - method: 'POST', }, }, output: { @@ -435,33 +774,46 @@ export const trackTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: updateCartEndpoint, - JSON: { - user: { - email: 'jessica@jlpdesign.net', - dataFields: { + method: 'POST', + endpoint: 'https://api.iterable.com/api/commerce/updateCart', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + user: { email: 'jessica@jlpdesign.net', + dataFields: { + email: 'jessica@jlpdesign.net', + }, + userId: 'userId', + preferUserId: false, + mergeNestedObjects: true, }, - userId, - preferUserId: false, - mergeNestedObjects: true, + items: [ + { + id: 10606, + sku: 'JB24691400-W05', + name: 'Vira Console Cabinet', + price: 797, + quantity: 1, + }, + ], }, - items: [ - { - id: productInfo.product_id, - sku: productInfo.sku, - name: productInfo.name, - price: productInfo.price, - quantity: productInfo.quantity, - }, - ], + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, - }), + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -479,32 +831,94 @@ export const trackTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, - message: generateTrackPayload({ - userId, - anonymousId, - event: 'product added', + message: { + type: 'track', + sentAt: '2020-08-28T16:26:16.473Z', + userId: 'userId', + channel: 'web', context: { + os: { + name: '', + version: '1.12.3', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.11', + namespace: 'com.rudderlabs.javascript', + }, traits: { email: 'jessica@jlpdesign.net', }, + locale: 'en-US', + device: { + token: 'token', + id: 'id', + type: 'ios', + }, + screen: { + density: 2, + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.1.11', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', }, + rudderId: '4osg2zcrsrh2accmbijxm0zqixtxyrsun8uc', + messageId: 'x901bx7paxtr7ktja5mhd1mi8q4lr5vlrl2x', + anonymousId: 'anonId', + originalTimestamp: '2020-08-28T16:26:06.468Z', + event: 'product added', properties: { campaignId: '1', templateId: '0', orderId: 10000, total: 1000, - ...products[1], + product_id: '507f1f77bcf86cd7994390112', + sku: '45790-322', + name: 'Monopoly: 3rd Edition2', + price: '192', + quantity: 22, + position: '12', + category: 'Cars2', + url: 'https://www.example.com/product/path2', + image_url: 'https://www.example.com/product/path.jpg2', + }, + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, }, - sentAt, - originalTimestamp, - }), - metadata: generateMetadata(1), + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, + }, }, ], - method: 'POST', }, }, output: { @@ -512,36 +926,49 @@ export const trackTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: updateCartEndpoint, - JSON: { - user: { - email: 'jessica@jlpdesign.net', - dataFields: { + method: 'POST', + endpoint: 'https://api.iterable.com/api/commerce/updateCart', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + user: { email: 'jessica@jlpdesign.net', + dataFields: { + email: 'jessica@jlpdesign.net', + }, + userId: 'userId', + preferUserId: false, + mergeNestedObjects: true, }, - userId, - preferUserId: false, - mergeNestedObjects: true, + items: [ + { + price: 192, + url: 'https://www.example.com/product/path2', + sku: '45790-322', + name: 'Monopoly: 3rd Edition2', + id: '507f1f77bcf86cd7994390112', + quantity: 22, + imageUrl: 'https://www.example.com/product/path.jpg2', + categories: ['Cars2'], + }, + ], }, - items: [ - { - price: 192, - url: products[1].url, - sku: products[1].sku, - name: products[1].name, - id: products[1].product_id, - quantity: products[1].quantity, - imageUrl: products[1].image_url, - categories: [products[1].category], - }, - ], + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, - }), + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -559,26 +986,89 @@ export const trackTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, - message: generateTrackPayload({ - userId, - anonymousId, - event: 'order completed', + message: { + type: 'track', + sentAt: '2020-08-28T16:26:16.473Z', + userId: 'userId', + channel: 'web', context: { + os: { + name: '', + version: '1.12.3', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.11', + namespace: 'com.rudderlabs.javascript', + }, traits: { email: 'jessica@jlpdesign.net', }, + locale: 'en-US', + device: { + token: 'token', + id: 'id', + type: 'ios', + }, + screen: { + density: 2, + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.1.11', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + rudderId: '9ovlz4kuew0wjcbwymj3vlhkngzixp9evf19', + messageId: 'ev0qyvsclinoh4z4e1uz4d8pdhrnf17q0rjd', + anonymousId: 'anonId', + originalTimestamp: '2020-08-28T16:26:06.468Z', + event: 'order completed', + properties: { + price: 45, + quantity: 1, + total: '1000', + name: 'Shoes', + orderId: 10000, + product_id: 1234, + campaignId: '123456', + templateId: '1213458', + }, + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, }, - properties: orderCompletedProductInfo, - sentAt, - originalTimestamp, - }), - metadata: generateMetadata(1), + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, + }, }, ], - method: 'POST', }, }, output: { @@ -586,38 +1076,60 @@ export const trackTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: trackPurchaseEndpoint, - JSON: { - dataFields: orderCompletedProductInfo, - user: { - email: 'jessica@jlpdesign.net', + method: 'POST', + endpoint: 'https://api.iterable.com/api/commerce/trackPurchase', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { dataFields: { + price: 45, + quantity: 1, + total: '1000', + name: 'Shoes', + orderId: 10000, + product_id: 1234, + campaignId: '123456', + templateId: '1213458', + }, + user: { email: 'jessica@jlpdesign.net', + dataFields: { + email: 'jessica@jlpdesign.net', + }, + userId: 'userId', + preferUserId: false, + mergeNestedObjects: true, }, - userId, - preferUserId: false, - mergeNestedObjects: true, + id: '10000', + total: 1000, + campaignId: 123456, + templateId: 1213458, + createdAt: 1598631966468, + items: [ + { + id: 1234, + name: 'Shoes', + price: 45, + quantity: 1, + }, + ], }, - id: '10000', - total: 1000, - campaignId: 123456, - templateId: 1213458, - createdAt: 1598631966468, - items: [ - { - id: orderCompletedProductInfo.product_id, - name: orderCompletedProductInfo.name, - price: orderCompletedProductInfo.price, - quantity: orderCompletedProductInfo.quantity, - }, - ], + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, - }), + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -635,21 +1147,49 @@ export const trackTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, message: { - anonymousId, + anonymousId: 'anonId', type: 'track', context: {}, - properties, - sentAt, - originalTimestamp, + properties: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', + }, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - metadata: generateMetadata(1), }, ], - method: 'POST', }, }, output: { @@ -657,18 +1197,35 @@ export const trackTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint, - JSON: { - userId: anonymousId, - createdAt: 1598631966468, - dataFields: properties, - }, - }), + method: 'POST', + endpoint: 'https://api.iterable.com/api/events/track', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + userId: 'anonId', + createdAt: 1598631966468, + dataFields: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -686,22 +1243,50 @@ export const trackTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, message: { - userId, - anonymousId, + userId: 'userId', + anonymousId: 'anonId', type: 'track', context: {}, - properties, - sentAt, - originalTimestamp, + properties: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', + }, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'USDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - metadata: generateMetadata(1), }, ], - method: 'POST', }, }, output: { @@ -709,18 +1294,35 @@ export const trackTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint, - JSON: { - userId, - createdAt: 1598631966468, - dataFields: properties, - }, - }), + method: 'POST', + endpoint: 'https://api.iterable.com/api/events/track', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + userId: 'userId', + createdAt: 1598631966468, + dataFields: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, @@ -738,22 +1340,50 @@ export const trackTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination: overrideDestination(destination, { dataCenter: 'EUDC' }), message: { - anonymousId, + anonymousId: 'anonId', event: 'Email Opened', type: 'track', context: {}, - properties, - sentAt, - originalTimestamp, + properties: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', + }, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', + }, + metadata: baseMetadata, + destination: { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + dataCenter: 'EUDC', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - metadata: generateMetadata(1), }, ], - method: 'POST', }, }, output: { @@ -761,19 +1391,36 @@ export const trackTestData: ProcessorTestData[] = [ status: 200, body: [ { - output: transformResultBuilder({ + output: { + version: '1', + type: 'REST', userId: '', - headers, - endpoint: endpointEUDC, - JSON: { - userId: 'anonId', - createdAt: 1598631966468, - eventName: 'Email Opened', - dataFields: properties, - }, - }), + method: 'POST', + endpoint: 'https://api.eu.iterable.com/api/events/track', + headers: { + api_key: 'testApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + userId: 'anonId', + createdAt: 1598631966468, + eventName: 'Email Opened', + dataFields: { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: baseMetadata, statusCode: 200, - metadata: generateMetadata(1), }, ], }, diff --git a/test/integrations/destinations/iterable/processor/validationTestData.ts b/test/integrations/destinations/iterable/processor/validationTestData.ts index 5e4b9478fd3..8f0813de3f2 100644 --- a/test/integrations/destinations/iterable/processor/validationTestData.ts +++ b/test/integrations/destinations/iterable/processor/validationTestData.ts @@ -1,8 +1,41 @@ -import { generateMetadata } from './../../../testUtils'; -import { Destination } from '../../../../../src/types'; import { ProcessorTestData } from '../../../testTypes'; +import { Metadata, Destination } from '../../../../../src/types'; -const destination: Destination = { +const baseMetadata: Metadata = { + sourceId: 'default-sourceId', + workspaceId: 'default-workspaceId', + namespace: 'default-namespace', + instanceId: 'default-instance', + sourceType: 'default-source-type', + sourceCategory: 'default-category', + trackingPlanId: 'default-tracking-plan', + trackingPlanVersion: 1, + sourceTpConfig: {}, + mergedTpConfig: {}, + destinationId: 'default-destinationId', + jobRunId: 'default-job-run', + jobId: 1, + sourceBatchId: 'default-batch', + sourceJobId: 'default-source-job', + sourceJobRunId: 'default-source-job-run', + sourceTaskId: 'default-task', + sourceTaskRunId: 'default-task-run', + recordId: {}, + destinationType: 'default-destination-type', + messageId: 'default-message-id', + oauthAccessToken: 'default-token', + messageIds: ['default-message-id'], + rudderId: 'default-rudder-id', + receivedAt: '2025-01-06T04:14:40.785Z', + eventName: 'default-event', + eventType: 'default-type', + sourceDefinitionId: 'default-source-def', + destinationDefinitionId: 'default-dest-def', + transformationId: 'default-transform', + dontBatch: false, +}; + +const baseDestination: Destination = { ID: '123', Name: 'iterable', DestinationDefinition: { @@ -11,8 +44,6 @@ const destination: Destination = { DisplayName: 'Iterable', Config: {}, }, - WorkspaceID: '123', - Transformations: [], Config: { apiKey: 'testApiKey', mapToSingleEvent: false, @@ -21,26 +52,11 @@ const destination: Destination = { trackNamedPages: false, }, Enabled: true, -}; - -const properties = { - url: 'https://dominos.com', - title: 'Pizza', - referrer: 'https://google.com', -}; - -const sentAt = '2020-08-28T16:26:16.473Z'; -const originalTimestamp = '2020-08-28T16:26:06.468Z'; - -const expectedStatTags = { - destType: 'ITERABLE', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - destinationId: 'default-destinationId', - workspaceId: 'default-workspaceId', + WorkspaceID: '123', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }; export const validationTestData: ProcessorTestData[] = [ @@ -56,9 +72,9 @@ export const validationTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, message: { userId: 'sajal12', anonymousId: 'abcdeeeeeeeexxxx102', @@ -67,15 +83,19 @@ export const validationTestData: ProcessorTestData[] = [ email: 'abc@example.com', }, }, - properties, + properties: { + url: 'https://dominos.com', + title: 'Pizza', + referrer: 'https://google.com', + }, type: 'page', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', }, - metadata: generateMetadata(1), + metadata: baseMetadata, + destination: baseDestination, }, ], - method: 'POST', }, }, output: { @@ -83,10 +103,19 @@ export const validationTestData: ProcessorTestData[] = [ status: 200, body: [ { + metadata: baseMetadata, statusCode: 400, error: 'Invalid page call', - statTags: { ...expectedStatTags, errorType: 'configuration' }, - metadata: generateMetadata(1), + statTags: { + destType: 'ITERABLE', + errorCategory: 'dataValidation', + errorType: 'configuration', + feature: 'processor', + implementation: 'native', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, }, ], }, @@ -104,19 +133,19 @@ export const validationTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, message: { context: {}, type: 'identify', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', }, - metadata: generateMetadata(1), + metadata: baseMetadata, + destination: baseDestination, }, ], - method: 'POST', }, }, output: { @@ -124,10 +153,19 @@ export const validationTestData: ProcessorTestData[] = [ status: 200, body: [ { + metadata: baseMetadata, statusCode: 400, error: 'userId or email is mandatory for this request', - statTags: expectedStatTags, - metadata: generateMetadata(1), + statTags: { + destType: 'ITERABLE', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, }, ], }, @@ -145,19 +183,19 @@ export const validationTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, message: { context: {}, type: 'group', - sentAt, - originalTimestamp, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', }, - metadata: generateMetadata(1), + metadata: baseMetadata, + destination: baseDestination, }, ], - method: 'POST', }, }, output: { @@ -165,10 +203,19 @@ export const validationTestData: ProcessorTestData[] = [ status: 200, body: [ { + metadata: baseMetadata, statusCode: 400, error: 'Message type group not supported', - statTags: expectedStatTags, - metadata: generateMetadata(1), + statTags: { + destType: 'ITERABLE', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, }, ], }, @@ -186,20 +233,24 @@ export const validationTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, message: { context: {}, type: 'alias', - properties, - sentAt, - originalTimestamp, + properties: { + url: 'https://dominos.com', + title: 'Pizza', + referrer: 'https://google.com', + }, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', }, - metadata: generateMetadata(1), + metadata: baseMetadata, + destination: baseDestination, }, ], - method: 'POST', }, }, output: { @@ -207,10 +258,19 @@ export const validationTestData: ProcessorTestData[] = [ status: 200, body: [ { + metadata: baseMetadata, statusCode: 400, error: 'Missing required value from "previousId"', - statTags: expectedStatTags, - metadata: generateMetadata(1), + statTags: { + destType: 'ITERABLE', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, }, ], }, @@ -228,22 +288,26 @@ export const validationTestData: ProcessorTestData[] = [ version: 'v0', input: { request: { + method: 'POST', body: [ { - destination, message: { context: {}, type: 'alias', previousId: 'old@email.com', anonymousId: 'anonId', - properties, - sentAt, - originalTimestamp, + properties: { + url: 'https://dominos.com', + title: 'Pizza', + referrer: 'https://google.com', + }, + sentAt: '2020-08-28T16:26:16.473Z', + originalTimestamp: '2020-08-28T16:26:06.468Z', }, - metadata: generateMetadata(1), + metadata: baseMetadata, + destination: baseDestination, }, ], - method: 'POST', }, }, output: { @@ -251,10 +315,19 @@ export const validationTestData: ProcessorTestData[] = [ status: 200, body: [ { + metadata: baseMetadata, statusCode: 400, error: 'Missing required value from "userId"', - statTags: expectedStatTags, - metadata: generateMetadata(1), + statTags: { + destType: 'ITERABLE', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, }, ], }, diff --git a/test/integrations/destinations/iterable/router/data.ts b/test/integrations/destinations/iterable/router/data.ts index 1917c078ebd..63c6f12a5e8 100644 --- a/test/integrations/destinations/iterable/router/data.ts +++ b/test/integrations/destinations/iterable/router/data.ts @@ -1,7 +1,15 @@ -export const data = [ +import { RouterTestData } from '../../../testTypes'; +import {} from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; +import { getBrowserInfo } from '../../../../../src/v0/util'; + +export const data: RouterTestData[] = [ { + id: 'router-1736135082961', name: 'iterable', description: 'Test 0', + scenario: 'Default router scenario', + successCriteria: 'Router test should pass successfully', feature: 'router', module: 'destination', version: 'v0', @@ -15,7 +23,10 @@ export const data = [ sentAt: '2022-09-27T11:13:03.777Z', messageId: '9ad41366-8060-4c9f-b181-f6bea67d5469', originalTimestamp: '2022-09-27T11:13:03.777Z', - traits: { ruchira: 'donaldbaker@ellis.com', new_field2: 'GB' }, + traits: { + ruchira: 'donaldbaker@ellis.com', + new_field2: 'GB', + }, channel: 'sources', rudderId: '3d51640c-ab09-42c1-b7b2-db6ab433b35e', context: { @@ -29,7 +40,11 @@ export const data = [ }, mappedToDestination: 'true', externalId: [ - { id: 'Tiffany', type: 'ITERABLE-test-ruchira', identifierType: 'itemId' }, + { + id: 'Tiffany', + type: 'ITERABLE-test-ruchira', + identifierType: 'itemId', + }, ], }, timestamp: '2022-09-27T11:12:59.079Z', @@ -38,10 +53,26 @@ export const data = [ recordId: '10', request_ip: '10.1.86.248', }, - metadata: { jobId: 2, userId: 'u1' }, + metadata: generateMetadata(1), destination: { - Config: { apiKey: '583af2f8-15ba-49c0-8511-76383e7de07e', hubID: '22066036' }, + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, + Config: { + apiKey: '583af2f8-15ba-49c0-8511-76383e7de07e', + hubID: '22066036', + }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, }, { @@ -50,7 +81,10 @@ export const data = [ sentAt: '2022-09-27T11:13:03.777Z', messageId: '9ad41366-8060-4c9f-b181-f6bea67d5469', originalTimestamp: '2022-09-27T11:13:03.777Z', - traits: { ruchira: 'abc@ellis.com', new_field2: 'GB1' }, + traits: { + ruchira: 'abc@ellis.com', + new_field2: 'GB1', + }, channel: 'sources', rudderId: '3d51640c-ab09-42c1-b7b2-db6ab433b35e', context: { @@ -64,7 +98,11 @@ export const data = [ }, mappedToDestination: 'true', externalId: [ - { id: 'ABC', type: 'ITERABLE-test-ruchira', identifierType: 'itemId' }, + { + id: 'ABC', + type: 'ITERABLE-test-ruchira', + identifierType: 'itemId', + }, ], }, timestamp: '2022-09-27T11:12:59.079Z', @@ -73,15 +111,32 @@ export const data = [ recordId: '10', request_ip: '10.1.86.248', }, - metadata: { jobId: 2, userId: 'u1' }, + metadata: generateMetadata(2), destination: { - Config: { apiKey: '583af2f8-15ba-49c0-8511-76383e7de07e', hubID: '22066036' }, + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, + Config: { + apiKey: '583af2f8-15ba-49c0-8511-76383e7de07e', + hubID: '22066036', + }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, }, ], destType: 'iterable', }, + method: 'POST', }, }, output: { @@ -103,8 +158,14 @@ export const data = [ body: { JSON: { documents: { - Tiffany: { ruchira: 'donaldbaker@ellis.com', new_field2: 'GB' }, - ABC: { ruchira: 'abc@ellis.com', new_field2: 'GB1' }, + Tiffany: { + ruchira: 'donaldbaker@ellis.com', + new_field2: 'GB', + }, + ABC: { + ruchira: 'abc@ellis.com', + new_field2: 'GB1', + }, }, replaceUploadedFieldsOnly: true, }, @@ -114,16 +175,29 @@ export const data = [ }, files: {}, }, - metadata: [ - { jobId: 2, userId: 'u1' }, - { jobId: 2, userId: 'u1' }, - ], - batched: true, + metadata: [generateMetadata(1), generateMetadata(2)], statusCode: 200, destination: { - Config: { apiKey: '583af2f8-15ba-49c0-8511-76383e7de07e', hubID: '22066036' }, + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, + Config: { + apiKey: '583af2f8-15ba-49c0-8511-76383e7de07e', + hubID: '22066036', + }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, + batched: true, }, ], }, @@ -131,8 +205,11 @@ export const data = [ }, }, { + id: 'router-1736135082962', name: 'iterable', description: 'routerTest 1', + scenario: 'Default router scenario', + successCriteria: 'Router test should pass successfully', feature: 'router', module: 'destination', version: 'v0', @@ -145,8 +222,15 @@ export const data = [ type: 'track', event: 'Email Opened', sentAt: '2020-08-28T16:26:16.473Z', - context: { library: { name: 'analytics-node', version: '0.0.3' } }, - _metadata: { nodeVersion: '10.22.0' }, + context: { + library: { + name: 'analytics-node', + version: '0.0.3', + }, + }, + _metadata: { + nodeVersion: '10.22.0', + }, messageId: 'node-570110489d3e99b234b18af9a9eca9d4-6009779e-82d7-469d-aaeb-5ccf162b0453', properties: { @@ -157,8 +241,16 @@ export const data = [ anonymousId: 'abcdeeeeeeeexxxx102', originalTimestamp: '2020-08-28T16:26:06.468Z', }, - metadata: { jobId: 2, userId: 'u1' }, + metadata: generateMetadata(2), destination: { + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, Config: { apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', mapToSingleEvent: false, @@ -167,6 +259,11 @@ export const data = [ trackNamedPages: false, }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, }, { @@ -187,16 +284,29 @@ export const data = [ email: 'manashi@website.com', country: 'India', }, - library: { name: 'analytics-node', version: '0.0.3' }, + library: { + name: 'analytics-node', + version: '0.0.3', + }, + }, + _metadata: { + nodeVersion: '10.22.0', }, - _metadata: { nodeVersion: '10.22.0' }, messageId: 'node-cc3ef811f686139ee527b806ee0129ef-163a3a88-266f-447e-8cce-34a8f42f8dcd', anonymousId: 'abcdeeeeeeeexxxx102', originalTimestamp: '2020-08-28T16:26:06.462Z', }, - metadata: { jobId: 3, userId: 'u1' }, + metadata: generateMetadata(3), destination: { + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, Config: { apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', mapToSingleEvent: false, @@ -205,6 +315,11 @@ export const data = [ trackNamedPages: false, }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, }, { @@ -217,14 +332,24 @@ export const data = [ namespace: 'com.rudderlabs.javascript', version: '1.0.0', }, - traits: { email: 'sayan@gmail.com' }, - library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + traits: { + email: 'sayan@gmail.com', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', locale: 'en-US', ip: '0.0.0.0', - os: { name: '', version: '' }, - screen: { density: 2 }, + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, }, type: 'page', messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', @@ -239,12 +364,22 @@ export const data = [ url: '', category: 'test-category', }, - integrations: { All: true }, + integrations: { + All: true, + }, name: 'ApplicationLoaded', sentAt: '2019-10-14T11:15:53.296Z', }, - metadata: { jobId: 4, userId: 'u1' }, + metadata: generateMetadata(4), destination: { + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, Config: { apiKey: '12345', dataCenter: 'USDC', @@ -254,6 +389,11 @@ export const data = [ trackNamedPages: false, }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, }, { @@ -276,7 +416,9 @@ export const data = [ task_run_id: 'c5tar6cqgmgmcjvupdi0', version: 'release.v1.6.8', }, - device: { token: 54321 }, + device: { + token: 54321, + }, }, messageId: '2f052f7c-f694-4849-a7ed-a432f7ffa0a4', originalTimestamp: '2021-10-28T14:03:50.503Z', @@ -297,7 +439,7 @@ export const data = [ type: 'identify', userId: 'lynnanderson@smith.net', }, - metadata: { jobId: 5, userId: 'u1' }, + metadata: generateMetadata(5), destination: { ID: '1zia9wKshXt80YksLmUdJnr7IHI', Name: 'test_iterable', @@ -337,7 +479,6 @@ export const data = [ transformAt: 'processor', transformAtV1: 'processor', }, - ResponseRules: null, }, Config: { apiKey: '12345', @@ -348,11 +489,12 @@ export const data = [ trackNamedPages: true, }, Enabled: true, + WorkspaceID: 'default-workspace', Transformations: [], + RevisionID: 'default-revision', IsProcessorEnabled: true, + IsConnectionEnabled: true, }, - libraries: [], - request: { query: {} }, }, { message: { @@ -364,14 +506,24 @@ export const data = [ namespace: 'com.rudderlabs.javascript', version: '1.0.0', }, - traits: { email: 'sayan@gmail.com' }, - library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + traits: { + email: 'sayan@gmail.com', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', locale: 'en-US', ip: '0.0.0.0', - os: { name: '', version: '' }, - screen: { density: 2 }, + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, }, event: 'product added', type: 'track', @@ -409,12 +561,22 @@ export const data = [ }, ], }, - integrations: { All: true }, + integrations: { + All: true, + }, name: 'ApplicationLoaded', sentAt: '2019-10-14T11:15:53.296Z', }, - metadata: { jobId: 6, userId: 'u1' }, + metadata: generateMetadata(6), destination: { + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, Config: { apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', dataCenter: 'USDC', @@ -424,14 +586,26 @@ export const data = [ trackNamedPages: false, }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, }, { message: { type: 'page', sentAt: '2020-08-28T16:26:16.473Z', - context: { library: { name: 'analytics-node', version: '0.0.3' } }, - _metadata: { nodeVersion: '10.22.0' }, + context: { + library: { + name: 'analytics-node', + version: '0.0.3', + }, + }, + _metadata: { + nodeVersion: '10.22.0', + }, messageId: 'node-6f62b91e789a636929ca38aed01c5f6e-103c720d-81bd-4742-98d6-d45a65aed23e', properties: { @@ -442,8 +616,16 @@ export const data = [ anonymousId: 'abcdeeeeeeeexxxx102', originalTimestamp: '2020-08-28T16:26:06.468Z', }, - metadata: { jobId: 7, userId: 'u1' }, + metadata: generateMetadata(7), destination: { + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, Config: { apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', dataCenter: 'USDC', @@ -453,14 +635,26 @@ export const data = [ trackNamedPages: false, }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, }, { message: { type: 'alias', sentAt: '2020-08-28T16:26:16.473Z', - context: { library: { name: 'analytics-node', version: '0.0.3' } }, - _metadata: { nodeVersion: '10.22.0' }, + context: { + library: { + name: 'analytics-node', + version: '0.0.3', + }, + }, + _metadata: { + nodeVersion: '10.22.0', + }, messageId: 'node-6f62b91e789a636929ca38aed01c5f6e-103c720d-81bd-4742-98d6-d45a65aed23e', properties: { @@ -473,8 +667,16 @@ export const data = [ anonymousId: 'abcdeeeeeeeexxxx102', originalTimestamp: '2020-08-28T16:26:06.468Z', }, - metadata: { jobId: 8, userId: 'u1' }, + metadata: generateMetadata(8), destination: { + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, Config: { apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', dataCenter: 'USDC', @@ -484,11 +686,17 @@ export const data = [ trackNamedPages: false, }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, }, ], destType: 'iterable', }, + method: 'POST', }, }, output: { @@ -528,10 +736,17 @@ export const data = [ }, files: {}, }, - metadata: [{ jobId: 2, userId: 'u1' }], - batched: true, + metadata: [generateMetadata(2)], statusCode: 200, destination: { + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, Config: { apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', mapToSingleEvent: false, @@ -540,7 +755,13 @@ export const data = [ trackNamedPages: false, }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, + batched: true, }, { batchedRequest: { @@ -576,10 +797,17 @@ export const data = [ }, files: {}, }, - metadata: [{ jobId: 3, userId: 'u1' }], - batched: true, + metadata: [generateMetadata(3)], statusCode: 200, destination: { + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, Config: { apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', mapToSingleEvent: false, @@ -588,7 +816,13 @@ export const data = [ trackNamedPages: false, }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, + batched: true, }, { batchedRequest: { @@ -596,7 +830,10 @@ export const data = [ type: 'REST', method: 'POST', endpoint: 'https://api.iterable.com/api/events/trackBulk', - headers: { 'Content-Type': 'application/json', api_key: '12345' }, + headers: { + 'Content-Type': 'application/json', + api_key: '12345', + }, params: {}, body: { JSON: { @@ -623,10 +860,17 @@ export const data = [ }, files: {}, }, - metadata: [{ jobId: 4, userId: 'u1' }], - batched: true, + metadata: [generateMetadata(4)], statusCode: 200, destination: { + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, Config: { apiKey: '12345', dataCenter: 'USDC', @@ -636,7 +880,13 @@ export const data = [ trackNamedPages: false, }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, + batched: true, }, { batchedRequest: { @@ -653,7 +903,9 @@ export const data = [ JSON: { user: { email: 'sayan@gmail.com', - dataFields: { email: 'sayan@gmail.com' }, + dataFields: { + email: 'sayan@gmail.com', + }, userId: '12345', preferUserId: true, mergeNestedObjects: true, @@ -687,10 +939,17 @@ export const data = [ }, files: {}, }, - metadata: [{ jobId: 6, userId: 'u1' }], - batched: false, + metadata: [generateMetadata(6)], statusCode: 200, destination: { + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, Config: { apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', dataCenter: 'USDC', @@ -700,7 +959,13 @@ export const data = [ trackNamedPages: false, }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, + batched: false, }, { batchedRequest: { @@ -734,10 +999,17 @@ export const data = [ }, files: {}, }, - metadata: [{ jobId: 7, userId: 'u1' }], - batched: true, + metadata: [generateMetadata(7)], statusCode: 200, destination: { + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, Config: { apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', dataCenter: 'USDC', @@ -747,7 +1019,13 @@ export const data = [ trackNamedPages: false, }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, + batched: true, }, { batchedRequest: { @@ -761,17 +1039,27 @@ export const data = [ }, params: {}, body: { - JSON: { currentEmail: 'old@email.com', newEmail: 'new@email.com' }, + JSON: { + currentEmail: 'old@email.com', + newEmail: 'new@email.com', + }, JSON_ARRAY: {}, XML: {}, FORM: {}, }, files: {}, }, - metadata: [{ jobId: 8, userId: 'u1' }], - batched: false, + metadata: [generateMetadata(8)], statusCode: 200, destination: { + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, Config: { apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', dataCenter: 'USDC', @@ -781,7 +1069,13 @@ export const data = [ trackNamedPages: false, }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, + batched: false, }, { batchedRequest: { @@ -789,7 +1083,10 @@ export const data = [ type: 'REST', method: 'POST', endpoint: 'https://api.iterable.com/api/users/bulkUpdate', - headers: { 'Content-Type': 'application/json', api_key: '12345' }, + headers: { + 'Content-Type': 'application/json', + api_key: '12345', + }, params: {}, body: { JSON: { @@ -817,8 +1114,7 @@ export const data = [ }, files: {}, }, - metadata: [{ jobId: 5, userId: 'u1' }], - batched: true, + metadata: [generateMetadata(5)], statusCode: 200, destination: { ID: '1zia9wKshXt80YksLmUdJnr7IHI', @@ -859,7 +1155,6 @@ export const data = [ transformAt: 'processor', transformAtV1: 'processor', }, - ResponseRules: null, }, Config: { apiKey: '12345', @@ -870,9 +1165,13 @@ export const data = [ trackNamedPages: true, }, Enabled: true, + WorkspaceID: 'default-workspace', Transformations: [], + RevisionID: 'default-revision', IsProcessorEnabled: true, + IsConnectionEnabled: true, }, + batched: true, }, ], }, @@ -880,8 +1179,11 @@ export const data = [ }, }, { + id: 'router-1736135082962', name: 'iterable', description: 'Simple identify call with EUDC dataCenter', + scenario: 'Default router scenario', + successCriteria: 'Router test should pass successfully', feature: 'router', module: 'destination', version: 'v0', @@ -895,7 +1197,10 @@ export const data = [ sentAt: '2022-09-27T11:13:03.777Z', messageId: '9ad41366-8060-4c9f-b181-f6bea67d5469', originalTimestamp: '2022-09-27T11:13:03.777Z', - traits: { ruchira: 'donaldbaker@ellis.com', new_field2: 'GB' }, + traits: { + ruchira: 'donaldbaker@ellis.com', + new_field2: 'GB', + }, channel: 'sources', rudderId: '3d51640c-ab09-42c1-b7b2-db6ab433b35e', context: { @@ -909,7 +1214,11 @@ export const data = [ }, mappedToDestination: 'true', externalId: [ - { id: 'Tiffany', type: 'ITERABLE-test-ruchira', identifierType: 'itemId' }, + { + id: 'Tiffany', + type: 'ITERABLE-test-ruchira', + identifierType: 'itemId', + }, ], }, timestamp: '2022-09-27T11:12:59.079Z', @@ -918,14 +1227,27 @@ export const data = [ recordId: '10', request_ip: '10.1.86.248', }, - metadata: { jobId: 2, userId: 'u1' }, + metadata: generateMetadata(1), destination: { + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, Config: { apiKey: '583af2f8-15ba-49c0-8511-76383e7de07e', dataCenter: 'EUDC', hubID: '22066036', }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, }, { @@ -934,7 +1256,10 @@ export const data = [ sentAt: '2022-09-27T11:13:03.777Z', messageId: '9ad41366-8060-4c9f-b181-f6bea67d5469', originalTimestamp: '2022-09-27T11:13:03.777Z', - traits: { ruchira: 'abc@ellis.com', new_field2: 'GB1' }, + traits: { + ruchira: 'abc@ellis.com', + new_field2: 'GB1', + }, channel: 'sources', rudderId: '3d51640c-ab09-42c1-b7b2-db6ab433b35e', context: { @@ -948,7 +1273,11 @@ export const data = [ }, mappedToDestination: 'true', externalId: [ - { id: 'ABC', type: 'ITERABLE-test-ruchira', identifierType: 'itemId' }, + { + id: 'ABC', + type: 'ITERABLE-test-ruchira', + identifierType: 'itemId', + }, ], }, timestamp: '2022-09-27T11:12:59.079Z', @@ -957,19 +1286,33 @@ export const data = [ recordId: '10', request_ip: '10.1.86.248', }, - metadata: { jobId: 2, userId: 'u1' }, + metadata: generateMetadata(2), destination: { + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, Config: { apiKey: '583af2f8-15ba-49c0-8511-76383e7de07e', dataCenter: 'EUDC', hubID: '22066036', }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, }, ], destType: 'iterable', }, + method: 'POST', }, }, output: { @@ -991,8 +1334,14 @@ export const data = [ body: { JSON: { documents: { - Tiffany: { ruchira: 'donaldbaker@ellis.com', new_field2: 'GB' }, - ABC: { ruchira: 'abc@ellis.com', new_field2: 'GB1' }, + Tiffany: { + ruchira: 'donaldbaker@ellis.com', + new_field2: 'GB', + }, + ABC: { + ruchira: 'abc@ellis.com', + new_field2: 'GB1', + }, }, replaceUploadedFieldsOnly: true, }, @@ -1002,20 +1351,30 @@ export const data = [ }, files: {}, }, - metadata: [ - { jobId: 2, userId: 'u1' }, - { jobId: 2, userId: 'u1' }, - ], - batched: true, + metadata: [generateMetadata(1), generateMetadata(2)], statusCode: 200, destination: { + ID: 'default-destination-id', + Name: 'Default Destination', + DestinationDefinition: { + ID: 'default-dest-def-id', + Name: 'Default Destination Definition', + DisplayName: 'Default Display Name', + Config: {}, + }, Config: { apiKey: '583af2f8-15ba-49c0-8511-76383e7de07e', dataCenter: 'EUDC', hubID: '22066036', }, Enabled: true, + WorkspaceID: 'default-workspace', + Transformations: [], + RevisionID: 'default-revision', + IsProcessorEnabled: true, + IsConnectionEnabled: true, }, + batched: true, }, ], }, From 4cc52d21bcd2109e0d44efab78919b15b5574123 Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Mon, 6 Jan 2025 10:30:20 +0530 Subject: [PATCH 21/21] fix: review comment addressed --- src/v1/destinations/iterable/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v1/destinations/iterable/types.ts b/src/v1/destinations/iterable/types.ts index ff495271acf..f0875494ed3 100644 --- a/src/v1/destinations/iterable/types.ts +++ b/src/v1/destinations/iterable/types.ts @@ -39,7 +39,7 @@ type IterableBulkRequestBody = { export type IterableBulkProxyInput = { destinationResponse: IterableBulkApiResponse; - rudderJobMetadata: ProxyMetdata; + rudderJobMetadata: ProxyMetdata[]; destType: string; destinationRequest?: { body: {