Skip to content

Commit

Permalink
fix: adding identify call
Browse files Browse the repository at this point in the history
  • Loading branch information
shrouti1507 committed Feb 9, 2024
1 parent 6b5797d commit acabc11
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 135 deletions.
4 changes: 2 additions & 2 deletions src/v0/destinations/bluecore/data/bluecoreIdentifyConfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[
{
"destKey": "properties.distinct_id",
"sourceKeys": ["email"],
"sourceKeys": ["traits.email","context.traits.email","userId","anonymousId"],
"required": true
},
{
Expand Down Expand Up @@ -29,7 +29,7 @@
},
{
"destKey": "event",
"sourceKeys": "event",
"sourceKeys": ["traits.action", "context.traits.action"],
"required": false
},
{
Expand Down
251 changes: 118 additions & 133 deletions src/v0/destinations/bluecore/transform.js
Original file line number Diff line number Diff line change
@@ -1,141 +1,126 @@
const { getHashFromArrayWithDuplicate, isDefinedAndNotNull, ConfigurationError, TransformationError,
InstrumentationError, } = require('@rudderstack/integrations-lib');
const { EventType } = require('../../../constants');
const {
constructPayload,
ErrorMessage,
defaultRequestConfig,
getValueFromMessage,
simpleProcessRouterDest,
validateEventName,
} = require('../../util');

const {
MAPPING_CONFIG,
CONFIG_CATEGORIES,
BASE_URL,
EVENT_NAME_MAPPING,
} = require('./config');

const verifyTrackPayload = (payload) => {
switch (payload.event) {
case 'search':
if (!payload.properties.search_term) {
throw new InstrumentationError('[Bluecore] property:: search_query is required for search event');
// TODO: Add support for product array
// TODO : experiment if a custom event without distinct_id as an identifier reaches bluecore or not

const { isDefinedAndNotNull, ConfigurationError, TransformationError,
InstrumentationError, } = require('@rudderstack/integrations-lib');
const { EventType } = require('../../../constants');
const {
constructPayload,
ErrorMessage,
defaultRequestConfig,
getValueFromMessage,

Check failure on line 11 in src/v0/destinations/bluecore/transform.js

View workflow job for this annotation

GitHub Actions / Code Coverage

'getValueFromMessage' is assigned a value but never used
simpleProcessRouterDest,
} = require('../../util');

const {
MAPPING_CONFIG,
CONFIG_CATEGORIES,
BASE_URL,
} = require('./config');
const { verifyPayload, deduceTrackEventName } = require('./util');

const addProductArray = (message, payload, eventName) => {

Check failure on line 22 in src/v0/destinations/bluecore/transform.js

View workflow job for this annotation

GitHub Actions / Code Coverage

Expected to return a value at the end of arrow function
switch (eventName) {
case 'viewed_product':
case 'add_to_cart':
case 'remove_from_cart':
case 'wishlist':
if (isDefinedAndNotNull(message.properties.products)) {
return message.properties.products;
}
break;
case 'purchase':

Check failure on line 32 in src/v0/destinations/bluecore/transform.js

View workflow job for this annotation

GitHub Actions / Code Coverage

This case's code block is the same as the block for the case on line 27
if (!payload.properties.order_id) {
throw new InstrumentationError('[Bluecore] property:: order_id is required for purchase event');
}
if (!payload.properties.total) {
throw new InstrumentationError('[Bluecore] property:: total is required for purchase event');
if (isDefinedAndNotNull(message.properties.products)) {
return message.properties.products;
}
break;
default:
break;
default:
break;
}
};

const deduceTrackEventName = (trackEventName, Config) => {
let eventName;
const { eventsMapping } = Config;
validateEventName(trackEventName);
/*
Step 1: If the event is not amongst the above list of ecommerce events, will look for
the event mapping in the UI. In case it is similar, will map to that.
*/
if (eventsMapping.length > 0) {
const keyMap = getHashFromArrayWithDuplicate(eventsMapping, 'from', 'to', false);
eventName = keyMap[trackEventName];
}
/*
Step 2: To find if the particular event is amongst the list of standard
Rudderstack ecommerce events, used specifically for Bluecore API
mappings.
*/
if (!eventName) {
const eventMapInfo = EVENT_NAME_MAPPING.find((eventMap) => {
if (eventMap.src.includes(trackEventName.toLowerCase())) {
return eventMap;
}
return false;
});
if (!isDefinedAndNotNull(eventMapInfo)) {
throw new ConfigurationError(`[Bluecore] Event name ${trackEventName} is not mapped`);
} else {
return [eventMapInfo.dest];
}
}
return eventName;
};
const trackResponseBuilder = (message, category, { Config }, eventName) => {
const event = getValueFromMessage(message, 'event');

if (!event) {
throw new InstrumentationError('[Bluecore] property:: event is required for track call');
}

const payload = constructPayload(message, MAPPING_CONFIG[category.name]);
// TODO: add support for product array
payload.event = eventName;
verifyTrackPayload(payload);
payload.token = Config.bluecoreNamespace;
if (!payload) {
// fail-safety for developer error
throw new TransformationError(ErrorMessage.FailedToConstructPayload);
}
return payload;
};


const responseBuilderSimple = (response) => {
const resp = defaultRequestConfig();
resp.endpoint = BASE_URL;
resp.body.JSON = response;
resp.headers = {
'Content-Type': 'application/json',
};
return resp;
};

const trackResponseBuilder = (message, category, { Config }, eventName) => {
const payload = constructPayload(message, MAPPING_CONFIG[category.name]);
// TODO: add support for product array
if(eventName !== 'optin' || eventName !== 'unsubscribe') {
payload.properties.product = addProductArray(message, payload, eventName);
}

const process = async (event) => {
const deducedEventNameArray = [];
const toSendEvents = [];
const respList = [];
const { message, destination } = event;
if (!message.type) {
throw new InstrumentationError('Message Type is not present. Aborting message.');
}

if (!destination.Config.bluecoreNamespace) {
throw new ConfigurationError('[BLUECORE] bluecore account namespace required for Authentication.');
}
const messageType = message.type.toLowerCase();
const category = CONFIG_CATEGORIES[message.type.toUpperCase()];
switch (messageType) {
case EventType.TRACK:
deducedEventNameArray.push(...deduceTrackEventName(message.event, destination.Config));
deducedEventNameArray.forEach((eventName) => {
const trackResponse = trackResponseBuilder(message, category, destination, eventName);
toSendEvents.push(trackResponse);
});
break;
case EventType.IDENTIFY:
// response = await identifyResponseBuilder(message, category, destination);
break;
default:
throw new InstrumentationError(`Message type ${messageType} not supported`);
}
toSendEvents.forEach((sendEvent) => {
respList.push(responseBuilderSimple(sendEvent));
});
return respList;
};

const processRouterDest = async (inputs, reqMetadata) => {
const respList = await simpleProcessRouterDest(inputs, process, reqMetadata);
return respList;
payload.event = eventName;
verifyPayload(payload, message);
payload.token = Config.bluecoreNamespace;
if (!payload) {
// fail-safety for developer error
throw new TransformationError(ErrorMessage.FailedToConstructPayload);
}
return payload;
};

const identifyResponseBuilder = (message, category, destination) => {
const { Config } = destination;
const { bluecoreNamespace } = Config;
const payload = constructPayload(message, MAPPING_CONFIG[category.name]);
payload.token = bluecoreNamespace;

if (isDefinedAndNotNull(payload.event)) {
verifyPayload(payload, message);
} else {
// unless user specifies the event to be 'identify', we will default to customer_patch
payload.event = 'customer_patch';
}
if (!payload) {
// fail-safety for developer error
throw new TransformationError(ErrorMessage.FailedToConstructPayload);
}
return payload;
};

const responseBuilderSimple = (response) => {
const resp = defaultRequestConfig();
resp.endpoint = BASE_URL;
resp.body.JSON = response;
resp.headers = {
'Content-Type': 'application/json',
};

module.exports = { process, processRouterDest };
return resp;
}

const process = async (event) => {
const deducedEventNameArray = [];
const toSendEvents = [];
const respList = [];
const { message, destination } = event;
if (!message.type) {
throw new InstrumentationError('Message Type is not present. Aborting message.');
}

if (!destination.Config.bluecoreNamespace) {
throw new ConfigurationError('[BLUECORE] bluecore account namespace required for Authentication.');
}
const messageType = message.type.toLowerCase();
const category = CONFIG_CATEGORIES[message.type.toUpperCase()];
switch (messageType) {
case EventType.TRACK:
deducedEventNameArray.push(...deduceTrackEventName(message.event, destination.Config));
deducedEventNameArray.forEach((eventName) => {
const trackResponse = trackResponseBuilder(message, category, destination, eventName);
toSendEvents.push(trackResponse);
});
break;
case EventType.IDENTIFY:
toSendEvents.push(identifyResponseBuilder(message, category, destination));
break;
default:
throw new InstrumentationError(`Message type ${messageType} not supported`);
}
toSendEvents.forEach((sendEvent) => {
respList.push(responseBuilderSimple(sendEvent));
});
return respList;
};

const processRouterDest = async (inputs, reqMetadata) => {
const respList = await simpleProcessRouterDest(inputs, process, reqMetadata);
return respList;
};

module.exports = { process, processRouterDest };
67 changes: 67 additions & 0 deletions src/v0/destinations/bluecore/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const { InstrumentationError, isDefinedAndNotNullAndNotEmpty, getHashFromArrayWithDuplicate, isDefinedAndNotNull } = require("@rudderstack/integrations-lib");
const { getFieldValueFromMessage, validateEventName } = require("../../util");
const { EVENT_NAME_MAPPING } = require("./config");

const verifyPayload = (payload, message) => {
switch (payload.event) {
case 'search':
if (!payload.properties.search_term) {
throw new InstrumentationError('[Bluecore] property:: search_query is required for search event');
}
break;
case 'purchase':
if (!payload.properties.order_id) {
throw new InstrumentationError('[Bluecore] property:: order_id is required for purchase event');
}
if (!payload.properties.total) {
throw new InstrumentationError('[Bluecore] property:: total is required for purchase event');
}
break;
case 'identify':
if (!isDefinedAndNotNullAndNotEmpty(getFieldValueFromMessage(message, 'email'))) {
throw new InstrumentationError('[Bluecore] property:: email is required for identify event');
}
break;
default:
break;
}
};

const deduceTrackEventName = (trackEventName, Config) => {
let eventName;
const { eventsMapping } = Config;
validateEventName(trackEventName);
/*
Step 1: Will look for the event name in the eventsMapping array if mapped to a standard bluecore event.
and return the corresponding event name if found.
*/
if (eventsMapping.length > 0) {
const keyMap = getHashFromArrayWithDuplicate(eventsMapping, 'from', 'to', false);
eventName = keyMap[trackEventName];
return eventName;
}
/*
Step 2: To find if the particular event is amongst the list of standard
Rudderstack ecommerce events, used specifically for Bluecore API
mappings.
*/

const eventMapInfo = EVENT_NAME_MAPPING.find((eventMap) => {
if (eventMap.src.includes(trackEventName.toLowerCase())) {
return eventMap;
}
return false;
});
if (isDefinedAndNotNull(eventMapInfo)) {
return [eventMapInfo.dest];
}

// Step 3: if nothing matches this is to be considered as a custom event
return trackEventName;
};

module.exports = {
verifyPayload,
deduceTrackEventName
};

0 comments on commit acabc11

Please sign in to comment.