Skip to content

Commit

Permalink
fix: snapchat conversion introduce missing fields (#2704)
Browse files Browse the repository at this point in the history
* fix: snapchat conversion introduce missing fields

* chore: zip field and test case added

* brands test case added

* fix: complexity issue

* fix: complexity issue 1

* fix: complexity issue 2

* fix: complexity issue 3

* Update src/v0/destinations/snapchat_conversion/transform.js

Co-authored-by: Sankeerth <sankeerth@rudderstack.com>

* fix: complexity issue 3

* chore: comment addressed

---------

Co-authored-by: Sankeerth <sankeerth@rudderstack.com>
  • Loading branch information
anantjain45823 and sanpj2292 committed Oct 11, 2023
1 parent f49244d commit d6488d5
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 58 deletions.
174 changes: 125 additions & 49 deletions src/v0/destinations/snapchat_conversion/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,43 @@ function buildResponse(apiKey, payload) {
response.body.JSON = removeUndefinedAndNullValues(payload);
return response;
}
const populateHashedTraitsValues = (payload, message) => {
const firstName = getFieldValueFromMessage(message, 'firstName');
const lastName = getFieldValueFromMessage(message, 'lastName');
const middleName = getFieldValueFromMessage(message, 'middleName');
const city = getFieldValueFromMessage(message, 'city');
const state = getFieldValueFromMessage(message, 'state');
const zip = getFieldValueFromMessage(message, 'zipcode');
const updatedPayload = {
...payload,
hashed_first_name_sha: firstName
? getHashedValue(firstName.toString().toLowerCase().trim())
: undefined,
hashed_middle_name_sha: middleName
? getHashedValue(middleName.toString().toLowerCase().trim())
: undefined,
hashed_last_name_sha: lastName
? getHashedValue(lastName.toString().toLowerCase().trim())
: undefined,
hashed_city_sha: city ? getHashedValue(city.toString().toLowerCase().trim()) : undefined,
hashed_zip: zip ? getHashedValue(zip.toString().toLowerCase().trim()) : undefined,
hashed_state_sha: state ? getHashedValue(state.toString().toLowerCase().trim()) : undefined,
};
return updatedPayload;
};

/**
* Seperate out hashing operations into one function
* @param {*} payload
* @param {*} message
* @returns updatedPayload
*/
function populateHashedValues(payload, message) {
const updatedPayload = payload;
const populateHashedValues = (payload, message) => {
const email = getFieldValueFromMessage(message, 'email');
const phone = getNormalizedPhoneNumber(message);
const ip = message.context?.ip || message.request_ip;

const updatedPayload = populateHashedTraitsValues(payload, message);
if (email) {
updatedPayload.hashed_email = getHashedValue(email.toString().toLowerCase().trim());
}
Expand All @@ -85,14 +109,82 @@ function populateHashedValues(payload, message) {
);
}
return updatedPayload;
}
};
const getEventProperties = (message) => ({
description: get(message, 'properties.description'),
brands: Array.isArray(message.properties?.brands) ? get(message, 'properties.brands') : undefined,
customer_status: get(message, 'properties.customer_status'),
uuid_c1: get(message, 'properties.uuid_c1'),
level: get(message, 'properties.level'),
click_id: get(message, 'properties.click_id'),
event_tag: get(message, 'properties.event_tag'),
country: getFieldValueFromMessage(message, 'country'),
region: getFieldValueFromMessage(message, 'region'),
user_agent: message.context?.userAgent?.toString()?.toLowerCase(),
});
const validateEventConfiguration = (eventConversionType, pixelId, snapAppId, appId) => {
if ((eventConversionType === 'WEB' || eventConversionType === 'OFFLINE') && !pixelId) {
throw new ConfigurationError('Pixel Id is required for web and offline events');
}

// Returns the response for the track event after constructing the payload and setting necessary fields
function trackResponseBuilder(message, { Config }, mappedEvent) {
let payload = {};
const event = mappedEvent.trim().replace(/\s+/g, '_');
if (eventConversionType === 'MOBILE_APP' && !(appId && snapAppId)) {
let requiredId = 'App Id';
if (!snapAppId) {
requiredId = 'Snap App Id';
}
throw new ConfigurationError(`${requiredId} is required for app events`);
}
};
const validateRequiredFields = (payload) => {
if (
!payload.hashed_email &&
!payload.hashed_phone_number &&
!payload.hashed_mobile_ad_id &&
!(payload.hashed_ip_address && payload.user_agent)
) {
throw new InstrumentationError(
'At least one of email or phone or advertisingId or ip and userAgent is required',
);
}
};
const addSpecificEventDetails = (
message,
payload,
eventConversionType,
pixelId,
snapAppId,
appId,
) => {
const updatedPayload = { ...payload };
if (eventConversionType === 'WEB') {
updatedPayload.pixel_id = pixelId;
updatedPayload.page_url = getFieldValueFromMessage(message, 'pageUrl');
}

const { apiKey, pixelId, snapAppId, appId, deduplicationKey, enableDeduplication } = Config;
if (eventConversionType === 'MOBILE_APP') {
updatedPayload.snap_app_id = snapAppId;
updatedPayload.app_id = appId;
}

if (eventConversionType === 'OFFLINE') {
updatedPayload.pixel_id = pixelId;
}
return updatedPayload;
};
const handleDeduplication = (payload, enableDeduplication, deduplicationKey, message) => {
if (enableDeduplication) {
const dedupId = deduplicationKey || 'messageId';
const clientDedupId = get(message, dedupId);
if (!clientDedupId) {
throw new InstrumentationError(
'Deduplication enabled but no deduplication key provided in the message',
);
}
return clientDedupId;
}
return undefined;
};
const getEventConversionType = (message) => {
const channel = get(message, 'channel');
let eventConversionType = message?.properties?.eventConversionType;
if (
Expand All @@ -105,18 +197,16 @@ function trackResponseBuilder(message, { Config }, mappedEvent) {
} else {
eventConversionType = 'OFFLINE';
}
return eventConversionType;
};

if ((eventConversionType === 'WEB' || eventConversionType === 'OFFLINE') && !pixelId) {
throw new ConfigurationError('Pixel Id is required for web and offline events');
}

if (eventConversionType === 'MOBILE_APP' && !(appId && snapAppId)) {
if (!appId) {
throw new ConfigurationError('App Id is required for app events');
} else {
throw new ConfigurationError('Snap App Id is required for app events');
}
}
// Returns the response for the track event after constructing the payload and setting necessary fields
const trackResponseBuilder = (message, { Config }, mappedEvent) => {
let payload = {};
const event = mappedEvent.trim().replace(/\s+/g, '_');
const eventConversionType = getEventConversionType(message);
const { apiKey, pixelId, snapAppId, appId, deduplicationKey, enableDeduplication } = Config;
validateEventConfiguration(eventConversionType, pixelId, snapAppId, appId);

if (eventNameMapping[event.toLowerCase()]) {
// Snapchat standard events
Expand Down Expand Up @@ -192,21 +282,9 @@ function trackResponseBuilder(message, { Config }, mappedEvent) {
throw new InstrumentationError(`Event ${event} doesn't match with Snapchat Events!`);
}

payload.event_tag = get(message, 'properties.event_tag');
payload = { ...payload, ...getEventProperties(message) };
payload = populateHashedValues(payload, message);

payload.user_agent = message.context?.userAgent?.toString().toLowerCase();

if (
!payload.hashed_email &&
!payload.hashed_phone_number &&
!payload.hashed_mobile_ad_id &&
!(payload.hashed_ip_address && payload.user_agent)
) {
throw new InstrumentationError(
'At least one of email or phone or advertisingId or ip and userAgent is required',
);
}
validateRequiredFields(payload);
payload.timestamp = getFieldValueFromMessage(message, 'timestamp');
const timeStamp = payload.timestamp;
if (timeStamp) {
Expand All @@ -224,17 +302,15 @@ function trackResponseBuilder(message, { Config }, mappedEvent) {
}

payload.event_conversion_type = eventConversionType;
if (eventConversionType === 'WEB') {
payload.pixel_id = pixelId;
payload.page_url = getFieldValueFromMessage(message, 'pageUrl');
}
if (eventConversionType === 'MOBILE_APP') {
payload.snap_app_id = snapAppId;
payload.app_id = appId;
}
if (eventConversionType === 'OFFLINE') {
payload.pixel_id = pixelId;
}
payload = addSpecificEventDetails(
message,
payload,
eventConversionType,
pixelId,
snapAppId,
appId,
);
payload.client_dedup_id = handleDeduplication(enableDeduplication, deduplicationKey, message);

// adding for deduplication for more than one source
if (enableDeduplication) {
Expand All @@ -246,10 +322,10 @@ function trackResponseBuilder(message, { Config }, mappedEvent) {
// build response
const response = buildResponse(apiKey, payload);
return response;
}
};

// Checks if there are any mapping events for the track event and returns them
function eventMappingHandler(message, destination) {
const eventMappingHandler = (message, destination) => {
let event = get(message, 'event');

if (!event) {
Expand All @@ -276,9 +352,9 @@ function eventMappingHandler(message, destination) {
}

return [...mappedEvents];
}
};

function process(event) {
const process = (event) => {
const { message, destination } = event;
// const message = { ...incomingMessage };
if (!message.type) {
Expand All @@ -304,7 +380,7 @@ function process(event) {
throw new InstrumentationError(`Event type ${messageType} is not supported`);
}
return response;
}
};

const processRouterDest = async (inputs, reqMetadata) => {
const errorRespEvents = checkInvalidRtTfEvents(inputs);
Expand Down
40 changes: 32 additions & 8 deletions test/__tests__/data/snapchat_conversion.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,12 @@
"context": {
"traits": {
"email": "test@email.com",
"phone": "+91 2111111 "
"phone": "+91 2111111 ",
"firstName": "john",
"middleName": "victor",
"lastName": "doe",
"city": "some_city",
"state": "some_state"
},
"app": {
"build": "1.0.0",
Expand Down Expand Up @@ -147,7 +152,9 @@
"properties": {
"query": "t-shirts",
"event_conversion_type": "web",
"number_items": 4
"number_items": 4,
"click_id": "some_click_id",
"description": "Products Searched event for conversion type offline"
},
"integrations": {
"All": true
Expand Down Expand Up @@ -183,7 +190,13 @@
"timestamp": "1650625078",
"event_conversion_type": "OFFLINE",
"pixel_id": "dummyPixelId",
"number_items": 4
"number_items": 4,
"click_id": "some_click_id",
"description": "Products Searched event for conversion type offline",
"hashed_first_name_sha": "96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a",
"hashed_last_name_sha": "799ef92a11af918e3fb741df42934f3b568ed2d93ac1df74f1b8d41a27932a6f",
"hashed_state_sha": "6db488fc98e30afdf67a05a6da916805b02891ce58f03970c6deff79129c5f1c",
"hashed_middle_name_sha": "99bde068af2d49ed7fc8b8fa79abe13a6059e0db320bb73459fd96624bb4b33f"
},
"JSON_ARRAY": {},
"XML": {},
Expand All @@ -202,7 +215,10 @@
"context": {
"traits": {
"email": "test@email.com",
"phone": "+91 2111111 "
"phone": "+91 2111111 ",
"country": "IN",
"zicode": "Sxp-12345",
"region": "some_region"
},
"app": {
"build": "1.0.0",
Expand Down Expand Up @@ -564,7 +580,7 @@
},
"output": {
"statusCode": 400,
"error": "App Id is required for app events"
"error": "Snap App Id is required for app events"
}
},
{
Expand Down Expand Up @@ -749,7 +765,10 @@
"context": {
"traits": {
"email": "test@email.com",
"phone": "+91 2111111 "
"phone": "+91 2111111 ",
"country": "IN",
"zipcode": "Sxp-12345",
"region": "some_region"
},
"app": {
"build": "1.0.0",
Expand Down Expand Up @@ -830,6 +849,9 @@
"event_type": "VIEW_CONTENT",
"item_ids": ["123", "123"],
"price": "56",
"country": "IN",
"hashed_zip": "cbb2704f5b334a0cec32e5463d1fd9355f6ef73987bfe0ebb8389b7617452152",
"region": "some_region",
"hashed_email": "73062d872926c2a556f17b36f50e328ddf9bff9d403939bd14b6c3b7f5a33fc2",
"hashed_phone_number": "bc77d64d7045fe44795ed926df37231a0cfb6ec6b74588c512790e9f143cc492",
"hashed_mobile_ad_id": "f9779d734aaee50f16ee0011260bae7048f1d9a128c62b6a661077875701edd2",
Expand Down Expand Up @@ -1007,14 +1029,15 @@
"type": "track",
"event": "Order Completed",
"properties": {
"brands": ["brand01", "brand02"],
"products": [
{
"product_id": "123",
"price": "14",
"quantity": 1
},
{
"product_id": "123",
"product_id": "124",
"price": 14,
"quantity": 3
}
Expand Down Expand Up @@ -1047,7 +1070,8 @@
"body": {
"JSON": {
"event_type": "PURCHASE",
"item_ids": ["123", "123"],
"item_ids": ["123", "124"],
"brands": ["brand01", "brand02"],
"price": "56",
"hashed_email": "73062d872926c2a556f17b36f50e328ddf9bff9d403939bd14b6c3b7f5a33fc2",
"hashed_phone_number": "bc77d64d7045fe44795ed926df37231a0cfb6ec6b74588c512790e9f143cc492",
Expand Down
2 changes: 1 addition & 1 deletion test/__tests__/data/snapchat_conversion_router_output.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"body": {
"JSON": {},
"JSON_ARRAY": {
"batch": "[{\"search_string\":\"t-shirts\",\"event_type\":\"SEARCH\",\"hashed_email\":\"73062d872926c2a556f17b36f50e328ddf9bff9d403939bd14b6c3b7f5a33fc2\",\"hashed_phone_number\":\"bc77d64d7045fe44795ed926df37231a0cfb6ec6b74588c512790e9f143cc492\",\"hashed_idfv\":\"54bd0b26a3d39dad90f5149db49b9fd9ba885f8e35d1d94cae69273f5e657b9f\",\"hashed_mobile_ad_id\":\"f9779d734aaee50f16ee0011260bae7048f1d9a128c62b6a661077875701edd2\",\"user_agent\":\"mozilla/5.0 (macintosh; intel mac os x 10_15_2) applewebkit/537.36 (khtml, like gecko) chrome/79.0.3945.88 safari/537.36\",\"timestamp\":\"1650625078\",\"event_conversion_type\":\"OFFLINE\",\"pixel_id\":\"dummyPixelId\"},{\"search_string\":\"t-shirts\",\"event_type\":\"SEARCH\",\"hashed_email\":\"73062d872926c2a556f17b36f50e328ddf9bff9d403939bd14b6c3b7f5a33fc2\",\"hashed_phone_number\":\"bc77d64d7045fe44795ed926df37231a0cfb6ec6b74588c512790e9f143cc492\",\"hashed_idfv\":\"54bd0b26a3d39dad90f5149db49b9fd9ba885f8e35d1d94cae69273f5e657b9f\",\"hashed_mobile_ad_id\":\"f9779d734aaee50f16ee0011260bae7048f1d9a128c62b6a661077875701edd2\",\"user_agent\":\"mozilla/5.0 (macintosh; intel mac os x 10_15_2) applewebkit/537.36 (khtml, like gecko) chrome/79.0.3945.88 safari/537.36\",\"timestamp\":\"1650625078\",\"event_conversion_type\":\"OFFLINE\",\"pixel_id\":\"dummyPixelId\"},{\"search_string\":\"t-shirts\",\"event_type\":\"SEARCH\",\"hashed_email\":\"73062d872926c2a556f17b36f50e328ddf9bff9d403939bd14b6c3b7f5a33fc2\",\"hashed_phone_number\":\"bc77d64d7045fe44795ed926df37231a0cfb6ec6b74588c512790e9f143cc492\",\"hashed_idfv\":\"54bd0b26a3d39dad90f5149db49b9fd9ba885f8e35d1d94cae69273f5e657b9f\",\"hashed_mobile_ad_id\":\"f9779d734aaee50f16ee0011260bae7048f1d9a128c62b6a661077875701edd2\",\"user_agent\":\"mozilla/5.0 (macintosh; intel mac os x 10_15_2) applewebkit/537.36 (khtml, like gecko) chrome/79.0.3945.88 safari/537.36\",\"timestamp\":\"1650625078\",\"event_conversion_type\":\"OFFLINE\",\"pixel_id\":\"dummyPixelId\"}]"
"batch": "[{\"search_string\":\"t-shirts\",\"event_type\":\"SEARCH\",\"user_agent\":\"mozilla/5.0 (macintosh; intel mac os x 10_15_2) applewebkit/537.36 (khtml, like gecko) chrome/79.0.3945.88 safari/537.36\",\"hashed_email\":\"73062d872926c2a556f17b36f50e328ddf9bff9d403939bd14b6c3b7f5a33fc2\",\"hashed_phone_number\":\"bc77d64d7045fe44795ed926df37231a0cfb6ec6b74588c512790e9f143cc492\",\"hashed_idfv\":\"54bd0b26a3d39dad90f5149db49b9fd9ba885f8e35d1d94cae69273f5e657b9f\",\"hashed_mobile_ad_id\":\"f9779d734aaee50f16ee0011260bae7048f1d9a128c62b6a661077875701edd2\",\"timestamp\":\"1650625078\",\"event_conversion_type\":\"OFFLINE\",\"pixel_id\":\"dummyPixelId\"},{\"search_string\":\"t-shirts\",\"event_type\":\"SEARCH\",\"user_agent\":\"mozilla/5.0 (macintosh; intel mac os x 10_15_2) applewebkit/537.36 (khtml, like gecko) chrome/79.0.3945.88 safari/537.36\",\"hashed_email\":\"73062d872926c2a556f17b36f50e328ddf9bff9d403939bd14b6c3b7f5a33fc2\",\"hashed_phone_number\":\"bc77d64d7045fe44795ed926df37231a0cfb6ec6b74588c512790e9f143cc492\",\"hashed_idfv\":\"54bd0b26a3d39dad90f5149db49b9fd9ba885f8e35d1d94cae69273f5e657b9f\",\"hashed_mobile_ad_id\":\"f9779d734aaee50f16ee0011260bae7048f1d9a128c62b6a661077875701edd2\",\"timestamp\":\"1650625078\",\"event_conversion_type\":\"OFFLINE\",\"pixel_id\":\"dummyPixelId\"},{\"search_string\":\"t-shirts\",\"event_type\":\"SEARCH\",\"user_agent\":\"mozilla/5.0 (macintosh; intel mac os x 10_15_2) applewebkit/537.36 (khtml, like gecko) chrome/79.0.3945.88 safari/537.36\",\"hashed_email\":\"73062d872926c2a556f17b36f50e328ddf9bff9d403939bd14b6c3b7f5a33fc2\",\"hashed_phone_number\":\"bc77d64d7045fe44795ed926df37231a0cfb6ec6b74588c512790e9f143cc492\",\"hashed_idfv\":\"54bd0b26a3d39dad90f5149db49b9fd9ba885f8e35d1d94cae69273f5e657b9f\",\"hashed_mobile_ad_id\":\"f9779d734aaee50f16ee0011260bae7048f1d9a128c62b6a661077875701edd2\",\"timestamp\":\"1650625078\",\"event_conversion_type\":\"OFFLINE\",\"pixel_id\":\"dummyPixelId\"}]"
},
"XML": {},
"FORM": {}
Expand Down

0 comments on commit d6488d5

Please sign in to comment.