Skip to content

Commit

Permalink
Merge branch 'develop' into fix.transformerV0Metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
Jayachand authored Apr 8, 2024
2 parents 06e0346 + 3748f24 commit db27387
Show file tree
Hide file tree
Showing 50 changed files with 991 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
},
{
"destKey": "properties.customer.email",
"sourceKeys": "email",
"sourceKeys": "emailOnly",
"required": false,
"sourceFromGenericMap": true
},
Expand Down
6 changes: 4 additions & 2 deletions src/cdk/v2/destinations/rakuten/data/propertiesMapping.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
},
{
"sourceKeys": ["properties.tr", "properties.ran_site_id", "properties.ranSiteID"],
"required": true,
"destKey": "tr"
"destKey": "tr",
"metadata": {
"defaultValue": " "
}
},
{
"sourceKeys": ["properties.land", "properties.land_time", "properties.landTime"],
Expand Down
7 changes: 4 additions & 3 deletions src/cdk/v2/destinations/reddit/procWorkflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ bindings:
path: ../../../../v0/util/index
- name: OAuthSecretError
path: '@rudderstack/integrations-lib'
- path: ./utils

steps:
- name: validateInput
Expand Down Expand Up @@ -56,14 +57,14 @@ steps:
const event_type = (eventNames.length === 0 || eventNames[0]==="") ? ({"tracking_type": "Custom", "custom_event_name": event}): ({tracking_type: eventNames[0]});
- name: customFields
condition: $.outputs.prepareTrackPayload.eventType.tracking_type === "Purchase"
condition: $.outputs.prepareTrackPayload.eventType.tracking_type in ['Purchase', 'AddToCart', 'ViewContent']
reference: 'https://ads-api.reddit.com/docs/v2/#tag/Conversions/paths/~1api~1v2.0~1conversions~1events~1%7Baccount_id%7D/post'
template: |
const revenue_in_cents = .message.properties.revenue ? Math.round(Number(.message.properties.revenue)*100)
const revenue_in_cents = $.populateRevenueField($.outputs.prepareTrackPayload.eventType.tracking_type,^.message.properties)
const customFields = .message.().({
"currency": .properties.currency,
"value_decimal": revenue_in_cents ? revenue_in_cents / 100,
"item_count": (Array.isArray(.properties.products) && .properties.products.length) || (.properties.itemCount && Number(.properties.itemCount)),
"item_count": $.outputs.prepareTrackPayload.eventType.tracking_type === 'Purchase' ? (Array.isArray(.properties.products) && .properties.products.length) || (.properties.itemCount && Number(.properties.itemCount)) : null,
"value": revenue_in_cents,
"conversion_id": .properties.conversionId || .messageId,
});
Expand Down
53 changes: 53 additions & 0 deletions src/cdk/v2/destinations/reddit/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,59 @@ const batchEvents = (successfulEvents) => {
const batchedEvents = batchEventChunks(eventChunks);
return batchedEvents;
};

const calculateDefaultRevenue = (properties) => {
// Check if working with products array
if (properties?.products && properties.products.length > 0) {
// Check if all product prices are undefined
if (properties.products.every((product) => product.price === undefined)) {
return null; // Return null if all product prices are undefined
}
// Proceed with calculation if not all prices are undefined
return properties.products.reduce(
(acc, product) => acc + (product.price || 0) * (product.quantity || 1),
0,
);
}
// For single product scenario, check if price is undefined
if (properties.price === undefined) {
return null; // Return null if price is undefined
}
// Proceed with calculation if price is defined
return properties.price * (properties.quantity ?? 1);
};

const populateRevenueField = (eventType, properties) => {
let revenueInCents;
switch (eventType) {
case 'Purchase':
revenueInCents =
properties.revenue && !Number.isNaN(properties.revenue)
? Math.round(Number(properties?.revenue) * 100)
: null;
break;
case 'AddToCart':
revenueInCents =
properties.price && !Number.isNaN(properties.price)
? Math.round(Number(properties?.price) * Number(properties?.quantity || 1) * 100)
: null;
break;
default:
// for viewContent
// eslint-disable-next-line no-case-declarations
const revenue = calculateDefaultRevenue(properties);
revenueInCents = revenue ? revenue * 100 : null;
break;
}

if (lodash.isNaN(revenueInCents)) {
return null;
}
// Return the value as it is if it's not NaN
return revenueInCents;
};
module.exports = {
batchEvents,
populateRevenueField,
calculateDefaultRevenue,
};
121 changes: 121 additions & 0 deletions src/cdk/v2/destinations/reddit/utils.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
const { calculateDefaultRevenue, populateRevenueField } = require('./utils');

describe('calculateDefaultRevenue', () => {
// Calculates revenue for a single product with defined price and quantity
it('should calculate revenue for a single product with defined price and quantity', () => {
const properties = {
price: 10,
quantity: 2,
};

const result = calculateDefaultRevenue(properties);

expect(result).toBe(20);
});

// Returns null for properties parameter being undefined
it('should return null for price parameter being undefined', () => {
const properties = { products: [{ quantity: 1 }] };

const result = calculateDefaultRevenue(properties);

expect(result).toBeNull();
});

// Calculates revenue for a single product with defined price and default quantity
it('should calculate revenue for a single product with defined price and default quantity', () => {
const properties = {
price: 10,
};

const result = calculateDefaultRevenue(properties);

expect(result).toBe(10);
});

// Calculates revenue for multiple products with defined prices and quantities
it('should calculate revenue for multiple products with defined prices and quantities', () => {
const properties = {
products: [{ price: 10, quantity: 2 }, { quantity: 3 }],
};

const result = calculateDefaultRevenue(properties);

expect(result).toBe(20);
});

// Calculates revenue for multiple products with defined prices and default quantities
it('should calculate revenue for multiple products with defined prices and default quantities', () => {
const properties = {
products: [{ price: 10 }, { price: 5 }],
};

const result = calculateDefaultRevenue(properties);

expect(result).toBe(15);
});
});

describe('populateRevenueField', () => {
// Returns revenue in cents for Purchase event type with valid revenue property
it('should return revenue in cents when Purchase event type has valid revenue property', () => {
const eventType = 'Purchase';
const properties = {
revenue: '10.50',
};
const expected = 1050;

const result = populateRevenueField(eventType, properties);

expect(result).toBe(expected);
});

// Returns null for Purchase event type with revenue property as non-numeric string
it('should return null when Purchase event type has revenue property as non-numeric string', () => {
const eventType = 'Purchase';
const properties = {
revenue: 'invalid',
};
const expected = null;

const result = populateRevenueField(eventType, properties);

expect(result).toBe(expected);
});

// Returns revenue in cents for AddToCart event type with valid price and quantity properties
it('should return revenue in cents when AddToCart event type has valid price and quantity properties', () => {
const eventType = 'AddToCart';
const properties = {
price: '10.50',
quantity: 2,
};
const expected = 2100;

const result = populateRevenueField(eventType, properties);

expect(result).toBe(expected);
});

// Returns revenue in cents for ViewContent event type with valid properties
it('should return revenue in cents when ViewContent event type has valid properties', () => {
const eventType = 'ViewContent';
const properties = {
products: [
{
price: '10.50',
quantity: 2,
},
{
price: '5.25',
quantity: 3,
},
],
};
const expected = 3675;

const result = populateRevenueField(eventType, properties);

expect(result).toBe(expected);
});
});
5 changes: 5 additions & 0 deletions src/util/openfaas/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const FAAS_AST_VID = 'ast';
const FAAS_AST_FN_NAME = 'fn-ast';
const CUSTOM_NETWORK_POLICY_WORKSPACE_IDS = process.env.CUSTOM_NETWORK_POLICY_WORKSPACE_IDS || '';
const customNetworkPolicyWorkspaceIds = CUSTOM_NETWORK_POLICY_WORKSPACE_IDS.split(',');
const CUSTOMER_TIER = process.env.CUSTOMER_TIER || 'shared';

// Initialise node cache
const functionListCache = new NodeCache();
Expand Down Expand Up @@ -151,6 +152,10 @@ const deployFaasFunction = async (
'com.openfaas.scale.min': FAAS_MIN_PODS_IN_TEXT,
transformationId: trMetadata.transformationId,
workspaceId: trMetadata.workspaceId,
team: 'data-management',
service: 'openfaas-fn',
customer: 'shared',
'customer-tier': CUSTOMER_TIER,
};
if (
trMetadata.workspaceId &&
Expand Down
7 changes: 2 additions & 5 deletions src/util/redis/testData/shopify_source.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,8 @@
}
},
"output": {
"outputToSource": {
"body": "T0s=",
"contentType": "text/plain"
},
"statusCode": 200
"error": "Error: Error setting value in Redis due Error: Connection is Closed",
"statusCode": 500
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion src/v0/destinations/active_campaign/data/ACIdentify.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[
{
"destKey": "email",
"sourceKeys": "email",
"sourceKeys": "emailOnly",
"sourceFromGenericMap": true,
"required": true
},
Expand Down
14 changes: 11 additions & 3 deletions src/v0/destinations/awin/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ const { InstrumentationError, ConfigurationError } = require('@rudderstack/integ
const { BASE_URL, ConfigCategory, mappingConfig } = require('./config');
const { defaultRequestConfig, constructPayload, simpleProcessRouterDest } = require('../../util');

const { getParams } = require('./utils');
const { getParams, trackProduct } = require('./utils');

const responseBuilder = (message, { Config }) => {
const { advertiserId, eventsToTrack } = Config;
const { event, properties } = message;
let finalParams = {};

const payload = constructPayload(message, mappingConfig[ConfigCategory.TRACK.name]);

Expand All @@ -17,8 +19,14 @@ const responseBuilder = (message, { Config }) => {
});

// if the event is present in eventsList
if (eventsList.includes(message.event)) {
if (eventsList.includes(event)) {
params = getParams(payload.params, advertiserId);
const productTrackObject = trackProduct(properties, advertiserId, params.parts);

finalParams = {
...params,
...productTrackObject,
};
} else {
throw new InstrumentationError(
"Event is not present in 'Events to Track' list. Aborting message.",
Expand All @@ -27,7 +35,7 @@ const responseBuilder = (message, { Config }) => {
}
}
const response = defaultRequestConfig();
response.params = params;
response.params = finalParams;
response.endpoint = BASE_URL;

return response;
Expand Down
51 changes: 51 additions & 0 deletions src/v0/destinations/awin/utils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const lodash = require('lodash');

/**
* Returns final params
* @param {*} params
Expand All @@ -24,6 +26,55 @@ const getParams = (parameters, advertiserId) => {
return params;
};

const areAllValuesDefined = (obj) =>
lodash.every(lodash.values(obj), (value) => !lodash.isUndefined(value));

const buildProductPayloadString = (payload) => {
// URL-encode each value, and join back with the same key.
const encodedPayload = Object.entries(payload).reduce((acc, [key, value]) => {
// Encode each value. Assuming that all values are either strings or can be
// safely converted to strings.
acc[key] = encodeURIComponent(value);
return acc;
}, {});

return `AW:P|${encodedPayload.advertiserId}|${encodedPayload.orderReference}|${encodedPayload.productId}|${encodedPayload.productName}|${encodedPayload.productItemPrice}|${encodedPayload.productQuantity}|${encodedPayload.productSku}|${encodedPayload.commissionGroupCode}|${encodedPayload.productCategory}`;
};

// ref: https://wiki.awin.com/index.php/Advertiser_Tracking_Guide/Product_Level_Tracking#PLT_Via_Conversion_Pixel
const trackProduct = (properties, advertiserId, commissionParts) => {
const transformedProductInfoObj = {};
if (
properties?.products &&
Array.isArray(properties?.products) &&
properties.products.length > 0
) {
const productsArray = properties.products;
let productIndex = 0;
productsArray.forEach((product) => {
const productPayloadNew = {
advertiserId,
orderReference: properties.order_id || properties.orderId,
productId: product.product_id || product.productId,
productName: product.name,
productItemPrice: product.price,
productQuantity: product.quantity,
productSku: product.sku || '',
commissionGroupCode: commissionParts || 'DEFAULT',
productCategory: product.category || '',
};
if (areAllValuesDefined(productPayloadNew)) {
transformedProductInfoObj[`bd[${productIndex}]`] =
buildProductPayloadString(productPayloadNew);
productIndex += 1;
}
});
}
return transformedProductInfoObj;
};

module.exports = {
getParams,
trackProduct,
buildProductPayloadString,
};
Loading

0 comments on commit db27387

Please sign in to comment.