Skip to content

Commit

Permalink
Merge branch 'develop' into feat.hs-batching
Browse files Browse the repository at this point in the history
  • Loading branch information
mihir-4116 authored Feb 7, 2024
2 parents 8070f09 + 2a21274 commit 6c64210
Show file tree
Hide file tree
Showing 60 changed files with 45,435 additions and 40,977 deletions.
2 changes: 1 addition & 1 deletion jest.default.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module.exports = {
coverageDirectory: 'reports/coverage',

// An array of regexp pattern strings used to skip coverage collection
coveragePathIgnorePatterns: ['/node_modules/', '__tests__', 'warehouse/v0' ,'test'],
coveragePathIgnorePatterns: ['/node_modules/', '__tests__', 'warehouse/v0', 'test'],

// A list of reporter names that Jest uses when writing coverage reports
coverageReporters: ['json', 'text', 'lcov', 'clover'],
Expand Down
34 changes: 34 additions & 0 deletions src/cdk/v2/destinations/rakuten/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { getMappingConfig } = require('../../../../v0/util');

const ConfigCategories = {
TRACK: {
type: 'track',
name: 'propertiesMapping',
},
};
const mappingConfig = getMappingConfig(ConfigCategories, __dirname);
// Following contains the keys at item level mapping where key can be considered as destkey and value can be considered as sourcekey
const productProperties = {
skulist: 'sku',
qlist: 'quantity',
namelist: 'name',
brandlist: 'brand',
couponlist: 'coupon',
catidlist: 'categoryId',
catlist: 'category',
disamtlist: 'discountAmount',
distypelist: 'discountType',
isclearancelist: 'isClearance',
ismarketplacelist: 'isMarketPlace',
issalelist: 'isSale',
itmstatuslist: 'itmStatus',
marginlist: 'margin',
markdownlist: 'markdown',
shipidlist: 'shipId',
shipbylist: 'shipBy',
taxexemptlist: 'taxExempt',
sequencelist: 'sequence',
};
// list of all properties that are required
const requiredProductProperties = ['skulist', 'qlist', 'namelist'];
module.exports = { ConfigCategories, mappingConfig, productProperties, requiredProductProperties };
101 changes: 101 additions & 0 deletions src/cdk/v2/destinations/rakuten/data/propertiesMapping.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
[
{
"sourceKeys": "properties.orderId",
"required": true,
"destKey": "ord"
},
{
"sourceKeys": ["properties.tr", "properties.ranSiteID"],
"required": true,
"destKey": "tr"
},
{
"sourceKeys": ["properties.land", "properties.landTime"],
"required": true,
"destKey": "land"
},
{
"sourceKeys": ["properties.date", "properties.orderCompletedTime"],
"destKey": "date"
},
{
"sourceKeys": ["properties.altord", "properties.alterOrderId"],
"destKey": "altord"
},
{
"sourceKeys": "properties.currency",
"destKey": "cur"
},
{
"sourceKeys": "properties.creditCardType",
"destKey": "cc"
},
{
"sourceKeys": "properties.commReason",
"destKey": "commreason"
},
{
"sourceKeys": "properties.isComm",
"destKey": "iscomm"
},
{
"sourceKeys": "properties.consumed",
"destKey": "consumed"
},
{
"sourceKeys": "properties.coupon",
"destKey": "coupon"
},
{
"sourceKeys": ["properties.custId", "properties.customerId", "properties.userId"],
"destKey": "custid"
},
{
"sourceKeys": ["properties.custScore", "properties.customerScore"],
"destKey": "custscore"
},
{
"sourceKeys": ["properties.custStatus", "properties.customerStatus"],
"destKey": "custstatus"
},
{
"sourceKeys": ["properties.dId", "properties.advertisingId"],
"destKey": "did"
},
{
"sourceKeys": ["properties.disamt", "properties.discountAmout"],
"destKey": "disamt"
},
{
"sourceKeys": ["properties.ordStatus", "properties.orderStatus"],
"destKey": "ordstatus"
},
{
"sourceKeys": "properties.segment",
"destKey": "segment"
},
{
"sourceKeys": "properties.shipcountry",
"destKey": "shipcountry"
},
{
"sourceKeys": "properties.shipped",
"destKey": "shipped"
},
{
"sourceKeys": ["properties.sitename", "properties.url", "context.page.url"],
"destKey": "sitename"
},
{
"sourceKeys": "properties.storeId",
"destKey": "storeid"
},
{
"sourceKeys": ["properties.storecat", "properties.storeCategory"],
"destKey": "storecat"
},
{
"sourceKeys": "properties.currency",
"destKey": "cur"
}
]
39 changes: 39 additions & 0 deletions src/cdk/v2/destinations/rakuten/procWorkflow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
bindings:
- name: EventType
path: ../../../../constants
- path: ../../bindings/jsontemplate
- name: defaultRequestConfig
path: ../../../../v0/util
- name: removeUndefinedAndNullValues
path: ../../../../v0/util
- path: ./utils

steps:
- name: messageType
template: |
.message.type.toLowerCase();
- name: validateInput
template: |
let messageType = $.outputs.messageType;
$.assert(messageType, "message Type is not present. Aborting");
$.assert(messageType in {{$.EventType.([.TRACK])}}, "message type " + messageType + " is not supported");
$.assertConfig(.destination.Config.mid, "Merchant ID is not present. Aborting");
- name: prepareTrackPayload
condition: $.outputs.messageType === {{$.EventType.TRACK}}
template: |
const properties = $.constructProperties(.message);
const lineItems = $.constructLineItems(.message.properties)
$.context.payload = {...properties,...lineItems,xml:1, mid:.destination.Config.mid}
$.context.payload = $.removeUndefinedAndNullValues($.context.payload);
- name: buildResponse
template: |
const response = $.defaultRequestConfig();
response.params = $.context.payload;
response.method = "GET";
response.endpoint = "https://track.linksynergy.com/ep";
response.headers = {
"accept": "application/json",
"content-type": "application/json"
};
response
70 changes: 70 additions & 0 deletions src/cdk/v2/destinations/rakuten/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const { InstrumentationError } = require('@rudderstack/integrations-lib');
const { isDefinedAndNotNull } = require('rudder-transformer-cdk/build/utils');
const {
mappingConfig,
ConfigCategories,
productProperties,
requiredProductProperties,
} = require('./config');
const { constructPayload } = require('../../../../v0/util');

/**
* This fucntion constructs payloads based upon mappingConfig for Track call type
* @param {*} message
* @returns
*/
const constructProperties = (message) => {
const payload = constructPayload(message, mappingConfig[ConfigCategories.TRACK.name]);
return payload;
};

/**
* This fucntion build the item level list
* @param {*} properties
* @returns
*/
const constructLineItems = (properties) => {
// Validate the existence and non-emptiness of the 'products' array in 'properties'
if (!Array.isArray(properties?.products) || properties.products.length === 0) {
throw new InstrumentationError('Either properties.product is not an array or is empty');
}

const { products } = properties;
const productList = {};

// Iterate over product properties to construct the payload
Object.keys(productProperties).forEach((property) => {
const propertyKey = productProperties[property];

// Extract values for the current property from the 'products' array
const values = products.map((product) =>
isDefinedAndNotNull(product?.[propertyKey]) ? product[propertyKey] : '',
);

// Validate if a required property is missing
if (requiredProductProperties.includes(property) && values.includes('')) {
throw new InstrumentationError(`${propertyKey} is a required field. Aborting`);
}

// Include property in the payload if values are non-empty
if (values.some((element) => element !== '')) {
productList[property] = values.join('|');
}
});

// Map 'amountList' by evaluating 'amount' or deriving it from 'price' and 'quantity'
const amountList = products.map((product) => {
if (!product?.amount && !product?.price) {
throw new InstrumentationError('Either amount or price is required for every product');
}

if (product.price) {
return product.quantity * product.price * 100;
}
return product.amount * 100;
});
productList.amtlist = amountList.join('|');
return productList;
};

module.exports = { constructProperties, constructLineItems };
117 changes: 117 additions & 0 deletions src/cdk/v2/destinations/rakuten/utils.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
const { constructLineItems } = require('./utils');
describe('constructLineItems', () => {
it('should return a non-empty object when given a valid properties object with at least one product', () => {
const properties = {
products: [
{
name: 'Product 1',
sku: 'sku_1',
price: 10,
quantity: 2,
amount: 20,
},
],
};
const result = constructLineItems(properties);
const expectedObj = {
namelist: 'Product 1',
skulist: 'sku_1',
qlist: '2',
amtlist: '2000',
};
expect(result).toEqual(expectedObj);
});

it('should include all mapped properties in the returned object when present in at least one product', () => {
const properties = {
products: [
{
name: 'Product 1',
category: 'Category 1',
sku: 'sku_1',
brand: 'Brand 1',
price: 10,
quantity: 2,
amount: 20,
},
],
};

const result = constructLineItems(properties);

const expectedObj = {
namelist: 'Product 1',
catlist: 'Category 1',
skulist: 'sku_1',
brandlist: 'Brand 1',
qlist: '2',
amtlist: '2000',
};
expect(result).toEqual(expectedObj);
});

it('should include amtlist property in the returned object with calculated values', () => {
const properties = {
products: [
{
name: 'Product 1',
sku: 'sku_1',
price: 10,
quantity: 2,
},
{
name: 'Product 2',
sku: 'sku_2',
price: 5,
quantity: 3,
},
],
};

const result = constructLineItems(properties);

expect(result).toHaveProperty('amtlist');
expect(result.amtlist).toBe('2000|1500');
});

it('should throw an InstrumentationError when properties object is missing or has an empty products array', () => {
const properties = {};

expect(() => constructLineItems(properties)).toThrow(
'Either properties.product is not an array or is empty',
);

properties.products = [];

expect(() => constructLineItems(properties)).toThrow(
'Either properties.product is not an array or is empty',
);
});
it('should throw an InstrumentationError when a product is missing quantity property', () => {
const properties = {
products: [
{
name: 'Product 1',
sku: 'sku_1',
amount: '1234',
},
],
};
expect(() => constructLineItems(properties)).toThrow('quantity is a required field. Aborting');
});
it('should throw an InstrumentationError when a product is missing both amount and price properties', () => {
const properties = {
products: [
{
name: 'Product 1',
sku: 'sku_1',
quantity: 2,
},
],
};

expect(() => constructLineItems(properties)).toThrow(
'Either amount or price is required for every product',
);
});
});
Loading

0 comments on commit 6c64210

Please sign in to comment.