Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix((INT-360): enhancement: shopify ecom fix and enhancement #2493

Closed
wants to merge 37 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2e02c45
refactor: shopify
anantjain45823 Aug 17, 2023
0a2dfcf
Merge branch 'develop' into refactor.shopify
anantjain45823 Aug 18, 2023
19556fa
chore: used prettier
Gauravudia Aug 23, 2023
1d5033a
basic changes
anantjain45823 Aug 24, 2023
0c5a1c5
refactor: shopify ecomm (#2510)
Gauravudia Aug 24, 2023
6a52c0a
Merge branch 'develop' into refactor.shopify
Gauravudia Aug 24, 2023
7b79ea4
refactor: update line item exlusion fields
Gauravudia Aug 24, 2023
7690f9c
chore: track events flow improvement
anantjain45823 Aug 24, 2023
94bd394
fix: mapping config
Gauravudia Aug 24, 2023
caf9113
fix: ecom mapping
Gauravudia Aug 24, 2023
5720d7c
fix: order completed mapping
Gauravudia Aug 25, 2023
8ae971a
feat: add variant name mapping in lineitem
Gauravudia Aug 25, 2023
f716e0d
chore: added test cases and some fixes
anantjain45823 Aug 25, 2023
a1d87f2
fix:sonar issue
anantjain45823 Aug 25, 2023
9e466bb
Rename shopify_source.test_v2.js to shopify_source_v2.test.js
anantjain45823 Aug 25, 2023
d9f4e1a
fix: checkout id mapping
Gauravudia Aug 25, 2023
ea61758
Merge branch 'develop' into refactor.shopify
Gauravudia Aug 25, 2023
007c885
chore: remove redudant exclusion fields
Gauravudia Aug 26, 2023
7c98b1b
chore: added test cases
anantjain45823 Aug 28, 2023
dbed2a9
Merge branch 'develop' into refactor.shopify
anantjain45823 Aug 30, 2023
9fb9dec
test: add track and enrichment layers test cases
Gauravudia Sep 3, 2023
26ab1d7
Merge branch 'develop' into refactor.shopify
Gauravudia Sep 3, 2023
c0868fb
added product added or removed configs
anantjain45823 Sep 5, 2023
3256d86
chore: test cases added for products event
anantjain45823 Sep 5, 2023
d782328
Merge branch 'develop' into refactor.shopify
anantjain45823 Sep 5, 2023
293688b
chore: test cases added for products event
anantjain45823 Sep 5, 2023
c6727f7
remove: extra file
anantjain45823 Sep 5, 2023
7db623a
chore: resolve sonar issue
anantjain45823 Sep 5, 2023
db09237
Merge branch 'develop' into refactor.shopify
anantjain45823 Sep 8, 2023
6897f52
chore: add testcase
Gauravudia Sep 10, 2023
ee364f5
Merge branch 'develop' into refactor.shopify
anantjain45823 Sep 13, 2023
e98bac4
small mapping fix
anantjain45823 Sep 14, 2023
50a66ad
chore: addressing review comments
Gauravudia Sep 25, 2023
97c562f
Merge branch 'develop' into refactor.shopify
anantjain45823 Sep 25, 2023
093825f
Merge branch 'develop' into refactor.shopify
anantjain45823 Sep 26, 2023
263c5ba
Merge branch 'develop' into refactor.shopify
anantjain45823 Oct 5, 2023
cffefdf
Merge branch 'develop' into refactor.shopify
anantjain45823 Oct 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions src/v0/sources/shopify V2/commonUtils.js
anantjain45823 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/* eslint-disable camelcase */
const sha256 = require('sha256');
const stats = require('../../../util/stats');
const { constructPayload, extractCustomFields, flattenJson } = require('../../util');
const { RedisDB } = require('../../../util/redis/redisConnector');
const logger = require('../../../logger');
const {
lineItemsMappingJSON,
productMappingJSON,
LINE_ITEM_EXCLUSION_FIELDS,
PRODUCT_MAPPING_EXCLUSION_FIELDS,
SHOPIFY_TRACK_MAP,
} = require('./config');
const { TransformationError } = require('../../util/errorTypes');

const getCartToken = (message) => {
const { event } = message;
if (event === SHOPIFY_TRACK_MAP.carts_update) {
return message.properties?.id || message.properties?.token;
}
return message.properties?.cart_token || null;
};

const getDataFromRedis = async (key, metricMetadata) => {
try {
stats.increment('shopify_redis_calls', {
type: 'get',
field: 'all',
...metricMetadata,
});
const redisData = await RedisDB.getVal(key);
if (redisData === null) {
stats.increment('shopify_redis_no_val', {
...metricMetadata,
});
}
return redisData;
} catch (e) {
logger.debug(`{{SHOPIFY::}} Get call Failed due redis error ${e}`);
stats.increment('shopify_redis_failures', {
type: 'get',
...metricMetadata,
});
}
return null;
};

/**
* query_parameters : { topic: ['<shopify_topic>'], ...}
* Throws error otherwise
* @param {*} event
* @returns
*/
const getShopifyTopic = (event) => {
const { query_parameters: qParams } = event;
logger.debug(`[Shopify] Input event: query_params: ${JSON.stringify(qParams)}`);
if (!qParams) {
throw new TransformationError('Query_parameters is missing');
}
const { topic } = qParams;
if (!topic || !Array.isArray(topic)) {
throw new TransformationError('Invalid topic in query_parameters');
}
if (topic.length === 0) {
throw new TransformationError('Topic not found');
}
return topic[0];
};

const getHashLineItems = (cart) => {
if (cart && cart?.line_items && cart.line_items.length > 0) {
anantjain45823 marked this conversation as resolved.
Show resolved Hide resolved
return sha256(JSON.stringify(cart.line_items));
}
return 'EMPTY';
};

const getVariantString = (lineItem) => {
const { variant_id, variant_price, variant_title } = lineItem;
return `${variant_id || ''} ${variant_price || ''} ${variant_title || ''}`;
};
Gauravudia marked this conversation as resolved.
Show resolved Hide resolved

const getProductsListFromLineItems = (lineItems) => {
if (!lineItems || lineItems.length === 0) {
return [];
}
const products = [];
lineItems.forEach((lineItem) => {
const product = constructPayload(lineItem, lineItemsMappingJSON);
extractCustomFields(lineItem, product, 'root', LINE_ITEM_EXCLUSION_FIELDS);
product.variant = getVariantString(lineItem);
products.push(product);
});
return products;
};
Gauravudia marked this conversation as resolved.
Show resolved Hide resolved

const createPropertiesForEcomEvent = (message) => {
const { line_items: lineItems } = message;
const productsList = getProductsListFromLineItems(lineItems);
const mappedPayload = constructPayload(message, productMappingJSON);
Gauravudia marked this conversation as resolved.
Show resolved Hide resolved
extractCustomFields(message, mappedPayload, 'root', PRODUCT_MAPPING_EXCLUSION_FIELDS);
mappedPayload.products = productsList;
return mappedPayload;
};

const extractEmailFromPayload = (event) => {
const flattenedPayload = flattenJson(event);
let email;
const regex_email = /\bemail\b/i;
Object.entries(flattenedPayload).some(([key, value]) => {
if (regex_email.test(key)) {
email = value;
return true;
}
return false;
});
return email;
};

module.exports = {
getCartToken,
getShopifyTopic,
getProductsListFromLineItems,
createPropertiesForEcomEvent,
extractEmailFromPayload,
getHashLineItems,
getDataFromRedis,
};
130 changes: 130 additions & 0 deletions src/v0/sources/shopify V2/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
const path = require('path');
const fs = require('fs');
const { EventType } = require('../../../constants');

const INTEGERATION = 'SHOPIFY';
anantjain45823 marked this conversation as resolved.
Show resolved Hide resolved

const NO_OPERATION_SUCCESS = {
outputToSource: {
body: Buffer.from('OK').toString('base64'),
contentType: 'text/plain',
},
statusCode: 200,
};

const identifierEvents = ['rudderIdentifier', 'rudderSessionIdentifier'];

const IDENTIFY_TOPICS = {
CUSTOMERS_CREATE: 'customers_create',
CUSTOMERS_UPDATE: 'customers_update',
};

const RUDDER_ECOM_MAP = { // TOBEUPDATED:
checkouts_create: 'Checkout Started',
checkouts_update: 'Checkout Updated',
orders_updated: 'Order Updated',
orders_create: 'Order Created',
carts_update: 'Cart Updated' // This will split into Product Added and Product Removed
};

const SHOPIFY_TO_RUDDER_ECOM_EVENTS_MAP = ['Cart Update', 'Checkout Updated'];

const SHOPIFY_ADMIN_ONLY_EVENTS = ['Order Deleted', 'Fulfillments Create', 'Fulfillments Update'];

const SHOPIFY_TRACK_MAP = {
checkouts_delete: 'Checkout Deleted',
carts_update: 'Cart Update',
customers_enable: 'Customer Enabled',
customers_disable: 'Customer Disabled',
fulfillments_create: 'Fulfillments Create',
fulfillments_update: 'Fulfillments Update',
orders_delete: 'Order Deleted',
orders_edited: 'Order Edited',
orders_cancelled: 'Order Cancelled',
orders_fulfilled: 'Order Fulfilled',
orders_paid: 'Order Paid',
orders_partially_fullfilled: 'Order Partially Fulfilled',
};

const identifyMappingJSON = JSON.parse(
fs.readFileSync(path.resolve(__dirname, 'data', 'identifyMapping.json')),
);

const productMappingJSON = JSON.parse(
fs.readFileSync(path.resolve(__dirname, 'data', 'productMapping.json')),
);

const lineItemsMappingJSON = JSON.parse(
fs.readFileSync(path.resolve(__dirname, 'data', 'lineItemsMapping.json')),
);

const MAPPING_CATEGORIES = {
[EventType.IDENTIFY]: identifyMappingJSON,
[EventType.TRACK]: productMappingJSON,
// update it for every ECOM ma[ong and genera mapping]
};

const LINE_ITEM_EXCLUSION_FIELDS = [
'product_id',
'sku',
'name',
'price',
'vendor',
'quantity',
'variant_id',
'variant_price',
Gauravudia marked this conversation as resolved.
Show resolved Hide resolved
'variant_title',
];

const PRODUCT_MAPPING_EXCLUSION_FIELDS = [
Gauravudia marked this conversation as resolved.
Show resolved Hide resolved
'id',
'total_price',
'total_tax',
'currency',
'line_items',
'customer',
'shipping_address',
'billing_address',
];

/**
* list of events name supported as generic track calls
* track events not belonging to this list or ecom events will
* be discarded.
*/
const NON_ECOM_SUPPORTED_EVENTS = [ // to be updated
'checkouts_delete',
'checkouts_update',
'customers_disable',
'customers_enable',
'carts_update',
'fulfillments_create',
'fulfillments_update',
'orders_create',
'orders_delete',
'orders_edited',
'orders_cancelled',
'orders_fulfilled',
'orders_paid',
'orders_partially_fullfilled',
];

const maxTimeToIdentifyRSGeneratedCall = 10000; // in ms

module.exports = {
NO_OPERATION_SUCCESS,
identifierEvents,
IDENTIFY_TOPICS,
INTEGERATION,
SHOPIFY_TO_RUDDER_ECOM_EVENTS_MAP,
MAPPING_CATEGORIES,
RUDDER_ECOM_MAP,
lineItemsMappingJSON,
productMappingJSON,
LINE_ITEM_EXCLUSION_FIELDS,
PRODUCT_MAPPING_EXCLUSION_FIELDS,
NON_ECOM_SUPPORTED_EVENTS,
SHOPIFY_TRACK_MAP,
SHOPIFY_ADMIN_ONLY_EVENTS,
maxTimeToIdentifyRSGeneratedCall
};
112 changes: 112 additions & 0 deletions src/v0/sources/shopify V2/data/identifyMapping.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
[
{
"sourceKeys": "id",
"destKeys": "userId"
},
{
"sourceKeys": "email",
"destKeys": "traits.email"
},

{
"sourceKeys": "first_name",
"destKeys": "traits.firstName"
},

{
"sourceKeys": "last_name",
"destKeys": "traits.lastName"
},

{
"sourceKeys": "phone",
"destKeys": "traits.phone"
},

{
"sourceKeys": "addresses",
"destKeys": "traits.addressList"
},

{
"sourceKeys": "default_address",
"destKeys": "traits.address"
},

{
"sourceKeys": "shipping_address",
"destKeys": "traits.shippingAddress"
},

{
"sourceKeys": "billing_address",
"destKeys": "traits.billingAddress"
},

{
"sourceKeys": "accepts_marketing",
"destKeys": "traits.acceptsMarketing"
},

{
"sourceKeys": "orders_count",
"destKeys": "traits.orderCount"
},

{
"sourceKeys": "state",
"destKeys": "traits.state"
},
{
"sourceKeys": "total_spent",
"destKeys": "traits.totalSpent"
},
{
"sourceKeys": "note",
"destKeys": "traits.note"
},
{
"sourceKeys": "verified_email",
"destKeys": "traits.verifiedEmail"
},
{
"sourceKeys": "multipass_identifier",
"destKeys": "traits.multipassIdentifier"
},
{
"sourceKeys": "tax_exempt",
"destKeys": "traits.taxExempt"
},
{
"sourceKeys": "tags",
"destKeys": "traits.tags"
},
{
"sourceKeys": "last_order_name",
"destKeys": "traits.lastOrderName"
},
{
"sourceKeys": "currency",
"destKeys": "traits.currency"
},
{
"sourceKeys": "marketing_opt_in_level",
"destKeys": "traits.marketingOptInLevel"
},
{
"sourceKeys": "tax_exemptions",
"destKeys": "traits.taxExemptions"
},
{
"sourceKeys": "sms_marketing_consent",
"destKeys": "traits.smsMarketingConsent"
},
{
"sourceKeys": "admin_graphql_api_id",
"destKeys": "traits.adminGraphqlApiId"
},
{
"sourceKeys": "accepts_marketing_updated_at",
"destKeys": "traits.acceptsMarketingUpdatedAt"
}
]
26 changes: 26 additions & 0 deletions src/v0/sources/shopify V2/data/lineItemsMapping.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
"sourceKeys": "product_id",
"destKey": "product_id"
},
{
"sourceKeys": "sku",
"destKey": "sku"
},
{
"sourceKeys": "name",
"destKey": "title"
},
{
"sourceKeys": "price",
"destKey": "price"
},
{
"sourceKeys": "vendor",
"destKey": "brand"
},
{
"sourceKeys": "quantity",
"destKey": "quantity"
}
]
Loading