From 0610d38ebe17c872c12b7c9fe1cec35ef5a6e557 Mon Sep 17 00:00:00 2001 From: shrouti1507 Date: Thu, 2 May 2024 12:49:31 +0530 Subject: [PATCH] fix: track batch handled --- src/cdk/v2/destinations/emarsys/utils.js | 29 ++++++- src/cdk/v2/destinations/emarsys/utils.test.js | 81 ++++++++++++++++++- src/constants/destinationCanonicalNames.js | 2 +- src/v1/destinations/emarsys/networkHandler.js | 80 +++++------------- .../{emersys => emarsys}/router/data.ts | 22 ++--- 5 files changed, 137 insertions(+), 77 deletions(-) rename test/integrations/destinations/{emersys => emarsys}/router/data.ts (96%) diff --git a/src/cdk/v2/destinations/emarsys/utils.js b/src/cdk/v2/destinations/emarsys/utils.js index 85404cdf0b..1e7363e6ef 100644 --- a/src/cdk/v2/destinations/emarsys/utils.js +++ b/src/cdk/v2/destinations/emarsys/utils.js @@ -294,17 +294,23 @@ const createGroupBatches = (events) => { key_id: keyId, external_ids: chunk, }, - metadata: group.map((g) => g.metadata), // assuming metadata is needed per original event grouping + metadata: group.map((g) => g.metadata), })); }); }; + +const createTrackBatches = (events) => ({ + endpoint: events[0].message.endPoint, + payload: events[0].message.body.JSON.destinationPayload, + metadata: events[0].metadata, +}); const formatIdentifyPayloadsWithEndpoint = (combinedPayloads, endpointUrl = '') => combinedPayloads.map((payload) => ({ endpoint: endpointUrl, payload, })); -const buildBatchedRequest = (batches, method, constants) => +const buildBatchedRequest = (batches, method, constants, batchedStatus = true) => batches.map((batch) => ({ batchedRequest: { body: { @@ -322,7 +328,7 @@ const buildBatchedRequest = (batches, method, constants) => files: {}, }, metadata: batch.metadata, - batched: true, + batched: batchedStatus, statusCode: 200, destination: constants.destination, })); @@ -338,6 +344,10 @@ const batchResponseBuilder = (successfulEvents) => { method: 'POST', batches: [], }, + track: { + method: 'POST', + batches: [], + }, }; let batchesOfIdentifyEvents; if (successfulEvents.length === 0) { @@ -366,6 +376,9 @@ const batchResponseBuilder = (successfulEvents) => { case EVENT_TYPE.GROUP: groupedSuccessfulPayload.group.batches = createGroupBatches(eachEventGroup); break; + case EVENT_TYPE.TRACK: + groupedSuccessfulPayload.track.batches = createTrackBatches(eachEventGroup); + break; default: break; } @@ -391,6 +404,16 @@ const batchResponseBuilder = (successfulEvents) => { finaloutput.push(...groupBatches); } + if (groupedSuccessfulPayload.track) { + const trackBatches = buildBatchedRequest( + groupedSuccessfulPayload.track.batches, + groupedSuccessfulPayload.track.method, + constants, + false, + ); + finaloutput.push(...trackBatches); + } + return finaloutput; }; module.exports = { diff --git a/src/cdk/v2/destinations/emarsys/utils.test.js b/src/cdk/v2/destinations/emarsys/utils.test.js index a113ced60d..d9ce90097e 100644 --- a/src/cdk/v2/destinations/emarsys/utils.test.js +++ b/src/cdk/v2/destinations/emarsys/utils.test.js @@ -1,4 +1,3 @@ -const lodash = require('lodash'); const { EVENT_TYPE } = require('rudder-transformer-cdk/build/constants'); const { buildIdentifyPayload, @@ -8,7 +7,9 @@ const { findRudderPropertyByEmersysProperty, createGroupBatches, } = require('./utils'); -const utils = require('./utils'); +const { + checkIfEventIsAbortableAndExtractErrorMessage, +} = require('../../../../v1/destinations/emarsys/networkHandler'); const crypto = require('crypto'); const { InstrumentationError } = require('@rudderstack/integrations-lib'); @@ -409,3 +410,79 @@ describe('findRudderPropertyByEmersysProperty', () => { expect(result).toBeNull(); }); }); + +describe('checkIfEventIsAbortableAndExtractErrorMessage', () => { + // Returns {isAbortable: false, errorMsg: ''} if event is neither a string nor an object with keyId. + it('should return {isAbortable: false, errorMsg: ""} when event is neither a string nor an object with keyId', () => { + const event = 123; + const destinationResponse = { + data: { + errors: { + errorKey: { + errorCode: 'errorMessage', + }, + }, + }, + }; + const keyId = 'keyId'; + + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse, keyId); + + expect(result).toEqual({ isAbortable: false, errorMsg: '' }); + }); + + // Returns {isAbortable: false, errorMsg: ''} if errors object is empty. + it('should return {isAbortable: false, errorMsg: ""} when errors object is empty', () => { + const event = 'event'; + const destinationResponse = { + data: { + errors: {}, + }, + }; + const keyId = 'keyId'; + + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse, keyId); + + expect(result).toEqual({ isAbortable: false, errorMsg: '' }); + }); + + // Returns {isAbortable: true, errorMsg} if event is a string and has a corresponding error in the errors object. + it('should return {isAbortable: true, errorMsg} when event is a string and has a corresponding error in the errors object', () => { + const event = 'event'; + const destinationResponse = { + data: { + errors: { + event: { + errorCode: 'errorMessage', + }, + }, + }, + }; + const keyId = 'keyId'; + + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse, keyId); + + expect(result).toEqual({ isAbortable: true, errorMsg: '{"errorCode":"errorMessage"}' }); + }); + + // Returns {isAbortable: true, errorMsg} if event is an object with keyId and has a corresponding error in the errors object. + it('should return {isAbortable: true, errorMsg} when event is an object with keyId and has a corresponding error in the errors object', () => { + const event = { + keyId: 'event', + }; + const destinationResponse = { + data: { + errors: { + event: { + errorCode: 'errorMessage', + }, + }, + }, + }; + const keyId = 'keyId'; + + const result = checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse, keyId); + + expect(result).toEqual({ isAbortable: true, errorMsg: '{"errorCode":"errorMessage"}' }); + }); +}); diff --git a/src/constants/destinationCanonicalNames.js b/src/constants/destinationCanonicalNames.js index 3cc05e70e3..b620146230 100644 --- a/src/constants/destinationCanonicalNames.js +++ b/src/constants/destinationCanonicalNames.js @@ -166,7 +166,7 @@ const DestCanonicalNames = { ], koala: ['Koala', 'koala', 'KOALA'], bloomreach: ['Bloomreach', 'bloomreach', 'BLOOMREACH'], - emersys: ['EMERSYS', 'Emersys', 'emersys'], + emersys: ['EMARSYS', 'Emarsys', 'emarsys'], }; module.exports = { DestHandlerMap, DestCanonicalNames }; diff --git a/src/v1/destinations/emarsys/networkHandler.js b/src/v1/destinations/emarsys/networkHandler.js index f61b48de59..641c89c0de 100644 --- a/src/v1/destinations/emarsys/networkHandler.js +++ b/src/v1/destinations/emarsys/networkHandler.js @@ -9,45 +9,28 @@ const { } = require('../../../adapters/utils/networkUtils'); const tags = require('../../../v0/util/tags'); -function checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse) { - // Extract the errors from the destination response +function checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse, keyId) { const { errors } = destinationResponse.data; - const { key_id } = event; // Assuming the 'key_id' is constant as before - // Find the first abortable case - const result = event.find((item) => { - if (typeof item === 'string') { - return errors[item]; // Check if the string is a key in errors - } - if (typeof item === 'object' && item[key_id]) { - return errors[item[key_id]]; // Check if the object's value under key_id is a key in errors - } - return false; // Continue if no condition is met - }); + // Determine if event is a string or an object, then fetch the corresponding key or value + let errorKey; + if (typeof event === 'string') { + errorKey = event; + } else if (typeof event === 'object' && event[keyId]) { + errorKey = event[keyId]; + } else { + return { isAbortable: false, errorMsg: '' }; // Early return if neither condition is met or if keyId is missing in the object + } - if (result) { - if (typeof result === 'string') { - // Handle case where result is a string key found in errors - return { - isAbortable: true, - errorMsg: errors[result][Object.keys(errors[result])[0]], - }; - } - if (typeof result === 'object') { - // Handle case where result is an object found in errors - const keyValue = result[key_id]; - return { - isAbortable: true, - errorMsg: errors[keyValue][Object.keys(errors[keyValue])[0]], - }; - } + // Check if this key has a corresponding error in the errors object + if (errors[errorKey]) { + // const errorCode = Object.keys(errors[errorKey])[0]; // Assume there is at least one error code + const errorMsg = JSON.stringify(errors[errorKey]); + return { isAbortable: true, errorMsg }; } - // If no match or abortable condition is found - return { - isAbortable: false, - errorMsg: '', - }; + // Return false and an empty error message if no error is found + return { isAbortable: false, errorMsg: '' }; } const responseHandler = (responseParams) => { @@ -64,30 +47,6 @@ const responseHandler = (responseParams) => { metadata, error: errorMessage, })); - // if the status is 422, we need to parse the error message and construct the response array - // if (status === 422) { - // const destPartialStatus = constructPartialStatus(response?.message); - // // if the error message is not in the expected format, we will abort all of the events - // if (!destPartialStatus || lodash.isEmpty(destPartialStatus)) { - // throw new TransformerProxyError( - // `EMARSYS: Error transformer proxy v1 during EMARSYS response transformation. Error parsing error message`, - // status, - // { - // [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), - // }, - // destinationResponse, - // getAuthErrCategoryFromStCode(status), - // responseWithIndividualEvents, - // ); - // } - // responseWithIndividualEvents = [...createResponseArray(rudderJobMetadata, destPartialStatus)]; - // return { - // status, - // message, - // destinationResponse, - // response: responseWithIndividualEvents, - // }; - // } throw new TransformerProxyError( `EMARSYS: Error transformer proxy v1 during EMARSYS response transformation. ${errorMessage}`, status, @@ -103,7 +62,7 @@ const responseHandler = (responseParams) => { if (isHttpStatusSuccess(status)) { // check for Partial Event failures and Successes // eslint-disable-next-line @typescript-eslint/naming-convention - const { contacts, external_ids } = destinationRequest.body.JSON; + const { contacts, external_ids, key_id } = destinationRequest.body.JSON; const finalData = contacts || external_ids; finalData.forEach((event, idx) => { const proxyOutput = { @@ -115,6 +74,7 @@ const responseHandler = (responseParams) => { const { isAbortable, errorMsg } = checkIfEventIsAbortableAndExtractErrorMessage( event, destinationResponse, + key_id, ); if (isAbortable) { proxyOutput.statusCode = 400; @@ -152,4 +112,4 @@ function networkHandler() { this.responseHandler = responseHandler; } -module.exports = { networkHandler }; +module.exports = { networkHandler, checkIfEventIsAbortableAndExtractErrorMessage }; diff --git a/test/integrations/destinations/emersys/router/data.ts b/test/integrations/destinations/emarsys/router/data.ts similarity index 96% rename from test/integrations/destinations/emersys/router/data.ts rename to test/integrations/destinations/emarsys/router/data.ts index e3e2526f38..f98058474f 100644 --- a/test/integrations/destinations/emersys/router/data.ts +++ b/test/integrations/destinations/emarsys/router/data.ts @@ -28,8 +28,8 @@ const commonDestination = { Name: 'sample-destination', DestinationDefinition: { ID: '123', - Name: 'emersys', - DisplayName: 'Emersys', + Name: 'emarsys', + DisplayName: 'Emarsys', Config: { cdkV2Enabled: true, }, @@ -42,8 +42,8 @@ const commonDestination = { export const data = [ { - id: 'emersys-track-test-1', - name: 'emersys', + id: 'emarsys-track-test-1', + name: 'emarsys', description: 'Track call : custom event calls with simple user properties and traits', scenario: 'Business', successCriteria: @@ -202,7 +202,7 @@ export const data = [ destination: commonDestination, }, ], - destType: 'emersys', + destType: 'emarsys', }, method: 'POST', }, @@ -229,8 +229,8 @@ export const data = [ Name: 'sample-destination', DestinationDefinition: { ID: '123', - Name: 'emersys', - DisplayName: 'emersys', + Name: 'emarsys', + DisplayName: 'emarsys', Config: { cdkV2Enabled: true, }, @@ -243,9 +243,9 @@ export const data = [ batched: false, statusCode: 400, error: - '[emersys Conversion API] no matching user id found. Please provide at least one of the following: email, emersysFirstPartyAdsTrackingUUID, acxiomId, oracleMoatId', + '[emarsys Conversion API] no matching user id found. Please provide at least one of the following: email, emersysFirstPartyAdsTrackingUUID, acxiomId, oracleMoatId', statTags: { - destType: 'emersys', + destType: 'emarsys', errorCategory: 'dataValidation', errorType: 'instrumentation', feature: 'router', @@ -355,12 +355,12 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.emersys.com/rest/conversionEvents', + endpoint: 'https://api.emarsys.com/rest/conversionEvents', headers: { 'Content-Type': 'application/json', 'X-RestLi-Method': 'BATCH_CREATE', 'X-Restli-Protocol-Version': '2.0.0', - 'emersys-Version': '202402', + 'emarsys-Version': '202402', Authorization: 'Bearer dummyToken', }, params: {},