Skip to content

Commit

Permalink
Merge branch 'develop' into fb_content_type_fix
Browse files Browse the repository at this point in the history
  • Loading branch information
sandeepdsvs authored Mar 1, 2024
2 parents fccd214 + feb256b commit 3f046fe
Show file tree
Hide file tree
Showing 33 changed files with 1,908 additions and 244 deletions.
31 changes: 31 additions & 0 deletions src/cdk/v2/destinations/ninetailed/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const { getMappingConfig } = require('../../../../v0/util');

const ConfigCategories = {
GENERAL: {
type: 'general',
name: 'generalPayloadMapping',
},
CONTEXT: {
type: 'context',
name: 'contextMapping',
},
TRACK: {
type: 'track',
name: 'trackMapping',
},
IDENTIFY: {
type: 'identify',
name: 'identifyMapping',
},
PAGE: {
type: 'page',
name: 'pageMapping',
},
};

// MAX_BATCH_SIZE : // Maximum number of events to send in a single batch
const mappingConfig = getMappingConfig(ConfigCategories, __dirname);
const batchEndpoint =
'https://experience.ninetailed.co/v2/organizations/{{organisationId}}/environments/{{environment}}/events';

module.exports = { ConfigCategories, mappingConfig, batchEndpoint, MAX_BATCH_SIZE: 200 };
43 changes: 43 additions & 0 deletions src/cdk/v2/destinations/ninetailed/data/contextMapping.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[
{
"sourceKeys": "app.name",
"required": true,
"destKey": "app.name"
},
{
"sourceKeys": "app.version",
"required": true,
"destKey": "app.version"
},
{
"sourceKeys": "campaign",
"destKey": "campaign"
},
{
"sourceKeys": "library.name",
"required": true,
"destKey": "library.name"
},
{
"sourceKeys": "library.version",
"required": true,
"destKey": "library.version"
},
{
"sourceKeys": "locale",
"destKey": "locale"
},
{
"sourceKeys": "page",
"destKey": "page"
},
{
"sourceKeys": "userAgent",
"destKey": "userAgent"
},
{
"sourceKeys": "location",
"required": true,
"destKey": "location"
}
]
25 changes: 25 additions & 0 deletions src/cdk/v2/destinations/ninetailed/data/generalPayloadMapping.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[
{
"sourceKeys": "anonymousId",
"required": true,
"destKey": "anonymousId"
},
{
"sourceKeys": "messageId",
"required": true,
"destKey": "messageId"
},
{
"sourceKeys": "channel",
"required": true,
"destKey": "channel"
},
{
"sourceKeys": "type",
"destKey": "type"
},
{
"sourceKeys": "originalTimestamp",
"destKey": "originalTimestamp"
}
]
14 changes: 14 additions & 0 deletions src/cdk/v2/destinations/ninetailed/data/identifyMapping.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"sourceKeys": "traits",
"sourceFromGenericMap": true,
"required": true,
"destKey": "traits"
},
{
"sourceKeys": "userIdOnly",
"sourceFromGenericMap": true,
"required": true,
"destKey": "userId"
}
]
7 changes: 7 additions & 0 deletions src/cdk/v2/destinations/ninetailed/data/pageMapping.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"sourceKeys": "properties",
"required": true,
"destKey": "properties"
}
]
12 changes: 12 additions & 0 deletions src/cdk/v2/destinations/ninetailed/data/trackMapping.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"sourceKeys": "properties",
"required": true,
"destKey": "properties"
},
{
"sourceKeys": "event",
"required": true,
"destKey": "event"
}
]
33 changes: 33 additions & 0 deletions src/cdk/v2/destinations/ninetailed/procWorkflow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
bindings:
- name: EventType
path: ../../../../constants
- path: ../../bindings/jsontemplate
- name: defaultRequestConfig
path: ../../../../v0/util
- name: removeUndefinedAndNullValues
path: ../../../../v0/util
- path: ./utils

steps:
- name: messageType
template: |
.message.type.toLowerCase();
- name: validateInput
template: |
let messageType = $.outputs.messageType;
$.assert(messageType, "message Type is not present. Aborting");
$.assert(messageType in {{$.EventType.([.TRACK,.IDENTIFY,.PAGE])}}, "message type " + messageType + " is not supported");
$.assertConfig(.destination.Config.organisationId, "Organisation ID is not present. Aborting");
$.assertConfig(.destination.Config.environment, "Environment is not present. Aborting");
- name: preparePayload
template: |
const payload = $.constructFullPayload(.message);
$.context.payload = $.removeUndefinedAndNullValues(payload);
- name: buildResponse
template: |
const response = $.defaultRequestConfig();
response.body.JSON.events = [$.context.payload];
response.endpoint = $.getEndpoint(.destination.Config);
response.method = "POST";
response
35 changes: 35 additions & 0 deletions src/cdk/v2/destinations/ninetailed/rtWorkflow.yaml
Original file line number Diff line number Diff line change
@@ -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]
109 changes: 109 additions & 0 deletions src/cdk/v2/destinations/ninetailed/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
const { BatchUtils } = require('@rudderstack/workflow-engine');
const config = require('./config');
const { constructPayload } = require('../../../../v0/util');

/**
* This fucntion constructs payloads based upon mappingConfig for all calls
* We build context as it has some specific payloads with default values so just breaking them down
* @param {*} message
* @returns
*/
const constructFullPayload = (message) => {
const context = constructPayload(
message?.context || {},
config.mappingConfig[config.ConfigCategories.CONTEXT.name],
);
const payload = constructPayload(
message,
config.mappingConfig[config.ConfigCategories.GENERAL.name],
);
let typeSpecifcPayload;
switch (message.type) {
case 'track':
typeSpecifcPayload = constructPayload(
message,
config.mappingConfig[config.ConfigCategories.TRACK.name],
);
break;
case 'identify':
typeSpecifcPayload = constructPayload(
message,
config.mappingConfig[config.ConfigCategories.IDENTIFY.name],
);
break;
case 'page':
typeSpecifcPayload = constructPayload(
message,
config.mappingConfig[config.ConfigCategories.PAGE.name],
);
break;
default:
break;
}
payload.context = context;
return { ...payload, ...typeSpecifcPayload }; // merge base and type-specific payloads;
};

const getEndpoint = (Config) => {
const { organisationId, environment } = Config;
return config.batchEndpoint
.replace('{{organisationId}}', organisationId)
.replace('{{environment}}', environment);
};

const mergeMetadata = (batch) => {
const metadata = [];
batch.forEach((event) => {
metadata.push(event.metadata);
});
return metadata;
};

const getMergedEvents = (batch) => {
const events = [];
batch.forEach((event) => {
events.push(event.output);
});
return events;
};

const batchBuilder = (batch) => ({
batchedRequest: {
body: {
JSON: { events: getMergedEvents(batch) },
JSON_ARRAY: {},
XML: {},
FORM: {},
},
version: '1',
type: 'REST',
method: 'POST',
endpoint: getEndpoint(batch[0].destination.Config),
headers: {
'Content-Type': 'application/json',
},
params: {},
files: {},
},
metadata: mergeMetadata(batch),
batched: true,
statusCode: 200,
destination: batch[0].destination,
});

/**
* This fucntions make chunk of successful events based on MAX_BATCH_SIZE
* and then build the response for each chunk to be returned as object of an array
* @param {*} events
* @returns
*/
const batchResponseBuilder = (events) => {
const batches = BatchUtils.chunkArrayBySizeAndLength(events, { maxItems: config.MAX_BATCH_SIZE });
const response = [];
batches.items.forEach((batch) => {
response.push(batchBuilder(batch));
});
return response;
};

module.exports = { constructFullPayload, getEndpoint, batchResponseBuilder };
3 changes: 2 additions & 1 deletion src/features.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@
"TIKTOK_AUDIENCE": true,
"REDDIT": true,
"THE_TRADE_DESK": true,
"INTERCOM": true
"INTERCOM": true,
"NINETAILED": true
},
"regulations": [
"BRAZE",
Expand Down
3 changes: 2 additions & 1 deletion src/v0/destinations/adobe_analytics/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const {
getIntegrationsObj,
removeUndefinedAndNullValues,
simpleProcessRouterDest,
validateEventAndLowerCaseConversion,
} = require('../../util');

const {
Expand Down Expand Up @@ -307,7 +308,7 @@ const processTrackEvent = (message, adobeEventName, destinationConfig, extras =
destinationConfig;
const { event: rawMessageEvent, properties } = message;
const { overrideEventString, overrideProductString } = properties;
const event = rawMessageEvent.toLowerCase();
const event = validateEventAndLowerCaseConversion(rawMessageEvent, true, true);
const adobeEventArr = adobeEventName ? adobeEventName.split(',') : [];
// adobeEventArr is an array of events which is defined as
// ["eventName", "mapped Adobe Event=mapped merchproperty's value", "mapped Adobe Event=mapped merchproperty's value", . . .]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ const processEvent = async (metadata, message, destination) => {
}

Object.values(createdPayload).forEach((data) => {
const consentObj = populateConsentForGoogleDestinations(message.properties);
const consentObj = populateConsentForGoogleDestinations(destination.Config);
response.push(responseBuilder(metadata, data, destination, message, consentObj));
});
return response;
Expand Down
28 changes: 17 additions & 11 deletions src/v0/util/googleUtils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,27 @@ const GOOGLE_ALLOWED_CONSENT_STATUS = ['UNSPECIFIED', 'UNKNOWN', 'GRANTED', 'DEN
* ref : https://developers.google.com/google-ads/api/rest/reference/rest/v15/Consent
*/

const populateConsentForGoogleDestinations = (properties) => {
const populateConsentForGoogleDestinations = (config) => {
const consent = {};

if (
properties?.userDataConsent &&
GOOGLE_ALLOWED_CONSENT_STATUS.includes(properties.userDataConsent)
) {
consent.adUserData = properties.userDataConsent;
if (config?.userDataConsent) {
if (GOOGLE_ALLOWED_CONSENT_STATUS.includes(config.userDataConsent)) {
consent.adUserData = config.userDataConsent;
} else {
consent.adUserData = 'UNKNOWN';
}
} else {
consent.adUserData = 'UNSPECIFIED';
}

if (
properties?.personalizationConsent &&
GOOGLE_ALLOWED_CONSENT_STATUS.includes(properties.personalizationConsent)
) {
consent.adPersonalization = properties.personalizationConsent;
if (config?.personalizationConsent) {
if (GOOGLE_ALLOWED_CONSENT_STATUS.includes(config.personalizationConsent)) {
consent.adPersonalization = config.personalizationConsent;
} else {
consent.adPersonalization = 'UNKNOWN';
}
} else {
consent.adPersonalization = 'UNSPECIFIED';
}
return consent;
};
Expand Down
Loading

0 comments on commit 3f046fe

Please sign in to comment.