diff --git a/src/util/errorNotifier/bugsnag.js b/src/util/errorNotifier/bugsnag.js index 09b2cd9f71..a88432f23d 100644 --- a/src/util/errorNotifier/bugsnag.js +++ b/src/util/errorNotifier/bugsnag.js @@ -23,6 +23,7 @@ const { UnhandledStatusCodeError, UnauthorizedError, NetworkInstrumentationError, + FilteredEventsError, } = require('../../v0/util/errorTypes'); const { @@ -48,6 +49,7 @@ const errorTypesDenyList = [ NetworkInstrumentationError, CDKCustomError, DataValidationError, + FilteredEventsError, ]; const pathsDenyList = [ diff --git a/src/v0/destinations/braze/transform.js b/src/v0/destinations/braze/transform.js index db71cf5d7a..d9c8ee785d 100644 --- a/src/v0/destinations/braze/transform.js +++ b/src/v0/destinations/braze/transform.js @@ -23,7 +23,11 @@ const { simpleProcessRouterDestSync, simpleProcessRouterDest, } = require('../../util'); -const { InstrumentationError, NetworkError, FilteredEventsError } = require('../../util/errorTypes'); +const { + InstrumentationError, + NetworkError, + FilteredEventsError, +} = require('../../util/errorTypes'); const { ConfigCategory, mappingConfig, @@ -80,7 +84,7 @@ function getIdentifyPayload(message) { let payload = {}; payload = setAliasObjectWithAnonId(payload, message); payload = setExternalId(payload, message); - return { aliases_to_identify: [payload], merge_behavior: "merge" }; + return { aliases_to_identify: [payload], merge_behavior: 'merge' }; } function populateCustomAttributesWithOperation( @@ -239,7 +243,6 @@ function processTrackWithUserAttributes(message, destination, mappingJson, proce } else { throw new FilteredEventsError( '[Braze Deduplication]: Duplicate user detected, the user is dropped', - 'filtered' ); } } diff --git a/src/v0/destinations/braze/util.js b/src/v0/destinations/braze/util.js index d772503b58..1149b4cfc3 100644 --- a/src/v0/destinations/braze/util.js +++ b/src/v0/destinations/braze/util.js @@ -363,11 +363,14 @@ const processBatch = (transformedEvents) => { const purchaseArray = []; const successMetadata = []; const failureResponses = []; + const filteredResponses = []; const subscriptionsArray = []; const mergeUsersArray = []; for (const transformedEvent of transformedEvents) { if (!isHttpStatusSuccess(transformedEvent?.statusCode)) { failureResponses.push(transformedEvent); + } else if (transformedEvent?.statusCode === 298) { + filteredResponses.push(transformedEvent); } else if (transformedEvent?.batchedRequest?.body?.JSON) { const { attributes, events, purchases, subscription_groups, merge_updates } = transformedEvent.batchedRequest.body.JSON; @@ -446,6 +449,10 @@ const processBatch = (transformedEvents) => { finalResponse.push(...failureResponses); } + if (filteredResponses.length > 0) { + finalResponse.push(...filteredResponses); + } + return finalResponse; }; diff --git a/src/v0/destinations/klaviyo/transform.js b/src/v0/destinations/klaviyo/transform.js index 0b1a4353ca..dc8b24c9d6 100644 --- a/src/v0/destinations/klaviyo/transform.js +++ b/src/v0/destinations/klaviyo/transform.js @@ -18,7 +18,6 @@ const { populateCustomFieldsFromTraits, batchSubscribeEvents, getIdFromNewOrExistingProfile, - profileUpdateResponseBuilder, } = require('./util'); const { defaultRequestConfig, @@ -106,10 +105,10 @@ const identifyRequestHandler = async (message, category, destination) => { }, }; - const profileId = await getIdFromNewOrExistingProfile(endpoint, payload, requestOptions); + const response = await getIdFromNewOrExistingProfile(endpoint, payload, requestOptions); // Update Profile - const responseArray = [profileUpdateResponseBuilder(payload, profileId, category, privateApiKey)]; + const responseArray = [{ error: response }]; // check if user wants to subscribe profile or not and listId is present or not if ( @@ -336,10 +335,10 @@ const processRouterDest = async (inputs, reqMetadata) => { batchedSubscribeResponseList.push(...batchedResponseList); } const nonSubscribeSuccessList = nonSubscribeRespList.map((resp) => - resp.message.body.JSON?.action + resp.message?.error ? { - ...getSuppressRespEvents(resp.message, [resp.metadata], resp.destination), - action: resp.message.body.JSON.action, + ...getSuppressRespEvents({}, [resp.metadata], resp.destination), + error: resp.message.error, } : getSuccessRespEvents(resp.message, [resp.metadata], resp.destination), ); diff --git a/src/v0/destinations/klaviyo/util.js b/src/v0/destinations/klaviyo/util.js index be59384699..3a442314ca 100644 --- a/src/v0/destinations/klaviyo/util.js +++ b/src/v0/destinations/klaviyo/util.js @@ -10,7 +10,6 @@ const { removeUndefinedAndNullValues, defaultBatchRequestConfig, getSuccessRespEvents, - defaultPatchRequestConfig, } = require('../../util'); const { BASE_ENDPOINT, MAPPING_CONFIG, CONFIG_CATEGORIES, MAX_BATCH_SIZE } = require('./config'); @@ -33,7 +32,7 @@ const { client: errNotificationClient } = require('../../../util/errorNotifier') * @returns */ const getIdFromNewOrExistingProfile = async (endpoint, payload, requestOptions) => { - let profileId; + let response; const endpointPath = '/api/profiles'; const { processedResponse: resp } = await handleHttpRequest( 'post', @@ -46,15 +45,17 @@ const getIdFromNewOrExistingProfile = async (endpoint, payload, requestOptions) endpointPath, }, ); + if (resp.status === 201) { - profileId = resp.response?.data?.id; + const { data } = resp.response; + response = { id: data.id, attributes: data.attributes }; } else if (resp.status === 409) { const { errors } = resp.response; - profileId = errors?.[0]?.meta?.duplicate_profile_id; + response = errors; } - if (profileId) { - return profileId; + if (response) { + return response; } let statusCode = resp.status; @@ -78,22 +79,6 @@ const getIdFromNewOrExistingProfile = async (endpoint, payload, requestOptions) ); }; -const profileUpdateResponseBuilder = (payload, profileId, category, privateApiKey) => { - const updatedPayload = payload; - const identifyResponse = defaultRequestConfig(); - updatedPayload.data.id = profileId; - identifyResponse.endpoint = `${BASE_ENDPOINT}${category.apiUrl}/${profileId}`; - identifyResponse.method = defaultPatchRequestConfig.requestMethod; - identifyResponse.headers = { - Authorization: `Klaviyo-API-Key ${privateApiKey}`, - 'Content-Type': JSON_MIME_TYPE, - Accept: JSON_MIME_TYPE, - revision: '2023-02-22', - }; - identifyResponse.body.JSON = removeUndefinedAndNullValues({ ...payload, action: 'suppress' }); - return identifyResponse; -}; - /** * This function is used for creating response for subscribing users to a particular list. * DOCS: https://developers.klaviyo.com/en/v2023-02-22/reference/subscribe_profiles @@ -284,5 +269,4 @@ module.exports = { generateBatchedPaylaodForArray, batchSubscribeEvents, getIdFromNewOrExistingProfile, - profileUpdateResponseBuilder, }; diff --git a/src/v0/util/errorTypes/filteredEventsError.js b/src/v0/util/errorTypes/filteredEventsError.js index d4791f5ba3..2bcfa26c3b 100644 --- a/src/v0/util/errorTypes/filteredEventsError.js +++ b/src/v0/util/errorTypes/filteredEventsError.js @@ -2,14 +2,13 @@ const tags = require('../tags'); const { BaseError } = require('./base'); class FilteredEventsError extends BaseError { - constructor(message, action = null, statusCode=298) { - const finalStatTags = { - [tags.TAG_NAMES.ERROR_CATEGORY]: tags.ERROR_CATEGORIES.TRANSFORMATION, - [tags.TAG_NAMES.ERROR_TYPE]: tags.ERROR_TYPES.FILTERED, - }; - super(message, statusCode, finalStatTags); - this.action = action - } + constructor(message, statusCode = 298) { + const finalStatTags = { + [tags.TAG_NAMES.ERROR_CATEGORY]: tags.ERROR_CATEGORIES.TRANSFORMATION, + [tags.TAG_NAMES.ERROR_TYPE]: tags.ERROR_TYPES.FILTERED, + }; + super(message, statusCode, finalStatTags); + } } -module.exports = FilteredEventsError; \ No newline at end of file +module.exports = FilteredEventsError; diff --git a/test/__mocks__/klaviyo.mock.js b/test/__mocks__/klaviyo.mock.js index f768af0fb3..020387d1f2 100644 --- a/test/__mocks__/klaviyo.mock.js +++ b/test/__mocks__/klaviyo.mock.js @@ -19,6 +19,7 @@ const klaviyoPostRequestHandler = (url, payload) => { data: { data: { id: '01GW3PHVY0MTCDGS0A1612HARX', + attributes: {} }, } }; diff --git a/test/__tests__/data/klaviyo.json b/test/__tests__/data/klaviyo.json index f8b05e1800..fe149f5ed1 100644 --- a/test/__tests__/data/klaviyo.json +++ b/test/__tests__/data/klaviyo.json @@ -351,49 +351,10 @@ } }, "output": { - "version": "1", - "type": "REST", - "method": "PATCH", - "endpoint": "https://a.klaviyo.com/api/profiles/01GW3PHVY0MTCDGS0A1612HARX", - "headers": { - "Accept": "application/json", - "Authorization": "Klaviyo-API-Key pk_b68c7b5163d98807fcb57e6f921216629d", - "Content-Type": "application/json", - "revision": "2023-02-22" - }, - "params": {}, - "body": { - "JSON": { - "action": "suppress", - "data": { - "type": "profile", - "attributes": { - "external_id": "user@1", - "email": "test@rudderstack.com", - "first_name": "Test", - "last_name": "Rudderlabs", - "phone_number": "+12 345 578 900", - "title": "Developer", - "organization": "Rudder", - "location": { - "city": "Tokyo", - "region": "Kanto", - "country": "JP", - "zip": "100-0001" - }, - "properties": { - "Flagged": false, - "Residence": "Shibuya" - } - }, - "id": "01GW3PHVY0MTCDGS0A1612HARX" - } - }, - "JSON_ARRAY": {}, - "XML": {}, - "FORM": {} - }, - "files": {} + "error": { + "attributes": {}, + "id": "01GW3PHVY0MTCDGS0A1612HARX" + } } }, { diff --git a/test/__tests__/data/klaviyo_router_output.json b/test/__tests__/data/klaviyo_router_output.json index 6a87085bfd..0efcb17016 100644 --- a/test/__tests__/data/klaviyo_router_output.json +++ b/test/__tests__/data/klaviyo_router_output.json @@ -61,59 +61,18 @@ } }, { - "batchedRequest": { - "version": "1", - "type": "REST", - "method": "PATCH", - "endpoint": "https://a.klaviyo.com/api/profiles/01GW3PHVY0MTCDGS0A1612HARX", - "headers": { - "Accept": "application/json", - "Authorization": "Klaviyo-API-Key pk_b68c7b5163d98807fcb57e6f921216629d", - "Content-Type": "application/json", - "revision": "2023-02-22" - }, - "params": {}, - "body": { - "JSON": { - "action": "suppress", - "data": { - "type": "profile", - "attributes": { - "external_id": "test", - "email": "test@rudderstack.com", - "first_name": "Test", - "last_name": "Rudderlabs", - "phone_number": "+12 345 578 900", - "title": "Developer", - "organization": "Rudder", - "location": { - "city": "Tokyo", - "region": "Kanto", - "country": "JP", - "zip": "100-0001" - }, - "properties": { - "Flagged": false, - "Residence": "Shibuya" - } - }, - "id": "01GW3PHVY0MTCDGS0A1612HARX" - } - }, - "JSON_ARRAY": {}, - "XML": {}, - "FORM": {} - }, - "files": {} - }, + "batchedRequest": {}, "metadata": [ { "jobId": 1 } ], "batched": false, - "action": "suppress", - "statusCode": 200, + "error": { + "attributes": {}, + "id": "01GW3PHVY0MTCDGS0A1612HARX" + }, + "statusCode": 299, "destination": { "Config": { "publicApiKey": "WJqij9",