diff --git a/src/cdk/v2/destinations/bluecore/data/bluecoreTrackConfig.json b/src/cdk/v2/destinations/bluecore/data/bluecoreTrackConfig.json index f25cb42714..e24ef847f5 100644 --- a/src/cdk/v2/destinations/bluecore/data/bluecoreTrackConfig.json +++ b/src/cdk/v2/destinations/bluecore/data/bluecoreTrackConfig.json @@ -1,92 +1,77 @@ [ - { - "destKey": "properties.distinct_id", - "sourceKeys": [ - "userId", - "anonymousId", - "email" - ], - "required": false - }, - { - "destKey": "properties.customer.name", - "sourceKeys": "name", - "required": false, - "sourceFromGenericMap": true - }, - { - "destKey": "properties.customer.first_name", - "sourceKeys": "firstName", - "required": false, - "sourceFromGenericMap": true - }, - { - "destKey": "properties.customer.last_name", - "sourceKeys": "lastName", - "required": false, - "sourceFromGenericMap": true - }, - { - "destKey": "properties.customer.age", - "sourceKeys": [ - "context.traits.age", - "traits.age" - ], - "required": false - }, - { - "destKey": "properties.customer.sex", - "sourceKeys": [ - "traits.gender", - "context.traits.gender", - "traits.sex", - "context.traits.sex" - ], - "required": false - }, - { - "destKey": "properties.customer.address", - "sourceKeys": "address", - "required": false, - "sourceFromGenericMap": true - }, - { - "destKey": "properties.customer.email", - "sourceKeys": "email", - "required": false, - "sourceFromGenericMap": true - }, - { - "destKey": "properties.search_term", - "sourceKeys": "properties.query", - "required": false - }, - { - "destKey": "properties.order_id", - "sourceKeys": "properties.order_id", - "required": false - }, - { - "destKey": "properties.total", - "sourceKeys": "properties.total", - "required": false - }, - { - "destKey": "properties.client", - "sourceKeys": "context.app.version", - "required": false - }, - { - "destKey": "properties.device", - "sourceKeys": "context.device.model", - "required": false - }, - { - "destKey": "properties.products", - "sourceKeys": [ - "properties.products", - "properties" - ], - "required": false - } - ] \ No newline at end of file + { + "destKey": "properties.distinct_id", + "sourceKeys": ["userId", "anonymousId", "email"], + "required": false + }, + { + "destKey": "properties.customer.name", + "sourceKeys": "name", + "required": false, + "sourceFromGenericMap": true + }, + { + "destKey": "properties.customer.first_name", + "sourceKeys": "firstName", + "required": false, + "sourceFromGenericMap": true + }, + { + "destKey": "properties.customer.last_name", + "sourceKeys": "lastName", + "required": false, + "sourceFromGenericMap": true + }, + { + "destKey": "properties.customer.age", + "sourceKeys": ["context.traits.age", "traits.age"], + "required": false + }, + { + "destKey": "properties.customer.sex", + "sourceKeys": ["traits.gender", "context.traits.gender", "traits.sex", "context.traits.sex"], + "required": false + }, + { + "destKey": "properties.customer.address", + "sourceKeys": "address", + "required": false, + "sourceFromGenericMap": true + }, + { + "destKey": "properties.customer.email", + "sourceKeys": "email", + "required": false, + "sourceFromGenericMap": true + }, + { + "destKey": "properties.search_term", + "sourceKeys": "properties.query", + "required": false + }, + { + "destKey": "properties.order_id", + "sourceKeys": "properties.order_id", + "required": false + }, + { + "destKey": "properties.total", + "sourceKeys": "properties.total", + "required": false + }, + { + "destKey": "properties.client", + "sourceKeys": "context.app.version", + "required": false + }, + { + "destKey": "properties.device", + "sourceKeys": "context.device.model", + "required": false + }, + { + "destKey": "properties.products", + "sourceKeys": ["properties.products"], + "required": false + } +] diff --git a/src/cdk/v2/destinations/bluecore/procWorkflow.yaml b/src/cdk/v2/destinations/bluecore/procWorkflow.yaml index 8c464c9314..85b0309874 100644 --- a/src/cdk/v2/destinations/bluecore/procWorkflow.yaml +++ b/src/cdk/v2/destinations/bluecore/procWorkflow.yaml @@ -34,7 +34,8 @@ steps: const payload = $.constructProperties(.message); $.context.payloads = $.outputs.deduceEventNames.( const newPayload = cloneDeep(payload); - newPayload.properties.products = $.addProductArray(newPayload.properties.products, .); + const temporaryProductArray = newPayload.properties.products ?? $.createProductForStandardEcommEvent(.message.properties, .); + newPayload.properties.products = $.addProductArray(temporaryProductArray); newPayload.event = eventName; newPayload.token = ^.destination.Config.bluecoreNamespace; $.verifyPayload(newPayload, ^.message); diff --git a/src/cdk/v2/destinations/bluecore/utils.js b/src/cdk/v2/destinations/bluecore/utils.js index 220b43b916..5f71b05c7f 100644 --- a/src/cdk/v2/destinations/bluecore/utils.js +++ b/src/cdk/v2/destinations/bluecore/utils.js @@ -1,11 +1,18 @@ -const { InstrumentationError, isDefinedAndNotNullAndNotEmpty, getHashFromArrayWithDuplicate, isDefinedAndNotNull, isDefinedNotNullNotEmpty } = require("@rudderstack/integrations-lib"); -const { getFieldValueFromMessage, validateEventName, constructPayload } = require('../../../../v0/util'); -const { EVENT_NAME_MAPPING } = require("./config"); -const { EventType } = require('../../../../constants'); const { - MAPPING_CONFIG, - CONFIG_CATEGORIES, - } = require('./config'); + InstrumentationError, + isDefinedAndNotNullAndNotEmpty, + getHashFromArrayWithDuplicate, + isDefinedAndNotNull, + isDefinedNotNullNotEmpty, +} = require('@rudderstack/integrations-lib'); +const { + getFieldValueFromMessage, + validateEventName, + constructPayload, +} = require('../../../../v0/util'); +const { EVENT_NAME_MAPPING } = require('./config'); +const { EventType } = require('../../../../constants'); +const { MAPPING_CONFIG, CONFIG_CATEGORIES } = require('./config'); /** * Verifies the correctness of payload for different events. @@ -16,31 +23,45 @@ const { * @returns {void} */ const verifyPayload = (payload, message) => { - if (message.type === EventType.IDENTIFY && isDefinedNotNullNotEmpty(payload.event) && payload.event !== 'identify') { - throw new InstrumentationError('[Bluecore] traits.action must be \'identify\' for identify action'); - } - 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\' action'); - } - break; - default: - break; - } + if ( + message.type === EventType.IDENTIFY && + isDefinedNotNullNotEmpty(payload.event) && + payload.event !== 'identify' + ) { + throw new InstrumentationError( + "[Bluecore] traits.action must be 'identify' for identify action", + ); + } + 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' action", + ); + } + break; + default: + break; + } }; /** @@ -51,37 +72,40 @@ const verifyPayload = (payload, message) => { * @returns {string|array} - The deduced track event name. */ const deduceTrackEventName = (trackEventName, Config) => { - let eventName; - const { eventsMapping } = Config; - validateEventName(trackEventName); - /* + 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]; - const finalEvent = [...eventName]; - return finalEvent; - } - /* + if (eventsMapping.length > 0) { + const keyMap = getHashFromArrayWithDuplicate(eventsMapping, 'from', 'to', false); + eventName = keyMap[trackEventName]; + } + if (isDefinedAndNotNullAndNotEmpty(eventName)) { + const finalEvent = typeof eventName === 'string' ? [eventName] : [...eventName]; + return finalEvent; + } + + /* 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]; + 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]; + // Step 3: if nothing matches this is to be considered as a custom event + return [trackEventName]; }; /** @@ -91,9 +115,9 @@ const deduceTrackEventName = (trackEventName, Config) => { * @returns {boolean} - True if the event is a standard Bluecore event, false otherwise. */ const isStandardBluecoreEvent = (eventName) => { - const standardEventList = EVENT_NAME_MAPPING.map(item => item.dest); - return !!(standardEventList.includes(eventName)); -} + const standardEventList = EVENT_NAME_MAPPING.map((item) => item.dest); + return !!standardEventList.includes(eventName); +}; /** * Adds an array of products to a message. @@ -104,36 +128,52 @@ const isStandardBluecoreEvent = (eventName) => { * @throws {InstrumentationError} - If the products array is not defined or null. * @returns {array} - The updated product array. */ -const addProductArray = (products, eventName) => { - let finalProductArray = null; - if(eventName !== 'optin' && eventName !== 'unsubscribe' && eventName !== 'search') { - if (!isDefinedAndNotNull(products) && isStandardBluecoreEvent(eventName)) { - throw new InstrumentationError(`Product array is required for ${eventName} event`); - } - if (isDefinedAndNotNull(products)) { - const productArray = Array.isArray(products) ? products : [products]; - const mappedProductArray = productArray.map(({ product_id, query, order_id, total, ...rest }) => ({ - id: product_id, - ...rest - })); - finalProductArray = mappedProductArray; - } - } - - return finalProductArray; -} +const addProductArray = (products) => { + let finalProductArray = null; + if (isDefinedAndNotNull(products)) { + const productArray = Array.isArray(products) ? products : [products]; + const mappedProductArray = productArray.map( + ({ product_id, sku, id, query, order_id, total, ...rest }) => ({ + id: product_id || sku || id, + ...rest, + }), + ); + finalProductArray = mappedProductArray; + } + return finalProductArray; +}; +/** + * Constructs properties based on the given message. + * + * @param {object} message - The message object. + * @returns {object} - The constructed properties object. + */ const constructProperties = (message) => { - const category = CONFIG_CATEGORIES[message.type.toUpperCase()]; - const payload = constructPayload(message, MAPPING_CONFIG[category.name]); - return payload; - }; + const category = CONFIG_CATEGORIES[message.type.toUpperCase()]; + const payload = constructPayload(message, MAPPING_CONFIG[category.name]); + return payload; +}; -module.exports = { - verifyPayload, - deduceTrackEventName, - addProductArray, - isStandardBluecoreEvent, - constructProperties +/** + * Creates a product for a standard e-commerce event. + * + * @param {Object} properties - The properties of the product. + * @param {string} eventName - The name of the event. + * @returns {Array|null} - An array containing the properties if the event is a standard Bluecore event and not 'search', otherwise null. + */ +const createProductForStandardEcommEvent = (properties, eventName) => { + if (isStandardBluecoreEvent(eventName) && eventName !== 'search') { + return [properties]; + } + return null; }; +module.exports = { + verifyPayload, + deduceTrackEventName, + addProductArray, + isStandardBluecoreEvent, + constructProperties, + createProductForStandardEcommEvent, +}; diff --git a/src/cdk/v2/destinations/bluecore/utils.test.js b/src/cdk/v2/destinations/bluecore/utils.test.js index 8483b63e46..1f5357cfee 100644 --- a/src/cdk/v2/destinations/bluecore/utils.test.js +++ b/src/cdk/v2/destinations/bluecore/utils.test.js @@ -1,244 +1,234 @@ -const { addProductArray, verifyPayload, isStandardBluecoreEvent, deduceTrackEventName } = require('./utils') const { - InstrumentationError -} = require('@rudderstack/integrations-lib'); + addProductArray, + verifyPayload, + isStandardBluecoreEvent, + deduceTrackEventName, +} = require('./utils'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); describe('addProductArray', () => { - - // Adds an array of products to a message when products array is defined and not null. - it('should add an array of products to a message when products array is defined and not null', () => { - const products = [{ product_id: 1, name: 'Product 1' }, { product_id: 2, name: 'Product 2' }]; - const eventName = 'purchase'; - - const result = addProductArray(products, eventName); - - expect(result).toEqual([{ id: 1, name: 'Product 1' }, { id: 2, name: 'Product 2' }]); - }); - - // Adds a single product object to a message when a single product object is passed. - it('should add a single product object to a message when a single product object is passed', () => { - const product = { product_id: 1, name: 'Product 1' }; - const eventName = 'add_to_cart'; - - const result = addProductArray(product, eventName); - expect(result).toEqual([{ id: 1, name: 'Product 1' }]); - }); - - // Throws an InstrumentationError when products array is null. - it('should throw an InstrumentationError when products array is null for a standard bluecore event', () => { - const products = null; - const eventName = 'purchase'; - - expect(() => { - addProductArray(products, eventName); - }).toThrow(InstrumentationError); - }); - - it('should not throw an InstrumentationError for a custom event when products array is null', () => { - const message = {}; - const products = null; - const eventName = 'custom'; - - expect(() => { - addProductArray(message, products, eventName); - }).toBeNull; - }); + // Adds an array of products to a message when products array is defined and not null. + it('should add an array of products to a message when products array is defined and not null', () => { + const products = [ + { product_id: 1, name: 'Product 1' }, + { product_id: 2, name: 'Product 2' }, + ]; + const eventName = 'purchase'; + + const result = addProductArray(products, eventName); + + expect(result).toEqual([ + { id: 1, name: 'Product 1' }, + { id: 2, name: 'Product 2' }, + ]); + }); + + // Adds a single product object to a message when a single product object is passed. + it('should add a single product object to a message when a single product object is passed', () => { + const product = { product_id: 1, name: 'Product 1' }; + const eventName = 'add_to_cart'; + + const result = addProductArray(product, eventName); + expect(result).toEqual([{ id: 1, name: 'Product 1' }]); + }); + + it('should not throw an InstrumentationError for a custom event when products array is null', () => { + const message = {}; + const products = null; + const eventName = 'custom'; + + expect(() => { + addProductArray(message, products, eventName); + }).toBeNull; + }); }); describe('verifyPayload', () => { - - // Verify payload for search event with search_term property. - it('should verify payload for search event with search_term property', () => { - const payload = { - event: 'search', - properties: { - search_term: 'example' - } - }; - expect(() => verifyPayload(payload, {})).not.toThrow(); - }); - - // Verify payload for purchase event with order_id and total properties. - it('should verify payload for purchase event with order_id and total properties', () => { - const payload = { - event: 'purchase', - properties: { - order_id: '123', - total: 100 - } - }; - expect(() => verifyPayload(payload, {})).not.toThrow(); - }); - - // Verify payload for identify event with email property. - it('should verify payload for identify event with email property', () => { - const payload = { - event: 'identify', - properties: {} - }; - const message = { - traits: { - email: 'test@example.com' - } - }; - expect(() => verifyPayload(payload, message)).not.toThrow(); - }); - - // Verify payload for search event without search_term property, should throw an InstrumentationError. - it('should throw an InstrumentationError when verifying payload for search event without search_term property', () => { - const payload = { - event: 'search', - properties: {} - }; - expect(() => verifyPayload(payload, {})).toThrow(InstrumentationError); - }); - - // Verify payload for purchase event without order_id property, should throw an InstrumentationError. - it('should throw an InstrumentationError when verifying payload for purchase event without order_id property', () => { - const payload = { - event: 'purchase', - properties: { - total: 100 - } - }; - expect(() => verifyPayload(payload, {})).toThrow(InstrumentationError); - }); - - // Verify payload for purchase event without total property, should throw an InstrumentationError. - it('should throw an InstrumentationError when verifying payload for purchase event without total property', () => { - const payload = { - event: 'purchase', - properties: { - order_id: '123' - } - }; - expect(() => verifyPayload(payload, {})).toThrow(InstrumentationError); - }); - - // Verify payload for purchase event without total property, should throw an InstrumentationError. - it('should throw an InstrumentationError when verifying payload for identify event with action field other than identify', () => { - const payload = { - event: 'random', - properties: { - email: 'abc@gmail.com' - } - }; - expect(() => verifyPayload(payload, {type: 'identify'})).toThrow(InstrumentationError); - }); + // Verify payload for search event with search_term property. + it('should verify payload for search event with search_term property', () => { + const payload = { + event: 'search', + properties: { + search_term: 'example', + }, + }; + expect(() => verifyPayload(payload, {})).not.toThrow(); + }); + + // Verify payload for purchase event with order_id and total properties. + it('should verify payload for purchase event with order_id and total properties', () => { + const payload = { + event: 'purchase', + properties: { + order_id: '123', + total: 100, + }, + }; + expect(() => verifyPayload(payload, {})).not.toThrow(); + }); + + // Verify payload for identify event with email property. + it('should verify payload for identify event with email property', () => { + const payload = { + event: 'identify', + properties: {}, + }; + const message = { + traits: { + email: 'test@example.com', + }, + }; + expect(() => verifyPayload(payload, message)).not.toThrow(); + }); + + // Verify payload for search event without search_term property, should throw an InstrumentationError. + it('should throw an InstrumentationError when verifying payload for search event without search_term property', () => { + const payload = { + event: 'search', + properties: {}, + }; + expect(() => verifyPayload(payload, {})).toThrow(InstrumentationError); + }); + + // Verify payload for purchase event without order_id property, should throw an InstrumentationError. + it('should throw an InstrumentationError when verifying payload for purchase event without order_id property', () => { + const payload = { + event: 'purchase', + properties: { + total: 100, + }, + }; + expect(() => verifyPayload(payload, {})).toThrow(InstrumentationError); + }); + + // Verify payload for purchase event without total property, should throw an InstrumentationError. + it('should throw an InstrumentationError when verifying payload for purchase event without total property', () => { + const payload = { + event: 'purchase', + properties: { + order_id: '123', + }, + }; + expect(() => verifyPayload(payload, {})).toThrow(InstrumentationError); + }); + + // Verify payload for purchase event without total property, should throw an InstrumentationError. + it('should throw an InstrumentationError when verifying payload for identify event with action field other than identify', () => { + const payload = { + event: 'random', + properties: { + email: 'abc@gmail.com', + }, + }; + expect(() => verifyPayload(payload, { type: 'identify' })).toThrow(InstrumentationError); + }); }); describe('isStandardBluecoreEvent', () => { - - // Returns true if the given event name is in the list of standard Bluecore events. - it('should return true when the given event name is in the list of standard Bluecore events', () => { - const eventName = 'search'; - const result = isStandardBluecoreEvent(eventName); - expect(result).toBe(true); - }); - - // Returns false if the given event name is not in the list of standard Bluecore events. - it('should return false when the given event name is not in the list of standard Bluecore events', () => { - const eventName = 'someEvent'; - const result = isStandardBluecoreEvent(eventName); - expect(result).toBe(false); - }); - - // Returns false if the given event name is null. - it('should return false when the given event name is null', () => { - const eventName = null; - const result = isStandardBluecoreEvent(eventName); - expect(result).toBe(false); - }); - - // Returns false if the given event name is undefined. - it('should return false when the given event name is undefined', () => { - const eventName = undefined; - const result = isStandardBluecoreEvent(eventName); - expect(result).toBe(false); - }); - - // Returns false if the given event name is not a string. - it('should return false when the given event name is not a string', () => { - const eventName = 123; - const result = isStandardBluecoreEvent(eventName); - expect(result).toBe(false); - }); - - // Returns false if the given event name is an empty string. - it('should return false when the given event name is an empty string', () => { - const eventName = ''; - const result = isStandardBluecoreEvent(eventName); - expect(result).toBe(false); - }); + // Returns true if the given event name is in the list of standard Bluecore events. + it('should return true when the given event name is in the list of standard Bluecore events', () => { + const eventName = 'search'; + const result = isStandardBluecoreEvent(eventName); + expect(result).toBe(true); + }); + + // Returns false if the given event name is not in the list of standard Bluecore events. + it('should return false when the given event name is not in the list of standard Bluecore events', () => { + const eventName = 'someEvent'; + const result = isStandardBluecoreEvent(eventName); + expect(result).toBe(false); + }); + + // Returns false if the given event name is null. + it('should return false when the given event name is null', () => { + const eventName = null; + const result = isStandardBluecoreEvent(eventName); + expect(result).toBe(false); + }); + + // Returns false if the given event name is undefined. + it('should return false when the given event name is undefined', () => { + const eventName = undefined; + const result = isStandardBluecoreEvent(eventName); + expect(result).toBe(false); + }); + + // Returns false if the given event name is not a string. + it('should return false when the given event name is not a string', () => { + const eventName = 123; + const result = isStandardBluecoreEvent(eventName); + expect(result).toBe(false); + }); + + // Returns false if the given event name is an empty string. + it('should return false when the given event name is an empty string', () => { + const eventName = ''; + const result = isStandardBluecoreEvent(eventName); + expect(result).toBe(false); + }); }); describe('deduceTrackEventName', () => { - - // The function returns the trackEventName if no eventsMapping is provided and the trackEventName is not a standard Rudderstack ecommerce event. - it('should return the trackEventName when no eventsMapping is provided and the trackEventName is not a standard Rudderstack ecommerce event', () => { - const trackEventName = 'customEvent'; - const Config = { - eventsMapping: [] - }; - const result = deduceTrackEventName(trackEventName, Config); - expect(result).toEqual([trackEventName]) - }); - - // The function returns the corresponding event name from eventsMapping if the trackEventName is mapped to a standard bluecore event. - it('should return the corresponding event name from eventsMapping if the trackEventName is mapped to a standard bluecore event', () => { - const trackEventName = 'customEvent'; - const Config = { - eventsMapping: [ - { from: 'customEvent', to: 'search' } - ] - }; - const result = deduceTrackEventName(trackEventName, Config); - console.log(result); - expect(result).toEqual(['search']); - }); - - // The function returns the corresponding event name from eventsMapping if the trackEventName is mapped to a standard bluecore event. - it('should return the corresponding event name array from eventsMapping if the trackEventName is mapped to more than one standard bluecore events', () => { - const trackEventName = 'customEvent'; - const Config = { - eventsMapping: [ - { from: 'customEvent', to: 'search' }, - { from: 'customEvent', to: 'purchase' } - ] - }; - const result = deduceTrackEventName(trackEventName, Config); - console.log(result); - expect(result).toEqual(['search', 'purchase']); - }); - - // The function returns the corresponding standard Rudderstack ecommerce event name if the trackEventName is a standard bluecore event. - it('should return the corresponding standard Rudderstack ecommerce event name if the trackEventName is a standard bluecore event', () => { - const trackEventName = 'Product Added to Wishlist'; - const Config = { - eventsMapping: [] - }; - const result = deduceTrackEventName(trackEventName, Config); - expect(result).toEqual(['wishlist']); - }); - - // The function throws an error if the trackEventName is not a string. - it('should throw an error if the trackEventName is not a string', () => { - const trackEventName = 123; - const Config = { - eventsMapping: [] - }; - expect(() => deduceTrackEventName(trackEventName, Config)).toThrow(); - }); - - // The function throws an error if the trackEventName is an empty string. - it('should throw an error if the trackEventName is an empty string', () => { - const trackEventName = ''; - const Config = { - eventsMapping: [] - }; - expect(() => deduceTrackEventName(trackEventName, Config)).toThrow(); - }); + // The function returns the trackEventName if no eventsMapping is provided and the trackEventName is not a standard Rudderstack ecommerce event. + it('should return the trackEventName when no eventsMapping is provided and the trackEventName is not a standard Rudderstack ecommerce event', () => { + const trackEventName = 'customEvent'; + const Config = { + eventsMapping: [], + }; + const result = deduceTrackEventName(trackEventName, Config); + expect(result).toEqual([trackEventName]); + }); + + // The function returns the corresponding event name from eventsMapping if the trackEventName is mapped to a standard bluecore event. + it('should return the corresponding event name from eventsMapping if the trackEventName is mapped to a standard bluecore event', () => { + const trackEventName = 'customEvent'; + const Config = { + eventsMapping: [{ from: 'customEvent', to: 'search' }], + }; + const result = deduceTrackEventName(trackEventName, Config); + console.log(result); + expect(result).toEqual(['search']); + }); + + // The function returns the corresponding event name from eventsMapping if the trackEventName is mapped to a standard bluecore event. + it('should return the corresponding event name array from eventsMapping if the trackEventName is mapped to more than one standard bluecore events', () => { + const trackEventName = 'customEvent'; + const Config = { + eventsMapping: [ + { from: 'customEvent', to: 'search' }, + { from: 'customEvent', to: 'purchase' }, + ], + }; + const result = deduceTrackEventName(trackEventName, Config); + console.log(result); + expect(result).toEqual(['search', 'purchase']); + }); + + // The function returns the corresponding standard Rudderstack ecommerce event name if the trackEventName is a standard bluecore event. + it('should return the corresponding standard Rudderstack ecommerce event name if the trackEventName is a standard bluecore event', () => { + const trackEventName = 'Product Added to Wishlist'; + const Config = { + eventsMapping: [], + }; + const result = deduceTrackEventName(trackEventName, Config); + expect(result).toEqual(['wishlist']); + }); + + // The function throws an error if the trackEventName is not a string. + it('should throw an error if the trackEventName is not a string', () => { + const trackEventName = 123; + const Config = { + eventsMapping: [], + }; + expect(() => deduceTrackEventName(trackEventName, Config)).toThrow(); + }); + + // The function throws an error if the trackEventName is an empty string. + it('should throw an error if the trackEventName is an empty string', () => { + const trackEventName = ''; + const Config = { + eventsMapping: [], + }; + expect(() => deduceTrackEventName(trackEventName, Config)).toThrow(); + }); }); - - - diff --git a/src/v0/destinations/bluecore/data/bluecoreTrackConfig.json b/src/v0/destinations/bluecore/data/bluecoreTrackConfig.json index 85680bcb3f..3e7f641d32 100644 --- a/src/v0/destinations/bluecore/data/bluecoreTrackConfig.json +++ b/src/v0/destinations/bluecore/data/bluecoreTrackConfig.json @@ -1,11 +1,7 @@ [ { "destKey": "properties.distinct_id", - "sourceKeys": [ - "userId", - "anonymousId", - "email" - ], + "sourceKeys": ["userId", "anonymousId", "email"], "required": true }, { @@ -28,20 +24,12 @@ }, { "destKey": "properties.customer.age", - "sourceKeys": [ - "context.traits.age", - "traits.age" - ], + "sourceKeys": ["context.traits.age", "traits.age"], "required": false }, { "destKey": "properties.customer.sex", - "sourceKeys": [ - "traits.gender", - "context.traits.gender", - "traits.sex", - "context.traits.sex" - ], + "sourceKeys": ["traits.gender", "context.traits.gender", "traits.sex", "context.traits.sex"], "required": false }, { @@ -83,10 +71,7 @@ }, { "destKey": "properties.products", - "sourceKeys": [ - "properties.products", - "properties" - ], + "sourceKeys": ["properties.products"], "required": false } -] \ No newline at end of file +] diff --git a/src/v0/destinations/bluecore/transform.js b/src/v0/destinations/bluecore/transform.js index a85d6309d7..6a76c8c84d 100644 --- a/src/v0/destinations/bluecore/transform.js +++ b/src/v0/destinations/bluecore/transform.js @@ -1,26 +1,32 @@ -const { isDefinedAndNotNull, ConfigurationError, TransformationError, +const { + isDefinedAndNotNull, + ConfigurationError, + TransformationError, InstrumentationError, - removeUndefinedAndNullValues, } = require('@rudderstack/integrations-lib'); +} = require('@rudderstack/integrations-lib'); const { EventType } = require('../../../constants'); const { constructPayload, ErrorMessage, defaultRequestConfig, simpleProcessRouterDest, + removeUndefinedNullValuesAndEmptyObjectArray, } = require('../../util'); +const { MAPPING_CONFIG, CONFIG_CATEGORIES, BASE_URL } = require('./config'); const { - MAPPING_CONFIG, - CONFIG_CATEGORIES, - BASE_URL, -} = require('./config'); -const { verifyPayload, deduceTrackEventName, addProductArray } = require('./util'); - - + verifyPayload, + deduceTrackEventName, + addProductArray, + createProductForStandardEcommEvent, +} = require('./util'); const trackResponseBuilder = (message, category, { Config }, eventName) => { const payload = constructPayload(message, MAPPING_CONFIG[category.name]); - payload.properties.products = addProductArray(payload.properties.products, eventName); + const temporaryProductArray = + payload.properties.products || + createProductForStandardEcommEvent(message.properties, eventName); + payload.properties.products = addProductArray(temporaryProductArray); payload.event = eventName; verifyPayload(payload, message); payload.token = Config.bluecoreNamespace; @@ -28,7 +34,7 @@ const trackResponseBuilder = (message, category, { Config }, eventName) => { // fail-safety for developer error throw new TransformationError(ErrorMessage.FailedToConstructPayload); } - return removeUndefinedAndNullValues(payload); + return removeUndefinedNullValuesAndEmptyObjectArray(payload); }; const identifyResponseBuilder = (message, category, destination) => { @@ -58,7 +64,7 @@ const responseBuilderSimple = (response) => { 'Content-Type': 'application/json', }; return resp; -} +}; const process = async (event) => { const deducedEventNameArray = []; @@ -70,7 +76,9 @@ const process = async (event) => { } if (!destination.Config.bluecoreNamespace) { - throw new ConfigurationError('[BLUECORE] bluecore account namespace required for Authentication.'); + throw new ConfigurationError( + '[BLUECORE] bluecore account namespace required for Authentication.', + ); } const messageType = message.type.toLowerCase(); const category = CONFIG_CATEGORIES[message.type.toUpperCase()]; @@ -99,4 +107,4 @@ const processRouterDest = async (inputs, reqMetadata) => { return respList; }; -module.exports = { process, processRouterDest, trackResponseBuilder, identifyResponseBuilder }; \ No newline at end of file +module.exports = { process, processRouterDest, trackResponseBuilder, identifyResponseBuilder }; diff --git a/src/v0/destinations/bluecore/transform.test.js b/src/v0/destinations/bluecore/transform.test.js index 62b78bf5d3..b98c7d7189 100644 --- a/src/v0/destinations/bluecore/transform.test.js +++ b/src/v0/destinations/bluecore/transform.test.js @@ -1,23 +1,25 @@ const { trackResponseBuilder, identifyResponseBuilder } = require('./transform'); -const { - InstrumentationError -} = require('@rudderstack/integrations-lib'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); const commonIdentifyMappingCategory = { name: 'bluecoreIdentifyConfig', type: 'identify', -} +}; const commonTrackMappingCategory = { name: 'bluecoreTrackConfig', type: 'track', }; describe('trackResponseBuilder', () => { - // Given a valid message, category, destination config, and event name, the function should construct a payload with the correct event name and token it('should construct a payload with the correct event name and token', () => { - const message = { type: 'track', event: 'purchase', properties: { total: 20, order_id: 123, products: [{ product_id: 12, name: "new prod" }] } }; - const category = commonTrackMappingCategory + const message = { + type: 'track', + event: 'purchase', + userId: 123, + properties: { total: 20, order_id: 123, products: [{ product_id: 12, name: 'new prod' }] }, + }; + const category = commonTrackMappingCategory; const destination = { Config: { bluecoreNamespace: 'namespace' } }; const eventName = 'purchase'; @@ -25,14 +27,15 @@ describe('trackResponseBuilder', () => { event: 'purchase', token: 'namespace', properties: { - "order_id": 123, - "products": [ + distinct_id: 123, + order_id: 123, + products: [ { - "id": 12, - "name": "new prod", + id: 12, + name: 'new prod', }, ], - "total": 20, + total: 20, }, }; @@ -43,8 +46,13 @@ describe('trackResponseBuilder', () => { // When the event name is 'optin', 'unsubscribe', or 'search', the function should not add a product array to the payload it('need not add a product array to the payload when the event name is "optin"', () => { - const message = { type: 'track', event: 'optin', properties: { total: 20, order_id: 123, } }; - const category = commonTrackMappingCategory + const message = { + type: 'track', + userId: 123, + event: 'optin', + properties: { total: 20, order_id: 123 }, + }; + const category = commonTrackMappingCategory; const destination = { Config: { bluecoreNamespace: 'namespace' } }; const eventName = 'optin'; @@ -52,6 +60,7 @@ describe('trackResponseBuilder', () => { event: 'optin', token: 'namespace', properties: { + distinct_id: 123, order_id: 123, total: 20, }, @@ -64,8 +73,13 @@ describe('trackResponseBuilder', () => { // When the event name is not 'optin', 'unsubscribe', or 'search', the function should add a product array to the payload it('should add a product array to the payload when the event name is not "optin", "unsubscribe", or "search"', () => { - const message = { type: 'track', event: 'purchase', properties: { order_id: 123, total: 20, products: [{ id: '1', name: 'product' }] } }; - const category = commonTrackMappingCategory + const message = { + type: 'track', + userId: 123, + event: 'purchase', + properties: { order_id: 123, total: 20, products: [{ id: '1', name: 'product' }] }, + }; + const category = commonTrackMappingCategory; const destination = { Config: { bluecoreNamespace: 'namespace' } }; const eventName = 'purchase'; @@ -73,10 +87,11 @@ describe('trackResponseBuilder', () => { event: 'purchase', token: 'namespace', properties: { + distinct_id: 123, order_id: 123, total: 20, - products: [{ id: '1', name: 'product' }] - } + products: [{ id: '1', name: 'product' }], + }, }; const result = trackResponseBuilder(message, category, destination, eventName); @@ -86,10 +101,13 @@ describe('trackResponseBuilder', () => { }); describe('identifyResponseBuilder', () => { - // Given a valid message, category, destination config, and event name, the function should construct a payload with the correct event name and token it('should construct a payload with the correct event name and token', () => { - const message = { type: 'identify', userId: '123', traits: { email: 'abc@gmail.com', name: 'test' } }; + const message = { + type: 'identify', + userId: '123', + traits: { email: 'abc@gmail.com', name: 'test' }, + }; const category = commonIdentifyMappingCategory; const destination = { Config: { bluecoreNamespace: 'namespace' } }; @@ -97,11 +115,11 @@ describe('identifyResponseBuilder', () => { event: 'customer_patch', token: 'namespace', properties: { - "customer": { - "email": "abc@gmail.com", - "name": "test", + distinct_id: 'abc@gmail.com', + customer: { + email: 'abc@gmail.com', + name: 'test', }, - "distinct_id": "abc@gmail.com", }, }; @@ -111,7 +129,11 @@ describe('identifyResponseBuilder', () => { }); it('should construct a payload with the identify event name and token', () => { - const message = { type: 'identify', userId: '123', traits: { email: 'abc@gmail.com', name: 'test', action: 'identify' } }; + const message = { + type: 'identify', + userId: '123', + traits: { email: 'abc@gmail.com', name: 'test', action: 'identify' }, + }; const category = commonIdentifyMappingCategory; const destination = { Config: { bluecoreNamespace: 'namespace' } }; @@ -119,11 +141,11 @@ describe('identifyResponseBuilder', () => { event: 'identify', token: 'namespace', properties: { - "customer": { - "email": "abc@gmail.com", - "name": "test", + customer: { + email: 'abc@gmail.com', + name: 'test', }, - "distinct_id": "abc@gmail.com", + distinct_id: 'abc@gmail.com', }, }; @@ -133,7 +155,11 @@ describe('identifyResponseBuilder', () => { }); it('should construct a payload with the identify event name and token', () => { - const message = { type: 'identify', userId: '123', traits: { email: 'abc@gmail.com', name: 'test', action: 'random' } }; + const message = { + type: 'identify', + userId: '123', + traits: { email: 'abc@gmail.com', name: 'test', action: 'random' }, + }; const category = commonIdentifyMappingCategory; const destination = { Config: { bluecoreNamespace: 'namespace' } }; @@ -142,5 +168,3 @@ describe('identifyResponseBuilder', () => { }).toThrow(InstrumentationError); }); }); - - diff --git a/src/v0/destinations/bluecore/util.js b/src/v0/destinations/bluecore/util.js index a7b3d30908..ee16b84993 100644 --- a/src/v0/destinations/bluecore/util.js +++ b/src/v0/destinations/bluecore/util.js @@ -1,6 +1,12 @@ -const { InstrumentationError, isDefinedAndNotNullAndNotEmpty, getHashFromArrayWithDuplicate, isDefinedAndNotNull, isDefinedNotNullNotEmpty } = require("@rudderstack/integrations-lib"); -const { getFieldValueFromMessage, validateEventName } = require("../../util"); -const { EVENT_NAME_MAPPING } = require("./config"); +const { + InstrumentationError, + isDefinedAndNotNullAndNotEmpty, + getHashFromArrayWithDuplicate, + isDefinedAndNotNull, + isDefinedNotNullNotEmpty, +} = require('@rudderstack/integrations-lib'); +const { getFieldValueFromMessage, validateEventName } = require('../../util'); +const { EVENT_NAME_MAPPING } = require('./config'); const { EventType } = require('../../../constants'); /** @@ -12,31 +18,45 @@ const { EventType } = require('../../../constants'); * @returns {void} */ const verifyPayload = (payload, message) => { - if (message.type === EventType.IDENTIFY && isDefinedNotNullNotEmpty(payload.event) && payload.event !== 'identify') { - throw new InstrumentationError('[Bluecore] traits.action must be \'identify\' for identify action'); - } - 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\' action'); - } - break; - default: - break; - } + if ( + message.type === EventType.IDENTIFY && + isDefinedNotNullNotEmpty(payload.event) && + payload.event !== 'identify' + ) { + throw new InstrumentationError( + "[Bluecore] traits.action must be 'identify' for identify action", + ); + } + 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' action", + ); + } + break; + default: + break; + } }; /** @@ -47,37 +67,40 @@ const verifyPayload = (payload, message) => { * @returns {string|array} - The deduced track event name. */ const deduceTrackEventName = (trackEventName, Config) => { - let eventName; - const { eventsMapping } = Config; - validateEventName(trackEventName); - /* + 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]; - const finalEvent = [...eventName]; - return finalEvent; - } - /* + if (eventsMapping.length > 0) { + const keyMap = getHashFromArrayWithDuplicate(eventsMapping, 'from', 'to', false); + eventName = keyMap[trackEventName]; + } + if (isDefinedAndNotNullAndNotEmpty(eventName)) { + const finalEvent = typeof eventName === 'string' ? [eventName] : [...eventName]; + return finalEvent; + } + + /* 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]; + 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]; + // Step 3: if nothing matches this is to be considered as a custom event + return [trackEventName]; }; /** @@ -87,9 +110,9 @@ const deduceTrackEventName = (trackEventName, Config) => { * @returns {boolean} - True if the event is a standard Bluecore event, false otherwise. */ const isStandardBluecoreEvent = (eventName) => { - const standardEventList = EVENT_NAME_MAPPING.map(item => item.dest); - return !!(standardEventList.includes(eventName)); -} + const standardEventList = EVENT_NAME_MAPPING.map((item) => item.dest); + return !!standardEventList.includes(eventName); +}; /** * Adds an array of products to a message. @@ -100,29 +123,34 @@ const isStandardBluecoreEvent = (eventName) => { * @throws {InstrumentationError} - If the products array is not defined or null. * @returns {array} - The updated product array. */ -const addProductArray = (products, eventName) => { - let finalProductArray = null; - if(eventName !== 'optin' && eventName !== 'unsubscribe' && eventName !== 'search') { - if (!isDefinedAndNotNull(products) && isStandardBluecoreEvent(eventName)) { - throw new InstrumentationError(`Product array is required for ${eventName} event`); - } - if (isDefinedAndNotNull(products)) { - const productArray = Array.isArray(products) ? products : [products]; - const mappedProductArray = productArray.map(({ product_id, query, order_id, total, ...rest }) => ({ - id: product_id, - ...rest - })); - finalProductArray = mappedProductArray; - } - } - - return finalProductArray; -} +const addProductArray = (products) => { + let finalProductArray = null; + if (isDefinedAndNotNull(products)) { + const productArray = Array.isArray(products) ? products : [products]; + const mappedProductArray = productArray.map( + ({ product_id, sku, id, query, order_id, total, ...rest }) => ({ + id: product_id || sku || id, + ...rest, + }), + ); + finalProductArray = mappedProductArray; + } + // } -module.exports = { - verifyPayload, - deduceTrackEventName, - addProductArray, - isStandardBluecoreEvent + return finalProductArray; +}; + +const createProductForStandardEcommEvent = (properties, eventName) => { + if (isStandardBluecoreEvent(eventName) && eventName !== 'search') { + return [properties]; + } + return null; }; +module.exports = { + verifyPayload, + deduceTrackEventName, + addProductArray, + isStandardBluecoreEvent, + createProductForStandardEcommEvent, +}; diff --git a/src/v0/destinations/bluecore/util.test.js b/src/v0/destinations/bluecore/util.test.js index 778cb80a91..7d3c2431bb 100644 --- a/src/v0/destinations/bluecore/util.test.js +++ b/src/v0/destinations/bluecore/util.test.js @@ -1,244 +1,234 @@ -const { addProductArray, verifyPayload, isStandardBluecoreEvent, deduceTrackEventName } = require('./util') const { - InstrumentationError -} = require('@rudderstack/integrations-lib'); + addProductArray, + verifyPayload, + isStandardBluecoreEvent, + deduceTrackEventName, +} = require('./util'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); describe('addProductArray', () => { - - // Adds an array of products to a message when products array is defined and not null. - it('should add an array of products to a message when products array is defined and not null', () => { - const products = [{ product_id: 1, name: 'Product 1' }, { product_id: 2, name: 'Product 2' }]; - const eventName = 'purchase'; - - const result = addProductArray(products, eventName); - - expect(result).toEqual([{ id: 1, name: 'Product 1' }, { id: 2, name: 'Product 2' }]); - }); - - // Adds a single product object to a message when a single product object is passed. - it('should add a single product object to a message when a single product object is passed', () => { - const product = { product_id: 1, name: 'Product 1' }; - const eventName = 'add_to_cart'; - - const result = addProductArray(product, eventName); - expect(result).toEqual([{ id: 1, name: 'Product 1' }]); - }); - - // Throws an InstrumentationError when products array is null. - it('should throw an InstrumentationError when products array is null for a standard bluecore event', () => { - const products = null; - const eventName = 'purchase'; - - expect(() => { - addProductArray(products, eventName); - }).toThrow(InstrumentationError); - }); - - it('should not throw an InstrumentationError for a custom event when products array is null', () => { - const message = {}; - const products = null; - const eventName = 'custom'; - - expect(() => { - addProductArray(message, products, eventName); - }).toBeNull; - }); + // Adds an array of products to a message when products array is defined and not null. + it('should add an array of products to a message when products array is defined and not null', () => { + const products = [ + { product_id: 1, name: 'Product 1' }, + { product_id: 2, name: 'Product 2' }, + ]; + const eventName = 'purchase'; + + const result = addProductArray(products, eventName); + + expect(result).toEqual([ + { id: 1, name: 'Product 1' }, + { id: 2, name: 'Product 2' }, + ]); + }); + + // Adds a single product object to a message when a single product object is passed. + it('should add a single product object to a message when a single product object is passed', () => { + const product = { product_id: 1, name: 'Product 1' }; + const eventName = 'add_to_cart'; + + const result = addProductArray(product, eventName); + expect(result).toEqual([{ id: 1, name: 'Product 1' }]); + }); + + it('should not throw an InstrumentationError for a custom event when products array is null', () => { + const message = {}; + const products = null; + const eventName = 'custom'; + + expect(() => { + addProductArray(message, products, eventName); + }).toBeNull; + }); }); describe('verifyPayload', () => { - - // Verify payload for search event with search_term property. - it('should verify payload for search event with search_term property', () => { - const payload = { - event: 'search', - properties: { - search_term: 'example' - } - }; - expect(() => verifyPayload(payload, {})).not.toThrow(); - }); - - // Verify payload for purchase event with order_id and total properties. - it('should verify payload for purchase event with order_id and total properties', () => { - const payload = { - event: 'purchase', - properties: { - order_id: '123', - total: 100 - } - }; - expect(() => verifyPayload(payload, {})).not.toThrow(); - }); - - // Verify payload for identify event with email property. - it('should verify payload for identify event with email property', () => { - const payload = { - event: 'identify', - properties: {} - }; - const message = { - traits: { - email: 'test@example.com' - } - }; - expect(() => verifyPayload(payload, message)).not.toThrow(); - }); - - // Verify payload for search event without search_term property, should throw an InstrumentationError. - it('should throw an InstrumentationError when verifying payload for search event without search_term property', () => { - const payload = { - event: 'search', - properties: {} - }; - expect(() => verifyPayload(payload, {})).toThrow(InstrumentationError); - }); - - // Verify payload for purchase event without order_id property, should throw an InstrumentationError. - it('should throw an InstrumentationError when verifying payload for purchase event without order_id property', () => { - const payload = { - event: 'purchase', - properties: { - total: 100 - } - }; - expect(() => verifyPayload(payload, {})).toThrow(InstrumentationError); - }); - - // Verify payload for purchase event without total property, should throw an InstrumentationError. - it('should throw an InstrumentationError when verifying payload for purchase event without total property', () => { - const payload = { - event: 'purchase', - properties: { - order_id: '123' - } - }; - expect(() => verifyPayload(payload, {})).toThrow(InstrumentationError); - }); - - // Verify payload for purchase event without total property, should throw an InstrumentationError. - it('should throw an InstrumentationError when verifying payload for identify event with action field other than identify', () => { - const payload = { - event: 'random', - properties: { - email: 'abc@gmail.com' - } - }; - expect(() => verifyPayload(payload, {type: 'identify'})).toThrow(InstrumentationError); - }); + // Verify payload for search event with search_term property. + it('should verify payload for search event with search_term property', () => { + const payload = { + event: 'search', + properties: { + search_term: 'example', + }, + }; + expect(() => verifyPayload(payload, {})).not.toThrow(); + }); + + // Verify payload for purchase event with order_id and total properties. + it('should verify payload for purchase event with order_id and total properties', () => { + const payload = { + event: 'purchase', + properties: { + order_id: '123', + total: 100, + }, + }; + expect(() => verifyPayload(payload, {})).not.toThrow(); + }); + + // Verify payload for identify event with email property. + it('should verify payload for identify event with email property', () => { + const payload = { + event: 'identify', + properties: {}, + }; + const message = { + traits: { + email: 'test@example.com', + }, + }; + expect(() => verifyPayload(payload, message)).not.toThrow(); + }); + + // Verify payload for search event without search_term property, should throw an InstrumentationError. + it('should throw an InstrumentationError when verifying payload for search event without search_term property', () => { + const payload = { + event: 'search', + properties: {}, + }; + expect(() => verifyPayload(payload, {})).toThrow(InstrumentationError); + }); + + // Verify payload for purchase event without order_id property, should throw an InstrumentationError. + it('should throw an InstrumentationError when verifying payload for purchase event without order_id property', () => { + const payload = { + event: 'purchase', + properties: { + total: 100, + }, + }; + expect(() => verifyPayload(payload, {})).toThrow(InstrumentationError); + }); + + // Verify payload for purchase event without total property, should throw an InstrumentationError. + it('should throw an InstrumentationError when verifying payload for purchase event without total property', () => { + const payload = { + event: 'purchase', + properties: { + order_id: '123', + }, + }; + expect(() => verifyPayload(payload, {})).toThrow(InstrumentationError); + }); + + // Verify payload for purchase event without total property, should throw an InstrumentationError. + it('should throw an InstrumentationError when verifying payload for identify event with action field other than identify', () => { + const payload = { + event: 'random', + properties: { + email: 'abc@gmail.com', + }, + }; + expect(() => verifyPayload(payload, { type: 'identify' })).toThrow(InstrumentationError); + }); }); describe('isStandardBluecoreEvent', () => { - - // Returns true if the given event name is in the list of standard Bluecore events. - it('should return true when the given event name is in the list of standard Bluecore events', () => { - const eventName = 'search'; - const result = isStandardBluecoreEvent(eventName); - expect(result).toBe(true); - }); - - // Returns false if the given event name is not in the list of standard Bluecore events. - it('should return false when the given event name is not in the list of standard Bluecore events', () => { - const eventName = 'someEvent'; - const result = isStandardBluecoreEvent(eventName); - expect(result).toBe(false); - }); - - // Returns false if the given event name is null. - it('should return false when the given event name is null', () => { - const eventName = null; - const result = isStandardBluecoreEvent(eventName); - expect(result).toBe(false); - }); - - // Returns false if the given event name is undefined. - it('should return false when the given event name is undefined', () => { - const eventName = undefined; - const result = isStandardBluecoreEvent(eventName); - expect(result).toBe(false); - }); - - // Returns false if the given event name is not a string. - it('should return false when the given event name is not a string', () => { - const eventName = 123; - const result = isStandardBluecoreEvent(eventName); - expect(result).toBe(false); - }); - - // Returns false if the given event name is an empty string. - it('should return false when the given event name is an empty string', () => { - const eventName = ''; - const result = isStandardBluecoreEvent(eventName); - expect(result).toBe(false); - }); + // Returns true if the given event name is in the list of standard Bluecore events. + it('should return true when the given event name is in the list of standard Bluecore events', () => { + const eventName = 'search'; + const result = isStandardBluecoreEvent(eventName); + expect(result).toBe(true); + }); + + // Returns false if the given event name is not in the list of standard Bluecore events. + it('should return false when the given event name is not in the list of standard Bluecore events', () => { + const eventName = 'someEvent'; + const result = isStandardBluecoreEvent(eventName); + expect(result).toBe(false); + }); + + // Returns false if the given event name is null. + it('should return false when the given event name is null', () => { + const eventName = null; + const result = isStandardBluecoreEvent(eventName); + expect(result).toBe(false); + }); + + // Returns false if the given event name is undefined. + it('should return false when the given event name is undefined', () => { + const eventName = undefined; + const result = isStandardBluecoreEvent(eventName); + expect(result).toBe(false); + }); + + // Returns false if the given event name is not a string. + it('should return false when the given event name is not a string', () => { + const eventName = 123; + const result = isStandardBluecoreEvent(eventName); + expect(result).toBe(false); + }); + + // Returns false if the given event name is an empty string. + it('should return false when the given event name is an empty string', () => { + const eventName = ''; + const result = isStandardBluecoreEvent(eventName); + expect(result).toBe(false); + }); }); describe('deduceTrackEventName', () => { - - // The function returns the trackEventName if no eventsMapping is provided and the trackEventName is not a standard Rudderstack ecommerce event. - it('should return the trackEventName when no eventsMapping is provided and the trackEventName is not a standard Rudderstack ecommerce event', () => { - const trackEventName = 'customEvent'; - const Config = { - eventsMapping: [] - }; - const result = deduceTrackEventName(trackEventName, Config); - expect(result).toEqual([trackEventName]) - }); - - // The function returns the corresponding event name from eventsMapping if the trackEventName is mapped to a standard bluecore event. - it('should return the corresponding event name from eventsMapping if the trackEventName is mapped to a standard bluecore event', () => { - const trackEventName = 'customEvent'; - const Config = { - eventsMapping: [ - { from: 'customEvent', to: 'search' } - ] - }; - const result = deduceTrackEventName(trackEventName, Config); - console.log(result); - expect(result).toEqual(['search']); - }); - - // The function returns the corresponding event name from eventsMapping if the trackEventName is mapped to a standard bluecore event. - it('should return the corresponding event name array from eventsMapping if the trackEventName is mapped to more than one standard bluecore events', () => { - const trackEventName = 'customEvent'; - const Config = { - eventsMapping: [ - { from: 'customEvent', to: 'search' }, - { from: 'customEvent', to: 'purchase' } - ] - }; - const result = deduceTrackEventName(trackEventName, Config); - console.log(result); - expect(result).toEqual(['search', 'purchase']); - }); - - // The function returns the corresponding standard Rudderstack ecommerce event name if the trackEventName is a standard bluecore event. - it('should return the corresponding standard Rudderstack ecommerce event name if the trackEventName is a standard bluecore event', () => { - const trackEventName = 'Product Added to Wishlist'; - const Config = { - eventsMapping: [] - }; - const result = deduceTrackEventName(trackEventName, Config); - expect(result).toEqual(['wishlist']); - }); - - // The function throws an error if the trackEventName is not a string. - it('should throw an error if the trackEventName is not a string', () => { - const trackEventName = 123; - const Config = { - eventsMapping: [] - }; - expect(() => deduceTrackEventName(trackEventName, Config)).toThrow(); - }); - - // The function throws an error if the trackEventName is an empty string. - it('should throw an error if the trackEventName is an empty string', () => { - const trackEventName = ''; - const Config = { - eventsMapping: [] - }; - expect(() => deduceTrackEventName(trackEventName, Config)).toThrow(); - }); + // The function returns the trackEventName if no eventsMapping is provided and the trackEventName is not a standard Rudderstack ecommerce event. + it('should return the trackEventName when no eventsMapping is provided and the trackEventName is not a standard Rudderstack ecommerce event', () => { + const trackEventName = 'customEvent'; + const Config = { + eventsMapping: [], + }; + const result = deduceTrackEventName(trackEventName, Config); + expect(result).toEqual([trackEventName]); + }); + + // The function returns the corresponding event name from eventsMapping if the trackEventName is mapped to a standard bluecore event. + it('should return the corresponding event name from eventsMapping if the trackEventName is mapped to a standard bluecore event', () => { + const trackEventName = 'customEvent'; + const Config = { + eventsMapping: [{ from: 'customEvent', to: 'search' }], + }; + const result = deduceTrackEventName(trackEventName, Config); + console.log(result); + expect(result).toEqual(['search']); + }); + + // The function returns the corresponding event name from eventsMapping if the trackEventName is mapped to a standard bluecore event. + it('should return the corresponding event name array from eventsMapping if the trackEventName is mapped to more than one standard bluecore events', () => { + const trackEventName = 'customEvent'; + const Config = { + eventsMapping: [ + { from: 'customEvent', to: 'search' }, + { from: 'customEvent', to: 'purchase' }, + ], + }; + const result = deduceTrackEventName(trackEventName, Config); + console.log(result); + expect(result).toEqual(['search', 'purchase']); + }); + + // The function returns the corresponding standard Rudderstack ecommerce event name if the trackEventName is a standard bluecore event. + it('should return the corresponding standard Rudderstack ecommerce event name if the trackEventName is a standard bluecore event', () => { + const trackEventName = 'Product Added to Wishlist'; + const Config = { + eventsMapping: [], + }; + const result = deduceTrackEventName(trackEventName, Config); + expect(result).toEqual(['wishlist']); + }); + + // The function throws an error if the trackEventName is not a string. + it('should throw an error if the trackEventName is not a string', () => { + const trackEventName = 123; + const Config = { + eventsMapping: [], + }; + expect(() => deduceTrackEventName(trackEventName, Config)).toThrow(); + }); + + // The function throws an error if the trackEventName is an empty string. + it('should throw an error if the trackEventName is an empty string', () => { + const trackEventName = ''; + const Config = { + eventsMapping: [], + }; + expect(() => deduceTrackEventName(trackEventName, Config)).toThrow(); + }); }); - - - diff --git a/test/integrations/destinations/bluecore/data.ts b/test/integrations/destinations/bluecore/data.ts index 981371394e..c2205c25a1 100644 --- a/test/integrations/destinations/bluecore/data.ts +++ b/test/integrations/destinations/bluecore/data.ts @@ -1,11 +1,6 @@ -// importmport { ecomTestData } from './ecomTestData'; +import { ecomTestData } from './ecommTestData'; import { identifyData } from './identifyTestData'; -// import { trackTestData } from './trackTestData'; +import { trackTestData } from './trackTestData'; import { validationTestData } from './validationTestData'; -export const data = [ - ...identifyData, -// ...trackTestData, -// ...ecomTestData, - ...validationTestData -]; +export const data = [...identifyData, ...trackTestData, ...ecomTestData, ...validationTestData]; diff --git a/test/integrations/destinations/bluecore/ecommTestData.ts b/test/integrations/destinations/bluecore/ecommTestData.ts new file mode 100644 index 0000000000..4b8f9ddd69 --- /dev/null +++ b/test/integrations/destinations/bluecore/ecommTestData.ts @@ -0,0 +1,407 @@ +import { generateSimplifiedTrackPayload, transformResultBuilder } from '../../testUtils'; + +const destination = { + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'BLUECORE', + Config: { + bluecoreNamespace: 'dummy_sandbox', + eventsMapping: [ + { + from: 'ABC Searched', + to: 'search', + }, + { + from: 'testPurchase', + to: 'purchase', + }, + { + from: 'testboth', + to: 'wishlist', + }, + { + from: 'testboth', + to: 'add_to_cart', + }, + ], + }, + Enabled: true, + Transformations: [], +}; + +const commonTraits = { + id: 'user@1', + age: '22', + anonymousId: '9c6bd77ea9da3e68', +}; + +const commonPropsWithProducts = { + property1: 'value1', + property2: 'value2', + products: [ + { + product_id: '123', + sku: 'sku123', + name: 'Product 1', + price: 100, + quantity: 2, + }, + { + product_id: '124', + sku: 'sku124', + name: 'Product 2', + price: 200, + quantity: 3, + }, + ], +}; + +const commonPropsWithoutProducts = { + property1: 'value1', + property2: 'value2', + product_id: '123', +}; + +const commonOutputHeaders = { + 'Content-Type': 'application/json', +}; + +const eventEndPoint = 'https://api.bluecore.com/api/track/mobile/v1'; + +export const ecomTestData = [ + { + id: 'bluecore-track-test-1', + name: 'bluecore', + description: + 'Track event call with custom event mapped in destination config to purchase event. This will fail as order_id is not present in the payload', + scenario: 'Business', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'testPurchase', + userId: 'sajal12', + context: { + traits: { + ...commonTraits, + email: 'test@rudderstack.com', + phone: '9112340375', + }, + }, + properties: commonPropsWithProducts, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: '[Bluecore] property:: order_id is required for purchase event', + statTags: { + destType: 'BLUECORE', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'bluecore-track-test-2', + name: 'bluecore', + description: + 'Track event call with custom event mapped in destination config to purchase event. This will fail as total is not present in the payload', + scenario: 'Business', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'testPurchase', + userId: 'sajal12', + context: { + traits: { + ...commonTraits, + email: 'test@rudderstack.com', + phone: '9112340375', + }, + }, + properties: { ...commonPropsWithProducts, order_id: '123' }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: '[Bluecore] property:: total is required for purchase event', + statTags: { + destType: 'BLUECORE', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'bluecore-track-test-3', + name: 'bluecore', + description: + 'Track event call with products searched event not mapped in destination config. This will fail as search_query is not present in the payload', + scenario: 'Business', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'Products Searched', + userId: 'sajal12', + context: { + traits: { + ...commonTraits, + email: 'test@rudderstack.com', + phone: '9112340375', + }, + }, + properties: { ...commonPropsWithoutProducts }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: '[Bluecore] property:: search_query is required for search event', + statTags: { + destType: 'BLUECORE', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'bluecore-track-test-4', + name: 'bluecore', + description: + 'Track event call with Product Viewed event not mapped in destination config. This will be sent with viewed_product name. This event without properties.products will add entire property object as products as this event type is recommended to sent with products', + scenario: 'Business', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product viewed', + userId: 'sajal12', + context: { + traits: { + ...commonTraits, + email: 'test@rudderstack.com', + phone: '9112340375', + }, + }, + properties: commonPropsWithoutProducts, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + JSON: { + properties: { + distinct_id: 'sajal12', + customer: { + age: '22', + email: 'test@rudderstack.com', + }, + products: [ + { + id: '123', + property1: 'value1', + property2: 'value2', + }, + ], + }, + event: 'viewed_product', + token: 'dummy_sandbox', + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'bluecore-track-test-5', + name: 'bluecore', + description: + 'Track event call with custom event mapped with two standard ecomm events in destination config. Both of the two corresponding standard events will be sent ', + scenario: 'Business', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'testboth', + sentAt: '2020-08-14T05:30:30.118Z', + channel: 'web', + context: { + source: 'test', + userAgent: 'chrome', + traits: { + id: 'user@1', + age: '22', + anonymousId: '9c6bd77ea9da3e68', + }, + device: { + advertisingId: 'abc123', + }, + library: { + name: 'rudder-sdk-ruby-sync', + version: '1.0.6', + }, + }, + properties: { + property1: 'value1', + property2: 'value2', + product_id: '123', + }, + anonymousId: 'new-id', + integrations: { + All: true, + }, + }, + destination: destination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + JSON: { + properties: { + distinct_id: 'new-id', + customer: { + age: '22', + }, + products: [ + { + id: '123', + property1: 'value1', + property2: 'value2', + }, + ], + }, + event: 'wishlist', + token: 'dummy_sandbox', + }, + userId: '', + }), + statusCode: 200, + }, + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + JSON: { + properties: { + distinct_id: 'new-id', + customer: { + age: '22', + }, + products: [ + { + id: '123', + property1: 'value1', + property2: 'value2', + }, + ], + }, + event: 'add_to_cart', + token: 'dummy_sandbox', + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/bluecore/identifyTestData.ts b/test/integrations/destinations/bluecore/identifyTestData.ts index 77ad6b614d..13c7dc947e 100644 --- a/test/integrations/destinations/bluecore/identifyTestData.ts +++ b/test/integrations/destinations/bluecore/identifyTestData.ts @@ -1,51 +1,51 @@ - import { overrideDestination, transformResultBuilder, generateSimplifiedIdentifyPayload, -} from '../../testUtils';const destination = { - "ID": "1pYpzzvcn7AQ2W9GGIAZSsN6Mfq", - "Name": "BLUECORE", - "Config": { - "bluecoreNamespace": "dummy_sandbox", - "eventsMapping": [ - { - "from": "ABC Searched", - "to": "search" - } - ] - }, - "Enabled": true, - "Transformations": [] +} from '../../testUtils'; + +const destination = { + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'BLUECORE', + Config: { + bluecoreNamespace: 'dummy_sandbox', + eventsMapping: [ + { + from: 'ABC Searched', + to: 'search', + }, + ], + }, + Enabled: true, + Transformations: [], }; const commonTraits = { - "anonymousId": "50be5c78-6c3f-4b60-be84-97805a316fb1", - "phone": "+1234589947", - "gender": "non-binary", - "db": "19950715", - "lastname": "Rudderlabs", - "firstName": "Test", - "address": { - "city": "Kolkata", - "state": "WB", - "zip": "700114", - "country": "IN" - } + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + phone: '+1234589947', + gender: 'non-binary', + db: '19950715', + lastname: 'Rudderlabs', + firstName: 'Test', + address: { + city: 'Kolkata', + state: 'WB', + zip: '700114', + country: 'IN', + }, }; const commonOutputCustomerProperties = { - "first_name": "Test", - "last_name": "Rudderlabs", - "sex": "non-binary", - "address": { - "city": "Kolkata", - "state": "WB", - "zip": "700114", - "country": "IN" - } -} - + first_name: 'Test', + last_name: 'Rudderlabs', + sex: 'non-binary', + address: { + city: 'Kolkata', + state: 'WB', + zip: '700114', + country: 'IN', + }, +}; const commonOutputHeaders = { 'Content-Type': 'application/json', @@ -76,13 +76,12 @@ export const identifyData = [ destination, message: generateSimplifiedIdentifyPayload({ context: { - traits: {...commonTraits, - "email": "abc@gmail.com"}, + traits: { ...commonTraits, email: 'abc@gmail.com' }, }, anonymousId, userId, sentAt, - originalTimestamp + originalTimestamp, }), }, ], @@ -100,15 +99,15 @@ export const identifyData = [ headers: commonOutputHeaders, JSON: { properties: { - distinct_id: 'abc@gmail.com', - customer: {...commonOutputCustomerProperties, email: 'abc@gmail.com',}, + distinct_id: 'abc@gmail.com', + customer: { ...commonOutputCustomerProperties, email: 'abc@gmail.com' }, }, - "token": "dummy_sandbox", - "event": "customer_patch" - }, + token: 'dummy_sandbox', + event: 'customer_patch', + }, }), statusCode: 200, - } + }, ], }, }, @@ -133,37 +132,37 @@ export const identifyData = [ context: { traits: commonTraits, }, - traits : { - action : 'identify' + traits: { + action: 'identify', }, anonymousId, userId, sentAt, - originalTimestamp + originalTimestamp, }), }, ], }, }, output: { - response: { - status: 200, - body: [ - { - error: '[Bluecore] property:: email is required for \'identify\' action', - statTags: { - destType: 'BLUECORE', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, + response: { + status: 200, + body: [ + { + error: "[Bluecore] property:: email is required for 'identify' action", + statTags: { + destType: 'BLUECORE', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', }, - ], - }, + statusCode: 400, + }, + ], }, + }, }, { id: 'bluecore-identify-test-3', @@ -185,37 +184,37 @@ export const identifyData = [ context: { traits: commonTraits, }, - traits : { - action : 'random' + traits: { + action: 'random', }, anonymousId, userId, sentAt, - originalTimestamp + originalTimestamp, }), }, ], }, }, output: { - response: { - status: 200, - body: [ - { - error: '[Bluecore] traits.action must be \'identify\' for identify action', - statTags: { - destType: 'BLUECORE', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, + response: { + status: 200, + body: [ + { + error: "[Bluecore] traits.action must be 'identify' for identify action", + statTags: { + destType: 'BLUECORE', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', }, - ], - }, + statusCode: 400, + }, + ], }, + }, }, { id: 'bluecore-identify-test-1', @@ -235,16 +234,15 @@ export const identifyData = [ destination, message: generateSimplifiedIdentifyPayload({ context: { - traits: {...commonTraits, - "email": "abc@gmail.com"}, + traits: { ...commonTraits, email: 'abc@gmail.com' }, }, traits: { - action: 'identify' + action: 'identify', }, anonymousId, userId, sentAt, - originalTimestamp + originalTimestamp, }), }, ], @@ -262,17 +260,17 @@ export const identifyData = [ headers: commonOutputHeaders, JSON: { properties: { - distinct_id: 'abc@gmail.com', - customer: {...commonOutputCustomerProperties, email: 'abc@gmail.com',}, + distinct_id: 'abc@gmail.com', + customer: { ...commonOutputCustomerProperties, email: 'abc@gmail.com' }, }, - "token": "dummy_sandbox", - "event": "identify" - }, + token: 'dummy_sandbox', + event: 'identify', + }, }), statusCode: 200, - } + }, ], }, }, - } + }, ]; diff --git a/test/integrations/destinations/bluecore/trackTestData.ts b/test/integrations/destinations/bluecore/trackTestData.ts new file mode 100644 index 0000000000..30eeb4fe2d --- /dev/null +++ b/test/integrations/destinations/bluecore/trackTestData.ts @@ -0,0 +1,342 @@ +import { + generateSimplifiedTrackPayload, + generateTrackPayload, + overrideDestination, + transformResultBuilder, +} from '../../testUtils'; + +const destination = { + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'BLUECORE', + Config: { + bluecoreNamespace: 'dummy_sandbox', + eventsMapping: [ + { + from: 'ABC Searched', + to: 'search', + }, + { + from: 'testPurchase', + to: 'purchase', + }, + { + from: 'testboth', + to: 'wishlist', + }, + { + from: 'testboth', + to: 'add_to_cart', + }, + ], + }, + Enabled: true, + Transformations: [], +}; + +const commonTraits = { + id: 'user@1', + age: '22', + anonymousId: '9c6bd77ea9da3e68', +}; + +const commonPropsWithProducts = { + property1: 'value1', + property2: 'value2', + products: [ + { + product_id: '123', + sku: 'sku123', + name: 'Product 1', + price: 100, + quantity: 2, + }, + { + product_id: '124', + sku: 'sku124', + name: 'Product 2', + price: 200, + quantity: 3, + }, + ], +}; + +const commonPropsWithoutProducts = { + property1: 'value1', + property2: 'value2', + product_id: '123', +}; + +const commonOutputHeaders = { + 'Content-Type': 'application/json', +}; + +const eventEndPoint = 'https://api.bluecore.com/api/track/mobile/v1'; + +export const trackTestData = [ + { + id: 'bluecore-track-test-1', + name: 'bluecore', + description: + 'Track event call with custom event with properties not mapped in destination config. This will be sent with its original name', + scenario: 'Business', + successCriteria: + 'Response should contain only event payload and status code should be 200, for the event payload should contain flattened properties in the payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'TestEven001', + userId: 'sajal12', + context: { + traits: { + ...commonTraits, + email: 'test@rudderstack.com', + phone: '9112340375', + }, + }, + properties: commonPropsWithProducts, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + JSON: { + properties: { + distinct_id: 'sajal12', + customer: { + age: '22', + email: 'test@rudderstack.com', + }, + products: [ + { + name: 'Product 1', + price: 100, + id: '123', + quantity: 2, + }, + { + name: 'Product 2', + price: 200, + id: '124', + quantity: 3, + }, + ], + }, + event: 'TestEven001', + token: 'dummy_sandbox', + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'bluecore-track-test-2', + name: 'bluecore', + description: + 'Track event call with custom event without properties not mapped in destination config. This will be sent with its original name', + scenario: 'Business', + successCriteria: + 'Response should contain only event payload and status code should be 200. As the event paylaod does not contains products, product array will not be sent', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'TestEven001', + userId: 'sajal12', + context: { + traits: { + ...commonTraits, + email: 'test@rudderstack.com', + phone: '9112340375', + }, + }, + properties: commonPropsWithoutProducts, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + JSON: { + properties: { + distinct_id: 'sajal12', + customer: { + age: '22', + email: 'test@rudderstack.com', + }, + }, + event: 'TestEven001', + token: 'dummy_sandbox', + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'bluecore-track-test-8', + name: 'bluecore', + description: + 'optin event is also considered as a track event, user need to not map it from the UI , it will be sent with the same event name to bluecore', + scenario: 'Business', + successCriteria: + 'Response should contain only event payload and status code should be 200, for the event payload should contain flattened properties in the payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'optin', + userId: 'sajal12', + context: { + traits: { + ...commonTraits, + email: 'test@rudderstack.com', + phone: '9112340375', + }, + }, + properties: commonPropsWithoutProducts, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + JSON: { + properties: { + distinct_id: 'sajal12', + customer: { + age: '22', + email: 'test@rudderstack.com', + }, + }, + event: 'optin', + token: 'dummy_sandbox', + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'bluecore-track-test-8', + name: 'bluecore', + description: + 'unsubscribe event is also considered as a track event, user need to not map it from the UI , it will be sent with the same event name to bluecore', + scenario: 'Business', + successCriteria: + 'Response should contain only event payload and status code should be 200, for the event payload should contain flattened properties in the payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'unsubscribe', + userId: 'sajal12', + context: { + traits: { + ...commonTraits, + email: 'test@rudderstack.com', + phone: '9112340375', + }, + }, + properties: commonPropsWithoutProducts, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + JSON: { + properties: { + distinct_id: 'sajal12', + customer: { + age: '22', + email: 'test@rudderstack.com', + }, + }, + event: 'unsubscribe', + token: 'dummy_sandbox', + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, +];