From 88b2d5709da00445ffae54f5a36de855cb5f8479 Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Fri, 10 May 2024 09:47:22 +0530 Subject: [PATCH 1/8] feat: onboard koddi destination --- src/cdk/v2/destinations/koddi/config.js | 20 +++++ .../destinations/koddi/data/ClicksConfig.json | 35 ++++++++ .../koddi/data/ConversionsConfig.json | 53 ++++++++++++ .../koddi/data/ImpressionsConfig.json | 22 +++++ .../v2/destinations/koddi/procWorkflow.yaml | 31 +++++++ src/cdk/v2/destinations/koddi/utils.js | 84 +++++++++++++++++++ 6 files changed, 245 insertions(+) create mode 100644 src/cdk/v2/destinations/koddi/config.js create mode 100644 src/cdk/v2/destinations/koddi/data/ClicksConfig.json create mode 100644 src/cdk/v2/destinations/koddi/data/ConversionsConfig.json create mode 100644 src/cdk/v2/destinations/koddi/data/ImpressionsConfig.json create mode 100644 src/cdk/v2/destinations/koddi/procWorkflow.yaml create mode 100644 src/cdk/v2/destinations/koddi/utils.js diff --git a/src/cdk/v2/destinations/koddi/config.js b/src/cdk/v2/destinations/koddi/config.js new file mode 100644 index 0000000000..c7af5c0b57 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/config.js @@ -0,0 +1,20 @@ +const { getMappingConfig } = require('../../../../v0/util'); + +const ConfigCategories = { + IMPRESSIONS: { + type: 'track', + name: 'impressionsMapping', + }, + CLICKS: { + type: 'track', + name: 'clicksMapping', + }, + CONVERSIONS: { + type: 'track', + name: 'conversionsMapping', + }, +}; + +const mappingConfig = getMappingConfig(ConfigCategories, __dirname); + +module.exports = { ConfigCategories, mappingConfig }; diff --git a/src/cdk/v2/destinations/koddi/data/ClicksConfig.json b/src/cdk/v2/destinations/koddi/data/ClicksConfig.json new file mode 100644 index 0000000000..96ab27b2ae --- /dev/null +++ b/src/cdk/v2/destinations/koddi/data/ClicksConfig.json @@ -0,0 +1,35 @@ +[ + { + "sourceKeys": "properties.tracking_data", + "required": true, + "destKey": "trackingData" + }, + { + "sourceKeys": "properties.rank", + "required": true, + "destKey": "rank" + }, + { + "sourceKeys": "properties.beacon_issued", + "required": true, + "destKey": "beaconIssued" + }, + { + "sourceKeys": "userId", + "sourceFromGenericMap": true, + "required": true, + "destKey": "userGuid" + }, + { + "sourceKeys": "properties.test_version_override", + "destKey": "testVersionOverride" + }, + { + "sourceKeys": "properties.destination_url", + "destKey": "destinationUrl" + }, + { + "sourceKeys": "properties.overrides", + "destKey": "overrides" + } +] diff --git a/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json b/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json new file mode 100644 index 0000000000..0d49e39c32 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json @@ -0,0 +1,53 @@ +[ + { + "sourceKeys": "context.page.referring_domain", + "destKey": "domain" + }, + { + "sourceKeys": "context.locale", + "required": true, + "destKey": "culture" + }, + { + "sourceKeys": "properties.currency", + "required": true, + "destKey": "currency" + }, + { + "sourceKeys": ["context.ip", "request_ip"], + "destKey": "user_ip" + }, + { + "sourceKeys": "context.userAgent", + "destKey": "user_agent" + }, + { + "sourceKeys": "userId", + "sourceFromGenericMap": true, + "required": true, + "destKey": "userGuid" + }, + { + "sourceKeys": "context.device.type", + "destKey": "device_type" + }, + { + "sourceKeys": ["properties.order_id", "properties.transaction_id"], + "required": true, + "destKey": "transaction_id" + }, + { + "sourceKeys": "properties.conversion_source", + "destKey": "conversion_source" + }, + { + "sourceKeys": "timestamp", + "sourceFromGenericMap": true, + "destKey": "unixtime" + }, + { + "sourceKeys": "properties.bidders", + "required": true, + "destKey": "bidders" + } +] diff --git a/src/cdk/v2/destinations/koddi/data/ImpressionsConfig.json b/src/cdk/v2/destinations/koddi/data/ImpressionsConfig.json new file mode 100644 index 0000000000..de53703b32 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/data/ImpressionsConfig.json @@ -0,0 +1,22 @@ +[ + { + "sourceKeys": "properties.tracking_data", + "required": true, + "destKey": "trackingData" + }, + { + "sourceKeys": "properties.rank", + "required": true, + "destKey": "rank" + }, + { + "sourceKeys": "properties.beacon_issued", + "required": true, + "destKey": "beaconIssued" + }, + { + "sourceKeys": "timestamp", + "sourceFromGenericMap": true, + "destKey": "ts" + } +] diff --git a/src/cdk/v2/destinations/koddi/procWorkflow.yaml b/src/cdk/v2/destinations/koddi/procWorkflow.yaml new file mode 100644 index 0000000000..d632b73273 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/procWorkflow.yaml @@ -0,0 +1,31 @@ +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 message."); + $.assert(messageType in {{$.EventType.([.TRACK])}}, "message type " + messageType + " is not supported"); + $.assert(.message.event, "Event name is not present. Aborting"); + $.assert(typeof .message.event === "string", "event name should be a string"); + $.assertConfig(.destination.Config.apiBaseUrl, "API Base URL is not present. Aborting"); + $.assertConfig(.destination.Config.clientName, "Client Name is not present. Aborting"); + - name: preparePayload + template: | + const payload = $.constructFullPayload(.message, .destination.Config); + $.context.payload = $.removeUndefinedAndNullValues(payload); + - name: buildResponse + template: | + const response = $.constructResponse(.context.payload, .destination.Config, .message); + response diff --git a/src/cdk/v2/destinations/koddi/utils.js b/src/cdk/v2/destinations/koddi/utils.js new file mode 100644 index 0000000000..2936d519a8 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/utils.js @@ -0,0 +1,84 @@ +const config = require('./config'); +const { constructPayload, defaultRequestConfig } = require('../../../../v0/util'); + +/** + * + * @param message + * @param Config + * @returns {{}} + */ +const constructFullPayload = (message, Config) => { + let payload; + switch (message.event) { + case 'Impressions': + payload = constructPayload( + message, + config.mappingConfig[config.ConfigCategories.IMPRESSIONS.name], + ); + payload.clientName = Config.clientName; + break; + case 'Clicks': + payload = constructPayload( + message, + config.mappingConfig[config.ConfigCategories.CLICKS.name], + ); + payload.clientName = Config.clientName; + if (Config.testVersionOverride === false) { + payload.properties.test_version_override = null; + } + if (Config.overrides === false) { + payload.properties.overrides = null; + } + break; + case 'Conversions': + payload = constructPayload( + message, + config.mappingConfig[config.ConfigCategories.CONVERSIONS.name], + ); + payload.client_name = Config.clientName; + break; + default: + break; + } + return payload; +}; + +const getEndpoint = (Config, message) => { + let endpoint = Config.apiBaseUrl; + switch (message.event) { + case 'Impressions': + endpoint += '?action=impression'; + break; + case 'Clicks': + endpoint += '?action=click'; + break; + case 'Conversions': + endpoint += '/conversion'; + break; + default: + break; + } + return endpoint; +}; + +const constructResponse = (payload, Config, message) => { + const response = defaultRequestConfig(); + response.endpoint = getEndpoint(Config, message); + response.headers = { + accept: 'application/json', + }; + if (message.event === 'Conversions') { + response.body.JSON = payload; + response.method = 'POST'; + response.headers = { + ...response.headers, + 'content-type': 'application/json', + }; + } else { + response.params = payload; + response.method = 'GET'; + } + return response; +}; + +module.exports = { constructFullPayload, getEndpoint, constructResponse }; From 847e3e04d3ef67b9a7b5e35127251f3fc34ba3bf Mon Sep 17 00:00:00 2001 From: Gauravudia Date: Fri, 10 May 2024 16:56:03 +0530 Subject: [PATCH 2/8] fix: config --- src/cdk/v2/destinations/koddi/config.js | 18 ++++++++++++------ src/cdk/v2/destinations/koddi/utils.js | 17 ++++------------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/cdk/v2/destinations/koddi/config.js b/src/cdk/v2/destinations/koddi/config.js index c7af5c0b57..dfae60a257 100644 --- a/src/cdk/v2/destinations/koddi/config.js +++ b/src/cdk/v2/destinations/koddi/config.js @@ -1,20 +1,26 @@ const { getMappingConfig } = require('../../../../v0/util'); -const ConfigCategories = { +const CONFIG_CATEGORIES = { IMPRESSIONS: { type: 'track', - name: 'impressionsMapping', + name: 'ImpressionsConfig', }, CLICKS: { type: 'track', - name: 'clicksMapping', + name: 'ClicksConfig', }, CONVERSIONS: { type: 'track', - name: 'conversionsMapping', + name: 'ConversionsConfig', }, }; -const mappingConfig = getMappingConfig(ConfigCategories, __dirname); +const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); -module.exports = { ConfigCategories, mappingConfig }; +module.exports = { + CONFIG_CATEGORIES, + MAPPING_CONFIG, + IMPRESSIONS_CONFIG: MAPPING_CONFIG[CONFIG_CATEGORIES.IMPRESSIONS.name], + CLICKS_CONFIG: MAPPING_CONFIG[CONFIG_CATEGORIES.CLICKS.name], + CONVERSIONS_CONFIG: MAPPING_CONFIG[CONFIG_CATEGORIES.CONVERSIONS.name], +}; diff --git a/src/cdk/v2/destinations/koddi/utils.js b/src/cdk/v2/destinations/koddi/utils.js index 2936d519a8..e9f2c6a412 100644 --- a/src/cdk/v2/destinations/koddi/utils.js +++ b/src/cdk/v2/destinations/koddi/utils.js @@ -1,4 +1,4 @@ -const config = require('./config'); +const { IMPRESSIONS_CONFIG, CLICKS_CONFIG, CONVERSIONS_CONFIG } = require('./config'); const { constructPayload, defaultRequestConfig } = require('../../../../v0/util'); /** @@ -11,17 +11,11 @@ const constructFullPayload = (message, Config) => { let payload; switch (message.event) { case 'Impressions': - payload = constructPayload( - message, - config.mappingConfig[config.ConfigCategories.IMPRESSIONS.name], - ); + payload = constructPayload(message, IMPRESSIONS_CONFIG); payload.clientName = Config.clientName; break; case 'Clicks': - payload = constructPayload( - message, - config.mappingConfig[config.ConfigCategories.CLICKS.name], - ); + payload = constructPayload(message, CLICKS_CONFIG); payload.clientName = Config.clientName; if (Config.testVersionOverride === false) { payload.properties.test_version_override = null; @@ -31,10 +25,7 @@ const constructFullPayload = (message, Config) => { } break; case 'Conversions': - payload = constructPayload( - message, - config.mappingConfig[config.ConfigCategories.CONVERSIONS.name], - ); + payload = constructPayload(message, CONVERSIONS_CONFIG); payload.client_name = Config.clientName; break; default: From ddf8d46fed980204c561f95daa12fc740302e6e3 Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Sun, 12 May 2024 20:59:12 +0530 Subject: [PATCH 3/8] fix: added conversions bidders validation and improved implementation --- src/cdk/v2/destinations/koddi/config.js | 7 ++ .../v2/destinations/koddi/procWorkflow.yaml | 12 ++- src/cdk/v2/destinations/koddi/rtWorkflow.yaml | 35 ++++++++ src/cdk/v2/destinations/koddi/utils.js | 81 +++++++++++++------ 4 files changed, 107 insertions(+), 28 deletions(-) create mode 100644 src/cdk/v2/destinations/koddi/rtWorkflow.yaml diff --git a/src/cdk/v2/destinations/koddi/config.js b/src/cdk/v2/destinations/koddi/config.js index dfae60a257..fa595dc627 100644 --- a/src/cdk/v2/destinations/koddi/config.js +++ b/src/cdk/v2/destinations/koddi/config.js @@ -1,5 +1,11 @@ const { getMappingConfig } = require('../../../../v0/util'); +const EVENT_NAMES = { + IMPRESSIONS: 'impressions', + CLICKS: 'clicks', + CONVERSIONS: 'conversions', +}; + const CONFIG_CATEGORIES = { IMPRESSIONS: { type: 'track', @@ -18,6 +24,7 @@ const CONFIG_CATEGORIES = { const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); module.exports = { + EVENT_NAMES, CONFIG_CATEGORIES, MAPPING_CONFIG, IMPRESSIONS_CONFIG: MAPPING_CONFIG[CONFIG_CATEGORIES.IMPRESSIONS.name], diff --git a/src/cdk/v2/destinations/koddi/procWorkflow.yaml b/src/cdk/v2/destinations/koddi/procWorkflow.yaml index d632b73273..28d062d4a4 100644 --- a/src/cdk/v2/destinations/koddi/procWorkflow.yaml +++ b/src/cdk/v2/destinations/koddi/procWorkflow.yaml @@ -2,30 +2,34 @@ bindings: - name: EventType path: ../../../../constants - path: ../../bindings/jsontemplate - - name: defaultRequestConfig - path: ../../../../v0/util - name: removeUndefinedAndNullValues path: ../../../../v0/util - path: ./utils + - path: ./config steps: - name: messageType template: | .message.type.toLowerCase(); + - name: eventName + template: | + .message.integrations.koddi.eventName.toLowerCase(); - name: validateInput template: | let messageType = $.outputs.messageType; + let eventName = $.outputs.eventName; $.assert(messageType, "message Type is not present. Aborting message."); $.assert(messageType in {{$.EventType.([.TRACK])}}, "message type " + messageType + " is not supported"); + $.assert(eventName in {{$.EVENT_NAMES.([.IMPRESSIONS, .CLICKS, .CONVERSIONS])}}, "event name " + eventName + " is not supported"); $.assert(.message.event, "Event name is not present. Aborting"); $.assert(typeof .message.event === "string", "event name should be a string"); $.assertConfig(.destination.Config.apiBaseUrl, "API Base URL is not present. Aborting"); $.assertConfig(.destination.Config.clientName, "Client Name is not present. Aborting"); - name: preparePayload template: | - const payload = $.constructFullPayload(.message, .destination.Config); + const payload = $.constructFullPayload($.outputs.eventName, .message, .destination.Config); $.context.payload = $.removeUndefinedAndNullValues(payload); - name: buildResponse template: | - const response = $.constructResponse(.context.payload, .destination.Config, .message); + const response = $.constructResponse($.outputs.eventName, .destination.Config, $.context.payload); response diff --git a/src/cdk/v2/destinations/koddi/rtWorkflow.yaml b/src/cdk/v2/destinations/koddi/rtWorkflow.yaml new file mode 100644 index 0000000000..30dd3fdd95 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/rtWorkflow.yaml @@ -0,0 +1,35 @@ +bindings: + - path: ./config + - name: handleRtTfSingleEventError + path: ../../../../v0/util/index + - path: ./utils +steps: + - name: validateInput + template: | + $.assert(Array.isArray(^) && ^.length > 0, "Invalid event array") + + - name: transform + externalWorkflow: + path: ./procWorkflow.yaml + loopOverInput: true + + - name: successfulEvents + template: | + $.outputs.transform#idx.output.({ + "output": .body.JSON.events[0], + "destination": ^[idx].destination, + "metadata": ^[idx].metadata + })[] + - name: failedEvents + template: | + $.outputs.transform#idx.error.( + $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) + )[] + - name: batchSuccessfulEvents + description: Batches the successfulEvents + template: | + $.batchResponseBuilder($.outputs.successfulEvents); + + - name: finalPayload + template: | + [...$.outputs.failedEvents, ...$.outputs.batchSuccessfulEvents] diff --git a/src/cdk/v2/destinations/koddi/utils.js b/src/cdk/v2/destinations/koddi/utils.js index e9f2c6a412..6e8d5ba12b 100644 --- a/src/cdk/v2/destinations/koddi/utils.js +++ b/src/cdk/v2/destinations/koddi/utils.js @@ -1,32 +1,58 @@ -const { IMPRESSIONS_CONFIG, CLICKS_CONFIG, CONVERSIONS_CONFIG } = require('./config'); -const { constructPayload, defaultRequestConfig } = require('../../../../v0/util'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { EVENT_NAMES, IMPRESSIONS_CONFIG, CLICKS_CONFIG, CONVERSIONS_CONFIG } = require('./config'); +const { constructPayload, defaultRequestConfig, toUnixTimestamp } = require('../../../../v0/util'); + +const validateBidders = (bidders) => { + if (!Array.isArray(bidders)) { + throw new InstrumentationError('properties.bidders should be an array of objects. Aborting.'); + } + if (bidders.length === 0) { + throw new InstrumentationError( + 'properties.bidders should contains at least one bidder. Aborting.', + ); + } + bidders.forEach((bidder) => { + if (!(bidder.bidder || bidder.alternate_bidder)) { + throw new InstrumentationError('bidder or alternate_bidder is not present. Aborting.'); + } + if (!bidder.count) { + throw new InstrumentationError('count is not present. Aborting.'); + } + if (!bidder.base_price) { + throw new InstrumentationError('base_price is not present. Aborting.'); + } + }); +}; /** - * - * @param message - * @param Config - * @returns {{}} + * This function constructs payloads based upon mappingConfig for all calls. + * @param {*} eventName + * @param {*} message + * @param {*} Config + * @returns */ -const constructFullPayload = (message, Config) => { +const constructFullPayload = (eventName, message, Config) => { let payload; - switch (message.event) { - case 'Impressions': + switch (eventName) { + case EVENT_NAMES.IMPRESSIONS: payload = constructPayload(message, IMPRESSIONS_CONFIG); payload.clientName = Config.clientName; break; - case 'Clicks': + case EVENT_NAMES.CLICKS: payload = constructPayload(message, CLICKS_CONFIG); payload.clientName = Config.clientName; - if (Config.testVersionOverride === false) { - payload.properties.test_version_override = null; + if (!Config.testVersionOverride) { + payload.testVersionOverride = null; } - if (Config.overrides === false) { - payload.properties.overrides = null; + if (!Config.overrides) { + payload.overrides = null; } break; - case 'Conversions': + case EVENT_NAMES.CONVERSIONS: payload = constructPayload(message, CONVERSIONS_CONFIG); payload.client_name = Config.clientName; + payload.unixtime = toUnixTimestamp(payload.unixtime); + validateBidders(payload.bidders); break; default: break; @@ -34,16 +60,16 @@ const constructFullPayload = (message, Config) => { return payload; }; -const getEndpoint = (Config, message) => { +const getEndpoint = (eventName, Config) => { let endpoint = Config.apiBaseUrl; - switch (message.event) { - case 'Impressions': + switch (eventName) { + case EVENT_NAMES.IMPRESSIONS: endpoint += '?action=impression'; break; - case 'Clicks': + case EVENT_NAMES.CLICKS: endpoint += '?action=click'; break; - case 'Conversions': + case EVENT_NAMES.CONVERSIONS: endpoint += '/conversion'; break; default: @@ -52,13 +78,20 @@ const getEndpoint = (Config, message) => { return endpoint; }; -const constructResponse = (payload, Config, message) => { +/** + * This function constructs response based upon event. + * @param {*} eventName + * @param {*} Config + * @param {*} payload + * @returns + */ +const constructResponse = (eventName, Config, payload) => { const response = defaultRequestConfig(); - response.endpoint = getEndpoint(Config, message); + response.endpoint = getEndpoint(eventName, Config); response.headers = { accept: 'application/json', }; - if (message.event === 'Conversions') { + if (eventName === EVENT_NAMES.CONVERSIONS) { response.body.JSON = payload; response.method = 'POST'; response.headers = { @@ -72,4 +105,4 @@ const constructResponse = (payload, Config, message) => { return response; }; -module.exports = { constructFullPayload, getEndpoint, constructResponse }; +module.exports = { constructFullPayload, constructResponse }; From bc7970c4f0a70e9fe8ad06ffd92f8f4b2a4ec910 Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Mon, 13 May 2024 18:04:23 +0530 Subject: [PATCH 4/8] fix: fixed some issue and added unit test --- src/cdk/v2/destinations/koddi/utils.js | 18 +- src/cdk/v2/destinations/koddi/utils.test.js | 423 ++++++++++++++++++++ src/features.json | 3 +- 3 files changed, 438 insertions(+), 6 deletions(-) create mode 100644 src/cdk/v2/destinations/koddi/utils.test.js diff --git a/src/cdk/v2/destinations/koddi/utils.js b/src/cdk/v2/destinations/koddi/utils.js index 6e8d5ba12b..f6521d1a66 100644 --- a/src/cdk/v2/destinations/koddi/utils.js +++ b/src/cdk/v2/destinations/koddi/utils.js @@ -1,6 +1,11 @@ const { InstrumentationError } = require('@rudderstack/integrations-lib'); const { EVENT_NAMES, IMPRESSIONS_CONFIG, CLICKS_CONFIG, CONVERSIONS_CONFIG } = require('./config'); -const { constructPayload, defaultRequestConfig, toUnixTimestamp } = require('../../../../v0/util'); +const { + constructPayload, + defaultRequestConfig, + toUnixTimestamp, + stripTrailingSlash, +} = require('../../../../v0/util'); const validateBidders = (bidders) => { if (!Array.isArray(bidders)) { @@ -55,13 +60,13 @@ const constructFullPayload = (eventName, message, Config) => { validateBidders(payload.bidders); break; default: - break; + throw new InstrumentationError(`event name ${eventName} is not supported.`); } return payload; }; const getEndpoint = (eventName, Config) => { - let endpoint = Config.apiBaseUrl; + let endpoint = stripTrailingSlash(Config.apiBaseUrl); switch (eventName) { case EVENT_NAMES.IMPRESSIONS: endpoint += '?action=impression'; @@ -73,7 +78,7 @@ const getEndpoint = (eventName, Config) => { endpoint += '/conversion'; break; default: - break; + throw new InstrumentationError(`event name ${eventName} is not supported.`); } return endpoint; }; @@ -86,6 +91,9 @@ const getEndpoint = (eventName, Config) => { * @returns */ const constructResponse = (eventName, Config, payload) => { + if (!Object.values(EVENT_NAMES).includes(eventName)) { + throw new InstrumentationError(`event name ${eventName} is not supported.`); + } const response = defaultRequestConfig(); response.endpoint = getEndpoint(eventName, Config); response.headers = { @@ -105,4 +113,4 @@ const constructResponse = (eventName, Config, payload) => { return response; }; -module.exports = { constructFullPayload, constructResponse }; +module.exports = { getEndpoint, validateBidders, constructFullPayload, constructResponse }; diff --git a/src/cdk/v2/destinations/koddi/utils.test.js b/src/cdk/v2/destinations/koddi/utils.test.js new file mode 100644 index 0000000000..e31bd6d9a7 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/utils.test.js @@ -0,0 +1,423 @@ +const { + getEndpoint, + validateBidders, + constructFullPayload, + constructResponse, +} = require('./utils'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); + +describe('getEndpoint', () => { + it('returns the correct endpoint for IMPRESSIONS event', () => { + const eventName = 'impressions'; + const Config = { + apiBaseUrl: 'https://www.test-client.com/', + clientName: 'test-client', + }; + const result = getEndpoint(eventName, Config); + expect(result).toEqual('https://www.test-client.com?action=impression'); + }); + + it('returns the correct endpoint for CLICKS event', () => { + const eventName = 'clicks'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const result = getEndpoint(eventName, Config); + expect(result).toEqual('https://www.test-client.com?action=click'); + }); + + it('returns the correct endpoint for IMPRESSIONS event', () => { + const eventName = 'conversions'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const result = getEndpoint(eventName, Config); + expect(result).toEqual('https://www.test-client.com/conversion'); + }); + + it('should throw error for unsupported event', () => { + const eventName = 'test'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + expect(() => getEndpoint(eventName, Config).toThrow(InstrumentationError)); + expect(() => getEndpoint(eventName, Config).toThrow('event name test is not supported.')); + }); +}); + +describe('validateBidders', () => { + it('should throw error if bidders is not an array', () => { + const bidders = {}; + expect(() => validateBidders(bidders)).toThrow(InstrumentationError); + expect(() => validateBidders(bidders)).toThrow( + 'properties.bidders should be an array of objects. Aborting.', + ); + }); + + it('should throw error if bidders is an empty array', () => { + const bidders = []; + expect(() => validateBidders(bidders)).toThrow(InstrumentationError); + expect(() => validateBidders(bidders)).toThrow( + 'properties.bidders should contains at least one bidder. Aborting.', + ); + }); + + it('should throw error if bidder or alternate_bidder is not present', () => { + const bidders = [ + { count: 1, base_price: 100 }, + { bidder: 'bidder1', count: 2, base_price: 200 }, + { alternate_bidder: 'alternate1', count: 3, base_price: 300 }, + ]; + expect(() => validateBidders(bidders)).toThrow(InstrumentationError); + expect(() => validateBidders(bidders)).toThrow( + 'bidder or alternate_bidder is not present. Aborting.', + ); + }); + + it('should throw error if count is not present', () => { + const bidders = [{ bidder: 'bidder1', alternate_bidder: 'alternate1', base_price: 100 }]; + expect(() => validateBidders(bidders)).toThrow(InstrumentationError); + expect(() => validateBidders(bidders)).toThrow('count is not present. Aborting.'); + }); + + it('should throw error if base_price is not present', () => { + const bidders = [ + { bidder: 'bidder1', alternate_bidder: 'alternate1', count: 1 }, // Missing base_price + ]; + expect(() => validateBidders(bidders)).toThrow(InstrumentationError); + expect(() => validateBidders(bidders)).toThrow('base_price is not present. Aborting.'); + }); + + it('should not throw error if all fields are present for all bidders', () => { + const bidders = [ + { bidder: 'bidder1', alternate_bidder: 'alternate1', count: 1, base_price: 100 }, + { bidder: 'bidder2', alternate_bidder: 'alternate2', count: 2, base_price: 200 }, + ]; + expect(() => validateBidders(bidders)).not.toThrow(); + }); +}); + +describe('constructFullPayload', () => { + it('should construct payload for IMPRESSIONS event', () => { + const eventName = 'impressions'; + const message = { + type: 'track', + event: 'Impressions Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + timestamp: '2024-03-03T00:29:12.117+05:30', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const expectedPayload = { + beaconIssued: '2024-03-04T15:32:56.409Z', + clientName: 'test-client', + rank: 1, + trackingData: 'dummy-tracking-data', + ts: '2024-03-03T00:29:12.117+05:30', + }; + const payload = constructFullPayload(eventName, message, Config); + expect(payload).toEqual(expectedPayload); + }); + it('should throw error if required value is missing for IMPRESSIONS event', () => { + const eventName = 'impressions'; + const message = { + type: 'track', + event: 'Impressions Event', + properties: { + tracking_data: '', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + timestamp: '2024-03-03T00:29:12.117+05:30', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + try { + const payload = constructFullPayload(eventName, message, Config); + } catch (error) { + expect(error.message).toEqual('Missing required value from "properties.tracking_data"'); + } + }); + + it('should construct payload for CLICKS event', () => { + const eventName = 'clicks'; + const message = { + type: 'track', + event: 'Clicks Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + anonymousId: '1234', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const expectedPayload = { + beaconIssued: '2024-03-04T15:32:56.409Z', + clientName: 'test-client', + rank: 1, + trackingData: 'dummy-tracking-data', + userGuid: '1234', + overrides: null, + testVersionOverride: null, + }; + const payload = constructFullPayload(eventName, message, Config); + expect(payload).toEqual(expectedPayload); + }); + it('should construct payload with non-null value if overrides and testVersionOverride are enable and values for these are provided for CLICKS event ', () => { + const eventName = 'clicks'; + const message = { + type: 'track', + event: 'Clicks Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + overrides: 'overridden-value', + testVersionOverride: 1, + }, + anonymousId: '1234', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + overrides: true, + testVersionOverride: false, + }; + const expectedPayload = { + beaconIssued: '2024-03-04T15:32:56.409Z', + clientName: 'test-client', + rank: 1, + trackingData: 'dummy-tracking-data', + userGuid: '1234', + overrides: 'overridden-value', + testVersionOverride: null, + }; + const payload = constructFullPayload(eventName, message, Config); + expect(payload).toEqual(expectedPayload); + }); + it('should throw error if required value is missing for CLICKS event', () => { + const eventName = 'clicks'; + const message = { + type: 'track', + event: 'Clicks Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + try { + const payload = constructFullPayload(eventName, message, Config); + } catch (error) { + expect(error.message).toEqual('Missing required value from "userId"'); + } + }); + + it('should construct payload for CONVERSIONS event', () => { + const eventName = 'conversions'; + const message = { + type: 'track', + event: 'Conversions Event', + properties: { + currency: 'USD', + order_id: '123', + bidders: [ + { + bidder: 'dummy-bidder-id', + count: 1, + base_price: 100.1, + }, + ], + }, + context: { + locale: 'en-US', + ip: '127.0.0.1', + }, + timestamp: '2024-03-03T00:29:12.117+05:30', + anonymousId: '1234', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const expectedPayload = { + client_name: 'test-client', + culture: 'en-US', + currency: 'USD', + transaction_id: '123', + unixtime: 1709405952, + userGuid: '1234', + user_ip: '127.0.0.1', + bidders: [ + { + bidder: 'dummy-bidder-id', + count: 1, + base_price: 100.1, + }, + ], + }; + const payload = constructFullPayload(eventName, message, Config); + expect(payload).toEqual(expectedPayload); + }); + it('should throw error if required value is missing for CONVERSIONS event', () => { + const eventName = 'conversions'; + const message = { + type: 'track', + event: 'Conversions Event', + properties: { + currency: 'USD', + order_id: '123', + bidders: [ + { + bidder: 'dummy-bidder-id', + count: 1, + base_price: 100.1, + }, + ], + }, + context: { + ip: '127.0.0.1', + }, + timestamp: '2024-03-03T00:29:12.117+05:30', + anonymousId: '1234', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + try { + const payload = constructFullPayload(eventName, message, Config); + } catch (error) { + expect(error.message).toEqual('Missing required value from "context.locale"'); + } + }); + + it('should throw error for unsupported event', () => { + const eventName = 'test'; + const message = {}; + const Config = {}; + expect(() => constructFullPayload(eventName, message, Config).toThrow(InstrumentationError)); + expect(() => + constructFullPayload(eventName, message, Config).toThrow('event name test is not supported.'), + ); + }); +}); + +describe('constructResponse', () => { + it('should construct response for IMPRESSIONS event', () => { + const eventName = 'impressions'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const payload = { + beaconIssued: '2024-03-04T15:32:56.409Z', + clientName: 'test-client', + rank: 1, + trackingData: 'dummy-tracking-data', + ts: '2024-03-03T00:29:12.117+05:30', + }; + const expectedResponse = { + endpoint: 'https://www.test-client.com?action=impression', + headers: { + accept: 'application/json', + }, + method: 'GET', + params: payload, + }; + const response = constructResponse(eventName, Config, payload); + expect(response).toMatchObject(expectedResponse); + }); + + it('should construct response for CLICKS event', () => { + const eventName = 'clicks'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const payload = { + beaconIssued: '2024-03-04T15:32:56.409Z', + clientName: 'test-client', + rank: 1, + trackingData: 'dummy-tracking-data', + userGuid: '1234', + }; + const expectedResponse = { + endpoint: 'https://www.test-client.com?action=click', + headers: { + accept: 'application/json', + }, + method: 'GET', + params: payload, + }; + const response = constructResponse(eventName, Config, payload); + expect(response).toMatchObject(expectedResponse); + }); + + it('should construct response for CONVERSIONS event', () => { + const eventName = 'conversions'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const payload = { + client_name: 'test-client', + culture: 'en-US', + currency: 'USD', + transaction_id: '123', + unixtime: 1709405952, + userGuid: '1234', + user_ip: '127.0.0.1', + bidders: [ + { + bidder: 'dummy-bidder-id', + count: 1, + base_price: 100.1, + }, + ], + }; + + const expectedResponse = { + endpoint: 'https://www.test-client.com/conversion', + headers: { + accept: 'application/json', + 'content-type': 'application/json', + }, + method: 'POST', + body: { + JSON: payload, + }, + }; + const response = constructResponse(eventName, Config, payload); + expect(response).toMatchObject(expectedResponse); + }); + + it('should throw error for unsupported event', () => { + const eventName = 'test'; + const Config = {}; + const payload = {}; + expect(() => constructResponse(eventName, Config, payload).toThrow(InstrumentationError)); + expect(() => + constructResponse(eventName, Config, payload).toThrow('event name test is not supported.'), + ); + }); +}); diff --git a/src/features.json b/src/features.json index 6d2cac9340..49b82e722a 100644 --- a/src/features.json +++ b/src/features.json @@ -70,7 +70,8 @@ "KOALA": true, "LINKEDIN_ADS": true, "BLOOMREACH": true, - "MOVABLE_INK": true + "MOVABLE_INK": true, + "KODDI": true }, "regulations": [ "BRAZE", From d4a82e2d2df7ee86c4b149bca7e0c12be3a6a545 Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Mon, 13 May 2024 18:39:09 +0530 Subject: [PATCH 5/8] fix: fixed unit test issue --- src/cdk/v2/destinations/koddi/utils.test.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cdk/v2/destinations/koddi/utils.test.js b/src/cdk/v2/destinations/koddi/utils.test.js index e31bd6d9a7..ed12ecd5dd 100644 --- a/src/cdk/v2/destinations/koddi/utils.test.js +++ b/src/cdk/v2/destinations/koddi/utils.test.js @@ -43,8 +43,8 @@ describe('getEndpoint', () => { apiBaseUrl: 'https://www.test-client.com', clientName: 'test-client', }; - expect(() => getEndpoint(eventName, Config).toThrow(InstrumentationError)); - expect(() => getEndpoint(eventName, Config).toThrow('event name test is not supported.')); + expect(() => getEndpoint(eventName, Config)).toThrow(InstrumentationError); + expect(() => getEndpoint(eventName, Config)).toThrow('event name test is not supported.'); }); }); @@ -91,7 +91,7 @@ describe('validateBidders', () => { expect(() => validateBidders(bidders)).toThrow('base_price is not present. Aborting.'); }); - it('should not throw error if all fields are present for all bidders', () => { + it('should not throw error if all required fields are present for all bidders', () => { const bidders = [ { bidder: 'bidder1', alternate_bidder: 'alternate1', count: 1, base_price: 100 }, { bidder: 'bidder2', alternate_bidder: 'alternate2', count: 2, base_price: 200 }, @@ -315,9 +315,9 @@ describe('constructFullPayload', () => { const eventName = 'test'; const message = {}; const Config = {}; - expect(() => constructFullPayload(eventName, message, Config).toThrow(InstrumentationError)); - expect(() => - constructFullPayload(eventName, message, Config).toThrow('event name test is not supported.'), + expect(() => constructFullPayload(eventName, message, Config)).toThrow(InstrumentationError); + expect(() => constructFullPayload(eventName, message, Config)).toThrow( + 'event name test is not supported.', ); }); }); @@ -415,9 +415,9 @@ describe('constructResponse', () => { const eventName = 'test'; const Config = {}; const payload = {}; - expect(() => constructResponse(eventName, Config, payload).toThrow(InstrumentationError)); - expect(() => - constructResponse(eventName, Config, payload).toThrow('event name test is not supported.'), + expect(() => constructResponse(eventName, Config, payload)).toThrow(InstrumentationError); + expect(() => constructResponse(eventName, Config, payload)).toThrow( + 'event name test is not supported.', ); }); }); From 189cf9367a907dc1848257733e13713245458579 Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Tue, 14 May 2024 20:40:04 +0530 Subject: [PATCH 6/8] fix: added componenet test --- src/cdk/v2/destinations/koddi/rtWorkflow.yaml | 16 +- .../integrations/destinations/koddi/common.ts | 78 ++++++ .../destinations/koddi/processor/clicks.ts | 70 ++++++ .../koddi/processor/conversions.ts | 142 +++++++++++ .../destinations/koddi/processor/data.ts | 5 + .../koddi/processor/impressions.ts | 70 ++++++ .../destinations/koddi/router/data.ts | 232 ++++++++++++++++++ 7 files changed, 603 insertions(+), 10 deletions(-) create mode 100644 test/integrations/destinations/koddi/common.ts create mode 100644 test/integrations/destinations/koddi/processor/clicks.ts create mode 100644 test/integrations/destinations/koddi/processor/conversions.ts create mode 100644 test/integrations/destinations/koddi/processor/data.ts create mode 100644 test/integrations/destinations/koddi/processor/impressions.ts create mode 100644 test/integrations/destinations/koddi/router/data.ts diff --git a/src/cdk/v2/destinations/koddi/rtWorkflow.yaml b/src/cdk/v2/destinations/koddi/rtWorkflow.yaml index 30dd3fdd95..dd438a911c 100644 --- a/src/cdk/v2/destinations/koddi/rtWorkflow.yaml +++ b/src/cdk/v2/destinations/koddi/rtWorkflow.yaml @@ -1,8 +1,7 @@ bindings: - - path: ./config - name: handleRtTfSingleEventError path: ../../../../v0/util/index - - path: ./utils + steps: - name: validateInput template: | @@ -16,20 +15,17 @@ steps: - name: successfulEvents template: | $.outputs.transform#idx.output.({ - "output": .body.JSON.events[0], + "batchedRequest": ., + "batched": false, "destination": ^[idx].destination, - "metadata": ^[idx].metadata + "metadata": ^[idx].metadata[], + "statusCode": 200 })[] - name: failedEvents template: | $.outputs.transform#idx.error.( $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) )[] - - name: batchSuccessfulEvents - description: Batches the successfulEvents - template: | - $.batchResponseBuilder($.outputs.successfulEvents); - - name: finalPayload template: | - [...$.outputs.failedEvents, ...$.outputs.batchSuccessfulEvents] + [...$.outputs.successfulEvents, ...$.outputs.failedEvents] diff --git a/test/integrations/destinations/koddi/common.ts b/test/integrations/destinations/koddi/common.ts new file mode 100644 index 0000000000..ec83a0f446 --- /dev/null +++ b/test/integrations/destinations/koddi/common.ts @@ -0,0 +1,78 @@ +import { Destination } from '../../../../src/types'; + +const destType = 'koddi'; +const destTypeInUpperCase = 'KODDI'; +const displayName = 'Koddi'; +const channel = 'web'; +const destination: Destination = { + Config: { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }, + DestinationDefinition: { + DisplayName: displayName, + ID: '123', + Name: destTypeInUpperCase, + Config: { cdkV2Enabled: true }, + }, + Enabled: true, + ID: '123', + Name: destTypeInUpperCase, + Transformations: [], + WorkspaceID: 'test-workspace-id', +}; + +const processorInstrumentationErrorStatTags = { + destType: destTypeInUpperCase, + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +const RouterInstrumentationErrorStatTags = { + ...processorInstrumentationErrorStatTags, + feature: 'router', +}; + +const getHeader = { + accept: 'application/json', +}; + +const postHeader = { + ...getHeader, + 'content-type': 'application/json', +}; + +const bidders = [ + { + bidder: 'bidder1', + alternate_bidder: 'alternate1', + count: 1, + base_price: 100, + total_price: 227, + }, +]; + +const alternateBidders = [ + { + count: 1, + base_price: 100, + total_price: 227, + }, +]; + +export { + destType, + channel, + destination, + processorInstrumentationErrorStatTags, + RouterInstrumentationErrorStatTags, + getHeader, + postHeader, + bidders, + alternateBidders, +}; diff --git a/test/integrations/destinations/koddi/processor/clicks.ts b/test/integrations/destinations/koddi/processor/clicks.ts new file mode 100644 index 0000000000..6270c0468d --- /dev/null +++ b/test/integrations/destinations/koddi/processor/clicks.ts @@ -0,0 +1,70 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, channel, destination, getHeader } from '../common'; + +export const clicks: ProcessorTestData[] = [ + { + id: 'Clicks-test', + name: destType, + description: 'Clicks call: Example Clicks Event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Clicks Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventName: 'Clicks', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'GET', + endpoint: destination.Config.apiBaseUrl + '?action=click', + headers: getHeader, + userId: '', + params: { + trackingData: 'dummy-tracking-data', + rank: 1, + beaconIssued: '2024-03-04T15:32:56.409Z', + userGuid: 'userId123', + clientName: destination.Config.clientName, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/koddi/processor/conversions.ts b/test/integrations/destinations/koddi/processor/conversions.ts new file mode 100644 index 0000000000..35d1670b95 --- /dev/null +++ b/test/integrations/destinations/koddi/processor/conversions.ts @@ -0,0 +1,142 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { + destType, + channel, + destination, + postHeader, + bidders, + alternateBidders, + processorInstrumentationErrorStatTags, +} from '../common'; + +export const conversions: ProcessorTestData[] = [ + { + id: 'Conversions-test-1', + name: destType, + description: 'Conversions call: Example Conversions Event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Conversions Event', + properties: { + currency: 'USD', + transaction_id: 'ABC123', + bidders, + }, + context: { + locale: 'en-US', + ip: '127.0.0.1', + }, + integrations: { + All: true, + koddi: { + eventName: 'Conversions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: destination.Config.apiBaseUrl + '/conversion', + headers: postHeader, + userId: '', + JSON: { + client_name: destination.Config.clientName, + culture: 'en-US', + currency: 'USD', + transaction_id: 'ABC123', + unixtime: 1709566376, + userGuid: 'userId123', + user_ip: '127.0.0.1', + bidders: bidders, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'Conversions-test-2', + name: destType, + description: 'Conversions call: Example Conversions Event with missing required field', + scenario: 'Framework+Business', + successCriteria: 'Response should contain error and status code should be 400', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Conversions Event', + properties: { + currency: 'USD', + transaction_id: 'ABC123', + alternateBidders, + }, + context: { + locale: 'en-US', + ip: '127.0.0.1', + }, + integrations: { + All: true, + koddi: { + eventName: 'Conversions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Missing required value from "properties.bidders": Workflow: procWorkflow, Step: preparePayload, ChildStep: undefined, OriginalError: Missing required value from "properties.bidders"', + statusCode: 400, + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/koddi/processor/data.ts b/test/integrations/destinations/koddi/processor/data.ts new file mode 100644 index 0000000000..5c3d7da472 --- /dev/null +++ b/test/integrations/destinations/koddi/processor/data.ts @@ -0,0 +1,5 @@ +import { impressions } from './impressions'; +import { clicks } from './clicks'; +import { conversions } from './conversions'; + +export const data = [...impressions, ...clicks, ...conversions]; diff --git a/test/integrations/destinations/koddi/processor/impressions.ts b/test/integrations/destinations/koddi/processor/impressions.ts new file mode 100644 index 0000000000..35ffcad22b --- /dev/null +++ b/test/integrations/destinations/koddi/processor/impressions.ts @@ -0,0 +1,70 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, channel, destination, getHeader } from '../common'; + +export const impressions: ProcessorTestData[] = [ + { + id: 'Impressions-test', + name: destType, + description: 'Impressions call: Example Impression Event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Impression Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventName: 'Impressions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'GET', + endpoint: destination.Config.apiBaseUrl + '?action=impression', + headers: getHeader, + userId: '', + params: { + trackingData: 'dummy-tracking-data', + rank: 1, + beaconIssued: '2024-03-04T15:32:56.409Z', + ts: '2024-03-04T15:32:56.409Z', + clientName: destination.Config.clientName, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/koddi/router/data.ts b/test/integrations/destinations/koddi/router/data.ts new file mode 100644 index 0000000000..f5fae9bfd4 --- /dev/null +++ b/test/integrations/destinations/koddi/router/data.ts @@ -0,0 +1,232 @@ +import { RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; +import { + destType, + channel, + destination, + getHeader, + postHeader, + RouterInstrumentationErrorStatTags, + bidders, +} from '../common'; + +const routerRequest: RouterTransformationRequest = { + input: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Impression Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventName: 'Impressions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Clicks Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventName: 'Clicks', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(2), + }, + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Conversions Event', + properties: { + currency: 'USD', + transaction_id: 'ABC123', + bidders, + }, + context: { + locale: 'en-US', + ip: '127.0.0.1', + }, + integrations: { + All: true, + koddi: { + eventName: 'Conversions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(3), + }, + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Impression Event', + properties: { + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventName: 'Impressions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(4), + }, + ], + destType, +}; + +export const data = [ + { + id: 'koddi-router-test', + name: destType, + description: 'Basic Router Test to test payloads and missing field error', + scenario: 'Framework', + successCriteria: + 'Some events should be transformed successfully and some should fail for missing fields and status code should be 200', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: routerRequest, + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batched: false, + batchedRequest: { + version: '1', + type: 'REST', + method: 'GET', + endpoint: destination.Config.apiBaseUrl + '?action=impression', + headers: getHeader, + params: { + trackingData: 'dummy-tracking-data', + rank: 1, + beaconIssued: '2024-03-04T15:32:56.409Z', + ts: '2024-03-04T15:32:56.409Z', + clientName: destination.Config.clientName, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + destination, + metadata: [generateMetadata(1)], + statusCode: 200, + }, + { + batched: false, + batchedRequest: { + version: '1', + type: 'REST', + method: 'GET', + endpoint: destination.Config.apiBaseUrl + '?action=click', + headers: getHeader, + params: { + trackingData: 'dummy-tracking-data', + rank: 1, + beaconIssued: '2024-03-04T15:32:56.409Z', + userGuid: 'userId123', + clientName: destination.Config.clientName, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + destination, + metadata: [generateMetadata(2)], + statusCode: 200, + }, + { + batched: false, + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: destination.Config.apiBaseUrl + '/conversion', + headers: postHeader, + params: {}, + body: { + JSON: { + client_name: 'test-client', + culture: 'en-US', + currency: 'USD', + transaction_id: 'ABC123', + unixtime: 1709566376, + userGuid: 'userId123', + user_ip: '127.0.0.1', + bidders: bidders, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + destination, + metadata: [generateMetadata(3)], + statusCode: 200, + }, + { + batched: false, + error: 'Missing required value from "properties.tracking_data"', + destination, + metadata: [generateMetadata(4)], + statTags: RouterInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + }, +]; From 31e6460ccc0c18014ebf67eab23b59abe5d81ef6 Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Wed, 15 May 2024 18:55:50 +0530 Subject: [PATCH 7/8] fix: minor mapping issue in conversions --- src/cdk/v2/destinations/koddi/data/ConversionsConfig.json | 2 +- src/cdk/v2/destinations/koddi/utils.test.js | 2 +- test/integrations/destinations/koddi/processor/conversions.ts | 2 +- test/integrations/destinations/koddi/router/data.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json b/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json index 0d49e39c32..495574f198 100644 --- a/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json +++ b/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json @@ -25,7 +25,7 @@ "sourceKeys": "userId", "sourceFromGenericMap": true, "required": true, - "destKey": "userGuid" + "destKey": "user_guid" }, { "sourceKeys": "context.device.type", diff --git a/src/cdk/v2/destinations/koddi/utils.test.js b/src/cdk/v2/destinations/koddi/utils.test.js index ed12ecd5dd..59a02b62a0 100644 --- a/src/cdk/v2/destinations/koddi/utils.test.js +++ b/src/cdk/v2/destinations/koddi/utils.test.js @@ -265,7 +265,7 @@ describe('constructFullPayload', () => { currency: 'USD', transaction_id: '123', unixtime: 1709405952, - userGuid: '1234', + user_guid: '1234', user_ip: '127.0.0.1', bidders: [ { diff --git a/test/integrations/destinations/koddi/processor/conversions.ts b/test/integrations/destinations/koddi/processor/conversions.ts index 35d1670b95..7c8494d258 100644 --- a/test/integrations/destinations/koddi/processor/conversions.ts +++ b/test/integrations/destinations/koddi/processor/conversions.ts @@ -70,7 +70,7 @@ export const conversions: ProcessorTestData[] = [ currency: 'USD', transaction_id: 'ABC123', unixtime: 1709566376, - userGuid: 'userId123', + user_guid: 'userId123', user_ip: '127.0.0.1', bidders: bidders, }, diff --git a/test/integrations/destinations/koddi/router/data.ts b/test/integrations/destinations/koddi/router/data.ts index f5fae9bfd4..85f1319e39 100644 --- a/test/integrations/destinations/koddi/router/data.ts +++ b/test/integrations/destinations/koddi/router/data.ts @@ -202,7 +202,7 @@ export const data = [ currency: 'USD', transaction_id: 'ABC123', unixtime: 1709566376, - userGuid: 'userId123', + user_guid: 'userId123', user_ip: '127.0.0.1', bidders: bidders, }, From 7c0d963d3ee87a3ed5712492300dc50768c529de Mon Sep 17 00:00:00 2001 From: Manish Kumar Date: Mon, 20 May 2024 14:20:40 +0530 Subject: [PATCH 8/8] fix: resolving comments --- src/cdk/v2/destinations/koddi/config.js | 10 ++- .../v2/destinations/koddi/procWorkflow.yaml | 14 ++-- src/cdk/v2/destinations/koddi/utils.js | 40 +++++----- src/cdk/v2/destinations/koddi/utils.test.js | 78 +++++++++---------- .../destinations/koddi/processor/clicks.ts | 2 +- .../koddi/processor/conversions.ts | 4 +- .../koddi/processor/impressions.ts | 2 +- .../destinations/koddi/router/data.ts | 38 ++++++++- 8 files changed, 110 insertions(+), 78 deletions(-) diff --git a/src/cdk/v2/destinations/koddi/config.js b/src/cdk/v2/destinations/koddi/config.js index fa595dc627..927e1858fc 100644 --- a/src/cdk/v2/destinations/koddi/config.js +++ b/src/cdk/v2/destinations/koddi/config.js @@ -1,6 +1,12 @@ const { getMappingConfig } = require('../../../../v0/util'); -const EVENT_NAMES = { +/** + * ref :- https://developers.koddi.com/reference/winning-ads + * impressions - https://developers.koddi.com/reference/impressions-1 + * clicks - https://developers.koddi.com/reference/clicks-1 + * conversions - https://developers.koddi.com/reference/conversions-1 + */ +const EVENT_TYPES = { IMPRESSIONS: 'impressions', CLICKS: 'clicks', CONVERSIONS: 'conversions', @@ -24,7 +30,7 @@ const CONFIG_CATEGORIES = { const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); module.exports = { - EVENT_NAMES, + EVENT_TYPES, CONFIG_CATEGORIES, MAPPING_CONFIG, IMPRESSIONS_CONFIG: MAPPING_CONFIG[CONFIG_CATEGORIES.IMPRESSIONS.name], diff --git a/src/cdk/v2/destinations/koddi/procWorkflow.yaml b/src/cdk/v2/destinations/koddi/procWorkflow.yaml index 28d062d4a4..cc3f0166dc 100644 --- a/src/cdk/v2/destinations/koddi/procWorkflow.yaml +++ b/src/cdk/v2/destinations/koddi/procWorkflow.yaml @@ -11,25 +11,23 @@ steps: - name: messageType template: | .message.type.toLowerCase(); - - name: eventName + - name: eventType template: | - .message.integrations.koddi.eventName.toLowerCase(); + .message.integrations.koddi.eventType.toLowerCase(); - name: validateInput template: | let messageType = $.outputs.messageType; - let eventName = $.outputs.eventName; + let eventType = $.outputs.eventType; $.assert(messageType, "message Type is not present. Aborting message."); $.assert(messageType in {{$.EventType.([.TRACK])}}, "message type " + messageType + " is not supported"); - $.assert(eventName in {{$.EVENT_NAMES.([.IMPRESSIONS, .CLICKS, .CONVERSIONS])}}, "event name " + eventName + " is not supported"); - $.assert(.message.event, "Event name is not present. Aborting"); - $.assert(typeof .message.event === "string", "event name should be a string"); + $.assert(eventType in {{$.EVENT_TYPES.([.IMPRESSIONS, .CLICKS, .CONVERSIONS])}}, "event type " + eventType + " is not supported"); $.assertConfig(.destination.Config.apiBaseUrl, "API Base URL is not present. Aborting"); $.assertConfig(.destination.Config.clientName, "Client Name is not present. Aborting"); - name: preparePayload template: | - const payload = $.constructFullPayload($.outputs.eventName, .message, .destination.Config); + const payload = $.constructFullPayload($.outputs.eventType, .message, .destination.Config); $.context.payload = $.removeUndefinedAndNullValues(payload); - name: buildResponse template: | - const response = $.constructResponse($.outputs.eventName, .destination.Config, $.context.payload); + const response = $.constructResponse($.outputs.eventType, .destination.Config, $.context.payload); response diff --git a/src/cdk/v2/destinations/koddi/utils.js b/src/cdk/v2/destinations/koddi/utils.js index f6521d1a66..13014e2e7c 100644 --- a/src/cdk/v2/destinations/koddi/utils.js +++ b/src/cdk/v2/destinations/koddi/utils.js @@ -1,5 +1,5 @@ const { InstrumentationError } = require('@rudderstack/integrations-lib'); -const { EVENT_NAMES, IMPRESSIONS_CONFIG, CLICKS_CONFIG, CONVERSIONS_CONFIG } = require('./config'); +const { EVENT_TYPES, IMPRESSIONS_CONFIG, CLICKS_CONFIG, CONVERSIONS_CONFIG } = require('./config'); const { constructPayload, defaultRequestConfig, @@ -31,19 +31,19 @@ const validateBidders = (bidders) => { /** * This function constructs payloads based upon mappingConfig for all calls. - * @param {*} eventName + * @param {*} eventType * @param {*} message * @param {*} Config * @returns */ -const constructFullPayload = (eventName, message, Config) => { +const constructFullPayload = (eventType, message, Config) => { let payload; - switch (eventName) { - case EVENT_NAMES.IMPRESSIONS: + switch (eventType) { + case EVENT_TYPES.IMPRESSIONS: payload = constructPayload(message, IMPRESSIONS_CONFIG); payload.clientName = Config.clientName; break; - case EVENT_NAMES.CLICKS: + case EVENT_TYPES.CLICKS: payload = constructPayload(message, CLICKS_CONFIG); payload.clientName = Config.clientName; if (!Config.testVersionOverride) { @@ -53,53 +53,53 @@ const constructFullPayload = (eventName, message, Config) => { payload.overrides = null; } break; - case EVENT_NAMES.CONVERSIONS: + case EVENT_TYPES.CONVERSIONS: payload = constructPayload(message, CONVERSIONS_CONFIG); payload.client_name = Config.clientName; payload.unixtime = toUnixTimestamp(payload.unixtime); validateBidders(payload.bidders); break; default: - throw new InstrumentationError(`event name ${eventName} is not supported.`); + throw new InstrumentationError(`event type ${eventType} is not supported.`); } return payload; }; -const getEndpoint = (eventName, Config) => { +const getEndpoint = (eventType, Config) => { let endpoint = stripTrailingSlash(Config.apiBaseUrl); - switch (eventName) { - case EVENT_NAMES.IMPRESSIONS: + switch (eventType) { + case EVENT_TYPES.IMPRESSIONS: endpoint += '?action=impression'; break; - case EVENT_NAMES.CLICKS: + case EVENT_TYPES.CLICKS: endpoint += '?action=click'; break; - case EVENT_NAMES.CONVERSIONS: + case EVENT_TYPES.CONVERSIONS: endpoint += '/conversion'; break; default: - throw new InstrumentationError(`event name ${eventName} is not supported.`); + throw new InstrumentationError(`event type ${eventType} is not supported.`); } return endpoint; }; /** * This function constructs response based upon event. - * @param {*} eventName + * @param {*} eventType * @param {*} Config * @param {*} payload * @returns */ -const constructResponse = (eventName, Config, payload) => { - if (!Object.values(EVENT_NAMES).includes(eventName)) { - throw new InstrumentationError(`event name ${eventName} is not supported.`); +const constructResponse = (eventType, Config, payload) => { + if (!Object.values(EVENT_TYPES).includes(eventType)) { + throw new InstrumentationError(`event type ${eventType} is not supported.`); } const response = defaultRequestConfig(); - response.endpoint = getEndpoint(eventName, Config); + response.endpoint = getEndpoint(eventType, Config); response.headers = { accept: 'application/json', }; - if (eventName === EVENT_NAMES.CONVERSIONS) { + if (eventType === EVENT_TYPES.CONVERSIONS) { response.body.JSON = payload; response.method = 'POST'; response.headers = { diff --git a/src/cdk/v2/destinations/koddi/utils.test.js b/src/cdk/v2/destinations/koddi/utils.test.js index 59a02b62a0..2c1f660f70 100644 --- a/src/cdk/v2/destinations/koddi/utils.test.js +++ b/src/cdk/v2/destinations/koddi/utils.test.js @@ -8,43 +8,43 @@ const { InstrumentationError } = require('@rudderstack/integrations-lib'); describe('getEndpoint', () => { it('returns the correct endpoint for IMPRESSIONS event', () => { - const eventName = 'impressions'; + const eventType = 'impressions'; const Config = { apiBaseUrl: 'https://www.test-client.com/', clientName: 'test-client', }; - const result = getEndpoint(eventName, Config); + const result = getEndpoint(eventType, Config); expect(result).toEqual('https://www.test-client.com?action=impression'); }); it('returns the correct endpoint for CLICKS event', () => { - const eventName = 'clicks'; + const eventType = 'clicks'; const Config = { apiBaseUrl: 'https://www.test-client.com', clientName: 'test-client', }; - const result = getEndpoint(eventName, Config); + const result = getEndpoint(eventType, Config); expect(result).toEqual('https://www.test-client.com?action=click'); }); it('returns the correct endpoint for IMPRESSIONS event', () => { - const eventName = 'conversions'; + const eventType = 'conversions'; const Config = { apiBaseUrl: 'https://www.test-client.com', clientName: 'test-client', }; - const result = getEndpoint(eventName, Config); + const result = getEndpoint(eventType, Config); expect(result).toEqual('https://www.test-client.com/conversion'); }); it('should throw error for unsupported event', () => { - const eventName = 'test'; + const eventType = 'test'; const Config = { apiBaseUrl: 'https://www.test-client.com', clientName: 'test-client', }; - expect(() => getEndpoint(eventName, Config)).toThrow(InstrumentationError); - expect(() => getEndpoint(eventName, Config)).toThrow('event name test is not supported.'); + expect(() => getEndpoint(eventType, Config)).toThrow(InstrumentationError); + expect(() => getEndpoint(eventType, Config)).toThrow('event type test is not supported.'); }); }); @@ -84,9 +84,7 @@ describe('validateBidders', () => { }); it('should throw error if base_price is not present', () => { - const bidders = [ - { bidder: 'bidder1', alternate_bidder: 'alternate1', count: 1 }, // Missing base_price - ]; + const bidders = [{ bidder: 'bidder1', alternate_bidder: 'alternate1', count: 1 }]; expect(() => validateBidders(bidders)).toThrow(InstrumentationError); expect(() => validateBidders(bidders)).toThrow('base_price is not present. Aborting.'); }); @@ -102,7 +100,7 @@ describe('validateBidders', () => { describe('constructFullPayload', () => { it('should construct payload for IMPRESSIONS event', () => { - const eventName = 'impressions'; + const eventType = 'impressions'; const message = { type: 'track', event: 'Impressions Event', @@ -124,11 +122,11 @@ describe('constructFullPayload', () => { trackingData: 'dummy-tracking-data', ts: '2024-03-03T00:29:12.117+05:30', }; - const payload = constructFullPayload(eventName, message, Config); + const payload = constructFullPayload(eventType, message, Config); expect(payload).toEqual(expectedPayload); }); it('should throw error if required value is missing for IMPRESSIONS event', () => { - const eventName = 'impressions'; + const eventType = 'impressions'; const message = { type: 'track', event: 'Impressions Event', @@ -144,14 +142,14 @@ describe('constructFullPayload', () => { clientName: 'test-client', }; try { - const payload = constructFullPayload(eventName, message, Config); + const payload = constructFullPayload(eventType, message, Config); } catch (error) { expect(error.message).toEqual('Missing required value from "properties.tracking_data"'); } }); it('should construct payload for CLICKS event', () => { - const eventName = 'clicks'; + const eventType = 'clicks'; const message = { type: 'track', event: 'Clicks Event', @@ -175,11 +173,11 @@ describe('constructFullPayload', () => { overrides: null, testVersionOverride: null, }; - const payload = constructFullPayload(eventName, message, Config); + const payload = constructFullPayload(eventType, message, Config); expect(payload).toEqual(expectedPayload); }); it('should construct payload with non-null value if overrides and testVersionOverride are enable and values for these are provided for CLICKS event ', () => { - const eventName = 'clicks'; + const eventType = 'clicks'; const message = { type: 'track', event: 'Clicks Event', @@ -207,11 +205,11 @@ describe('constructFullPayload', () => { overrides: 'overridden-value', testVersionOverride: null, }; - const payload = constructFullPayload(eventName, message, Config); + const payload = constructFullPayload(eventType, message, Config); expect(payload).toEqual(expectedPayload); }); it('should throw error if required value is missing for CLICKS event', () => { - const eventName = 'clicks'; + const eventType = 'clicks'; const message = { type: 'track', event: 'Clicks Event', @@ -226,14 +224,14 @@ describe('constructFullPayload', () => { clientName: 'test-client', }; try { - const payload = constructFullPayload(eventName, message, Config); + const payload = constructFullPayload(eventType, message, Config); } catch (error) { expect(error.message).toEqual('Missing required value from "userId"'); } }); it('should construct payload for CONVERSIONS event', () => { - const eventName = 'conversions'; + const eventType = 'conversions'; const message = { type: 'track', event: 'Conversions Event', @@ -275,11 +273,11 @@ describe('constructFullPayload', () => { }, ], }; - const payload = constructFullPayload(eventName, message, Config); + const payload = constructFullPayload(eventType, message, Config); expect(payload).toEqual(expectedPayload); }); it('should throw error if required value is missing for CONVERSIONS event', () => { - const eventName = 'conversions'; + const eventType = 'conversions'; const message = { type: 'track', event: 'Conversions Event', @@ -305,26 +303,26 @@ describe('constructFullPayload', () => { clientName: 'test-client', }; try { - const payload = constructFullPayload(eventName, message, Config); + const payload = constructFullPayload(eventType, message, Config); } catch (error) { expect(error.message).toEqual('Missing required value from "context.locale"'); } }); it('should throw error for unsupported event', () => { - const eventName = 'test'; + const eventType = 'test'; const message = {}; const Config = {}; - expect(() => constructFullPayload(eventName, message, Config)).toThrow(InstrumentationError); - expect(() => constructFullPayload(eventName, message, Config)).toThrow( - 'event name test is not supported.', + expect(() => constructFullPayload(eventType, message, Config)).toThrow(InstrumentationError); + expect(() => constructFullPayload(eventType, message, Config)).toThrow( + 'event type test is not supported.', ); }); }); describe('constructResponse', () => { it('should construct response for IMPRESSIONS event', () => { - const eventName = 'impressions'; + const eventType = 'impressions'; const Config = { apiBaseUrl: 'https://www.test-client.com', clientName: 'test-client', @@ -344,12 +342,12 @@ describe('constructResponse', () => { method: 'GET', params: payload, }; - const response = constructResponse(eventName, Config, payload); + const response = constructResponse(eventType, Config, payload); expect(response).toMatchObject(expectedResponse); }); it('should construct response for CLICKS event', () => { - const eventName = 'clicks'; + const eventType = 'clicks'; const Config = { apiBaseUrl: 'https://www.test-client.com', clientName: 'test-client', @@ -369,12 +367,12 @@ describe('constructResponse', () => { method: 'GET', params: payload, }; - const response = constructResponse(eventName, Config, payload); + const response = constructResponse(eventType, Config, payload); expect(response).toMatchObject(expectedResponse); }); it('should construct response for CONVERSIONS event', () => { - const eventName = 'conversions'; + const eventType = 'conversions'; const Config = { apiBaseUrl: 'https://www.test-client.com', clientName: 'test-client', @@ -407,17 +405,17 @@ describe('constructResponse', () => { JSON: payload, }, }; - const response = constructResponse(eventName, Config, payload); + const response = constructResponse(eventType, Config, payload); expect(response).toMatchObject(expectedResponse); }); it('should throw error for unsupported event', () => { - const eventName = 'test'; + const eventType = 'test'; const Config = {}; const payload = {}; - expect(() => constructResponse(eventName, Config, payload)).toThrow(InstrumentationError); - expect(() => constructResponse(eventName, Config, payload)).toThrow( - 'event name test is not supported.', + expect(() => constructResponse(eventType, Config, payload)).toThrow(InstrumentationError); + expect(() => constructResponse(eventType, Config, payload)).toThrow( + 'event type test is not supported.', ); }); }); diff --git a/test/integrations/destinations/koddi/processor/clicks.ts b/test/integrations/destinations/koddi/processor/clicks.ts index 6270c0468d..6101e9bafe 100644 --- a/test/integrations/destinations/koddi/processor/clicks.ts +++ b/test/integrations/destinations/koddi/processor/clicks.ts @@ -32,7 +32,7 @@ export const clicks: ProcessorTestData[] = [ integrations: { All: true, koddi: { - eventName: 'Clicks', + eventType: 'Clicks', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', diff --git a/test/integrations/destinations/koddi/processor/conversions.ts b/test/integrations/destinations/koddi/processor/conversions.ts index 7c8494d258..1647ffed7d 100644 --- a/test/integrations/destinations/koddi/processor/conversions.ts +++ b/test/integrations/destinations/koddi/processor/conversions.ts @@ -44,7 +44,7 @@ export const conversions: ProcessorTestData[] = [ integrations: { All: true, koddi: { - eventName: 'Conversions', + eventType: 'Conversions', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', @@ -114,7 +114,7 @@ export const conversions: ProcessorTestData[] = [ integrations: { All: true, koddi: { - eventName: 'Conversions', + eventType: 'Conversions', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', diff --git a/test/integrations/destinations/koddi/processor/impressions.ts b/test/integrations/destinations/koddi/processor/impressions.ts index 35ffcad22b..840ed9139f 100644 --- a/test/integrations/destinations/koddi/processor/impressions.ts +++ b/test/integrations/destinations/koddi/processor/impressions.ts @@ -32,7 +32,7 @@ export const impressions: ProcessorTestData[] = [ integrations: { All: true, koddi: { - eventName: 'Impressions', + eventType: 'Impressions', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', diff --git a/test/integrations/destinations/koddi/router/data.ts b/test/integrations/destinations/koddi/router/data.ts index 85f1319e39..1601a481e5 100644 --- a/test/integrations/destinations/koddi/router/data.ts +++ b/test/integrations/destinations/koddi/router/data.ts @@ -28,7 +28,7 @@ const routerRequest: RouterTransformationRequest = { integrations: { All: true, koddi: { - eventName: 'Impressions', + eventType: 'Impressions', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', @@ -51,7 +51,7 @@ const routerRequest: RouterTransformationRequest = { integrations: { All: true, koddi: { - eventName: 'Clicks', + eventType: 'Clicks', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', @@ -78,7 +78,7 @@ const routerRequest: RouterTransformationRequest = { integrations: { All: true, koddi: { - eventName: 'Conversions', + eventType: 'Conversions', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', @@ -100,13 +100,35 @@ const routerRequest: RouterTransformationRequest = { integrations: { All: true, koddi: { - eventName: 'Impressions', + eventType: 'Impressions', }, }, originalTimestamp: '2024-03-04T15:32:56.409Z', }, metadata: generateMetadata(4), }, + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventType: 'Unknown', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(5), + }, ], destType, }; @@ -224,6 +246,14 @@ export const data = [ statTags: RouterInstrumentationErrorStatTags, statusCode: 400, }, + { + batched: false, + error: 'event type unknown is not supported', + destination, + metadata: [generateMetadata(5)], + statTags: RouterInstrumentationErrorStatTags, + statusCode: 400, + }, ], }, },