Skip to content

Commit

Permalink
Adding Bluecore integration
Browse files Browse the repository at this point in the history
  • Loading branch information
proModeLife committed Dec 29, 2023
1 parent 66a5390 commit 25ceedc
Show file tree
Hide file tree
Showing 8 changed files with 2,955 additions and 0 deletions.
49 changes: 49 additions & 0 deletions src/v0/destinations/bluecore/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const { getMappingConfig } = require('../../util');
const BASE_URL = 'https://api.bluecore.com/api/track/mobile/v1';

const CONFIG_CATEGORIES = {
IDENTIFY: {
name: 'bluecoreIdentifyConfig',
type: 'identify',
},
TRACK: {
name: 'bluecoreTrackConfig',
type: 'track',
},
};

const EVENT_NAME_MAPPING = {
'Product Viewed': 'viewed_product',
'Product Added': 'add_to_cart',
'Order Completed': 'purchase',
'Products Searched': 'search',
'Product Added to Wishlist': 'wishlist',
'Checkout Step Viewed': 'checkout',
'Product Removed': 'remove_from_cart',
'Subscribe Interest': 'subscribe_interest',
'Unsubscribe Interest': 'unsubscribe_interest',
Identify: 'identify',
};

const BLUECORE_IDENTIFY_EXCLUSION = [
'event',
'phone',
'phoneNumber',
'phone_number',
'firstName',
'firstname',
'first_name',
'lastName',
'lastname',
'last_name',
'gender',
];

const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname);
module.exports = {
CONFIG_CATEGORIES,
MAPPING_CONFIG,
EVENT_NAME_MAPPING,
BLUECORE_IDENTIFY_EXCLUSION,
BASE_URL,
};
49 changes: 49 additions & 0 deletions src/v0/destinations/bluecore/data/bluecoreIdentifyConfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
[
{
"destKey": "properties.distinct_id",
"sourceKeys": ["userId", "anonymousId", "email"],
"required": true
},
{
"destKey": "properties.custormer.name",
"sourceKeys": "name",
"required": false,
"sourceFromGenericMap": true
},
{
"destKey": "properties.custormer.age",
"sourceKeys": "age",
"required": false
},
{
"destKey": "event",
"sourceKeys": "event",
"required": false
},
{
"destKey": "properties.custormer.sex",
"sourceKeys": "sex",
"required": false
},
{
"destKey": "properties.custormer.address",
"sourceKeys": "address",
"required": false
},
{
"destKey": "properties.custormer.email",
"sourceKeys": "email",
"required": true,
"sourceFromGenericMap": true
},
{
"destKey": "client",
"sourceKeys": "context.app.version",
"required": false
},
{
"destKey": "properties.device",
"sourceKeys": "context.device.model",
"required": false
}
]
43 changes: 43 additions & 0 deletions src/v0/destinations/bluecore/data/bluecoreTrackConfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[
{
"destKey": "distinct_id",
"sourceKeys": ["userId", "anonymousId", "email"],
"required": true
},
{
"destKey": "customer.name",
"sourceKeys": "name",
"required": false
},
{
"destKey": "customer.age",
"sourceKeys": "age",
"required": false
},
{
"destKey": "customer.sex",
"sourceKeys": "sex",
"required": false
},
{
"destKey": "customer.address",
"sourceKeys": "address",
"required": false
},
{
"destKey": "customer.email",
"sourceKeys": "email",
"required": false,
"sourceFromGenericMap": true
},
{
"destKey": "client",
"sourceKeys": "context.app.version",
"required": false
},
{
"destKey": "device",
"sourceKeys": "context.device.model",
"required": false
}
]
35 changes: 35 additions & 0 deletions src/v0/destinations/bluecore/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const eventProcessor = require('./transform');

const sampleEvent = {
message: {
type: 'TRACK',
event: 'Product Viewed',
userId: '27340af5c8819',
properties: {
product_id: '622c6f5d5cf86a4c77358033',
sku: '8472-998-0112',
category: 'Games',
name: 'Cones of Dunshire',
brand: 'Wyatt Games',
variant: 'exapansion pack',
price: 49.99,
quantity: 5,
coupon: 'PREORDER15',
currency: 'USD',
position: 1,
url: 'https://www.website.com/product/path',
image_url: 'https://www.website.com/product/path.webp',
},
},
destination: {

},
};

eventProcessor.process(sampleEvent)
.then(response => {
console.log('Response:', response);

Check warning on line 31 in src/v0/destinations/bluecore/test.js

View workflow job for this annotation

GitHub Actions / Code Coverage

Unexpected console statement
})
.catch(error => {
console.error('Error:', error);

Check warning on line 34 in src/v0/destinations/bluecore/test.js

View workflow job for this annotation

GitHub Actions / Code Coverage

Unexpected console statement
});
210 changes: 210 additions & 0 deletions src/v0/destinations/bluecore/transform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
const { EventType } = require('../../../constants');
const {
constructPayload,
ErrorMessage,
defaultRequestConfig,
defaultPostRequestConfig,
getValueFromMessage,
isDefinedAndNotNull,
extractCustomFields,
simpleProcessRouterDest,
} = require('../../util');

const {
// getActionSource,
// handleProduct,
// handleSearch,
handleProductListViewed,
// handleOrder,
// populateCustomDataBasedOnCategory,
// getCategoryFromEvent,
} = require('./utils');

const { JSON_MIME_TYPE } = require('../../util/constant');
const {
TransformationError,
InstrumentationError,
ConfigurationError,
} = require('../../util/errorTypes');

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

function checkValidEventName(str) {
return str.includes('.') || /\d/.test(str) || str.length > 64;
}


const trackResponseBuilder = async (message, category, { Config }) => {
console.log('Incoming Message:', message);

Check warning on line 44 in src/v0/destinations/bluecore/transform.js

View workflow job for this annotation

GitHub Actions / Code Coverage

Unexpected console statement
console.log('Destination Category:', category);

Check warning on line 45 in src/v0/destinations/bluecore/transform.js

View workflow job for this annotation

GitHub Actions / Code Coverage

Unexpected console statement
console.log('Configuration:', Config);

Check warning on line 46 in src/v0/destinations/bluecore/transform.js

View workflow job for this annotation

GitHub Actions / Code Coverage

Unexpected console statement

let event = getValueFromMessage(message, 'event');
if (!event) {
throw new InstrumentationError('[BLUECORE] property:: event is required for track call');
}

if (!Config.eventApiKey) {
throw new ConfigurationError('[BLUECORE] event Api Keys required for Authentication.');
}
let payload = {};
console.log('constructed Payload :', payload);

Check warning on line 57 in src/v0/destinations/bluecore/transform.js

View workflow job for this annotation

GitHub Actions / Code Coverage

Unexpected console statement

if (!payload) {
// fail-safety for developer error
throw new TransformationError(ErrorMessage.FailedToConstructPayload);
}
event = event.trim();
// Check for the event type being 'Product Viewed'
if (event.toLowerCase() === 'product viewed') {
// Utilize handleProductListViewed function

// console.log('Current Message:', message);
const properties = handleProductListViewed(message, category);
// Add other necessary properties to the payload
properties.distinct_id = '[email protected]';
properties.token = 'bluestore';
properties.client = '4.0';
properties.device = 'mobile/iPad';
console.log(properties);

Check warning on line 75 in src/v0/destinations/bluecore/transform.js

View workflow job for this annotation

GitHub Actions / Code Coverage

Unexpected console statement

payload = { event : 'viewed_product', properties : properties};
// payload.event = event.replace(/\s+/g, '_');
// payload.properties = properties;
// if (checkValidEventName(payload.event)) {
// throw new InstrumentationError(
// "[BLUECORE] Event shouldn't contain period(.), numeric value and contains not more than 64 characters",
// );
// }
// payload = extractCustomFields(message, payload, ['properties'], []);

for (const product of properties.products) {

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

View workflow job for this annotation

GitHub Actions / Code Coverage

iterators/generators require regenerator-runtime, which is too heavyweight for this guide to allow them. Separately, loops should be avoided in favor of array iterations
console.log("Product Details:");

Check warning on line 88 in src/v0/destinations/bluecore/transform.js

View workflow job for this annotation

GitHub Actions / Code Coverage

Unexpected console statement
console.log(product);

Check warning on line 89 in src/v0/destinations/bluecore/transform.js

View workflow job for this annotation

GitHub Actions / Code Coverage

Unexpected console statement
}

// Build the response object with Bluecore payload
const response = defaultRequestConfig();
response.endpoint = `${BASE_URL}`;

response.method = defaultPostRequestConfig.requestMethod;
const basicAuth = Buffer.from(Config.eventApiKey).toString('base64');
response.headers = {
Authorization: `Basic ${basicAuth}`,
'Content-Type': JSON_MIME_TYPE,
};
response.body.JSON = payload;
console.log('Transformed Payload:', payload);

Check warning on line 103 in src/v0/destinations/bluecore/transform.js

View workflow job for this annotation

GitHub Actions / Code Coverage

Unexpected console statement
return response;

}
if (isDefinedAndNotNull(EVENT_NAME_MAPPING[event])) {
payload.event = EVENT_NAME_MAPPING[event];
}
payload.event = payload.event.replace(/\s+/g, '_');
if (checkValidEventName(payload.event)) {
throw new InstrumentationError(
"[BLUECORE] Event shouldn't contain period(.), numeric value and contains not more than 64 characters",
);
}
payload = extractCustomFields(message, payload, ['properties'], []);
console.log('Transformed Payload:', payload);

const response = defaultRequestConfig();
response.endpoint = `${BASE_URL}`;

response.method = defaultPostRequestConfig.requestMethod;
const basicAuth = Buffer.from(Config.eventApiKey).toString('base64');
response.headers = {
Authorization: `Basic ${basicAuth}`,
'Content-Type': JSON_MIME_TYPE,
};
response.body.JSON = payload;
return response;
};

const identifyResponseBuilder = async (message, category, { Config }) => {
if (!Config.usersApiKey) {
throw new ConfigurationError('[BLUECORE] User API Key required for Authentication.');
}

let payload = constructPayload(message, MAPPING_CONFIG[category.name]);
let event = getValueFromMessage(message, 'event');

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

View workflow job for this annotation

GitHub Actions / Code Coverage

'event' is assigned a value but never used
console.log('Payload after constructPayload:', payload);
console.log('Payload properties after constructPayload:', payload.properties);

if (!payload) {
// fail-safety for developer error
throw new TransformationError(ErrorMessage.FailedToConstructPayload);
}

payload = extractCustomFields(
message,
payload,
['traits', 'context.traits'],
BLUECORE_IDENTIFY_EXCLUSION,
);

console.log('Payload after extractCustomFields:', payload);
console.log('Properties after extractCustomFields:', payload.properties);


const response = defaultRequestConfig();
response.endpoint = `${BASE_URL}`;
response.method = defaultPostRequestConfig.requestMethod;

const basicAuth = Buffer.from(Config.usersApiKey).toString('base64');
response.headers = {
Authorization: `Basic ${basicAuth}`,
'Content-Type': JSON_MIME_TYPE,
};
response.body.JSON = payload;
// response.body.JSON.event = event;

console.log('Final Response before returning:', response);

return response;
};


const process = async (event) => {
const { message, destination } = event;
console.log('Incoming Event:', event);
console.log('Incoming Destination:', destination);

if (!message.type) {
throw new InstrumentationError('Message Type is not present. Aborting message.');
}

const messageType = message.type.toLowerCase();
const category = CONFIG_CATEGORIES[message.type.toUpperCase()];
console.log('Category :', category);
let response;
switch (messageType) {
case EventType.TRACK:
response = await trackResponseBuilder(message, category, destination);
break;
case EventType.IDENTIFY:
response = await identifyResponseBuilder(message, category, destination);
break;
default:
throw new InstrumentationError(`Message type ${messageType} not supported`);
}
// Log the final response before returning it
console.log('Final Response:', response);

return response;
};

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

module.exports = { process, processRouterDest };
Loading

0 comments on commit 25ceedc

Please sign in to comment.