Skip to content

Commit 227419f

Browse files
aanshi07utsabcSai Sankeerth
authored
feat: onboard topsort destination (#3913)
* chore: topsort changes * chore: boilerplates * chore: topsort changes * chore: function updates * chore: product array changes * chore: purchase event implementation * chore: purchase function updated * chore: build finalPayload * chore: few updations * chore: mockfn changes * chore: restructured files - moved impressions and clicks logic into its own file - moved purchase related logic into its own file * chore: update formatting * chore: test cases added * chore: destType updated --------- Co-authored-by: Utsab Chowdhury <[email protected]> Co-authored-by: Sai Sankeerth <[email protected]>
1 parent d40db6c commit 227419f

19 files changed

+2347
-1
lines changed

src/features.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ const defaultFeaturesConfig: FeaturesConfig = {
9393
AMAZON_AUDIENCE: true,
9494
INTERCOM_V2: true,
9595
LINKEDIN_AUDIENCE: true,
96+
TOPSORT: true,
9697
},
9798
regulations: [
9899
'BRAZE',
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
const { getMappingConfig } = require('../../util');
2+
3+
const ENDPOINT = 'https://api.topsort.com/v2/events';
4+
5+
const ConfigCategory = {
6+
TRACK: {
7+
type: 'track',
8+
name: 'TopsortTrackConfig',
9+
},
10+
PLACEMENT: { name: 'TopsortPlacementConfig' },
11+
ITEM: { name: 'TopsortItemConfig' },
12+
PURCHASE_ITEM: { name: 'TopSortPurchaseProductConfig' },
13+
};
14+
15+
const ECOMM_EVENTS_WITH_PRODUCT_ARRAY = [
16+
'Cart Viewed',
17+
'Checkout Started',
18+
'Order Updated',
19+
'Order Completed',
20+
'Order Refunded',
21+
'Order Cancelled',
22+
];
23+
24+
const mappingConfig = getMappingConfig(ConfigCategory, __dirname);
25+
26+
module.exports = {
27+
mappingConfig,
28+
ConfigCategory,
29+
ENDPOINT,
30+
ECOMM_EVENTS_WITH_PRODUCT_ARRAY,
31+
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[
2+
{
3+
"destKey": "productId",
4+
"sourceKeys": ["product_id", "properties.product_id"]
5+
},
6+
{
7+
"destKey": "unitPrice",
8+
"sourceKeys": ["price", "properties.price"],
9+
"metadata": {
10+
"type": "toNumber"
11+
}
12+
},
13+
{
14+
"destKey": "quantity",
15+
"sourceKeys": ["quantity", "properties.quantity"],
16+
"metadata": {
17+
"toInt": true
18+
}
19+
},
20+
{
21+
"destKey": "vendorId",
22+
"sourceKeys": "properties.vendorId"
23+
}
24+
]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[
2+
{
3+
"destKey": "position",
4+
"sourceKeys": ["properties.position", "position"],
5+
"metadata": {
6+
"toInt": true
7+
},
8+
"required": false
9+
},
10+
{
11+
"destKey": "productId",
12+
"sourceKeys": ["properties.product_id", "product_id"],
13+
"required": false
14+
}
15+
]
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[
2+
{
3+
"destKey": "path",
4+
"sourceKeys": "context.page.path",
5+
"required": false
6+
},
7+
{
8+
"destKey": "searchQuery",
9+
"sourceKeys": "properties.query",
10+
"required": false
11+
},
12+
{
13+
"destKey": "page",
14+
"sourceKeys": "properties.pageNumber",
15+
"metadata": {
16+
"toInt": true
17+
},
18+
"required": false
19+
},
20+
{
21+
"destKey": "pageSize",
22+
"sourceKeys": "properties.pageSize",
23+
"metadata": {
24+
"toInt": true
25+
},
26+
"required": false
27+
},
28+
{
29+
"destKey": "categoryIds",
30+
"sourceKeys": "properties.category_id",
31+
"required": false,
32+
"metadata": {
33+
"toArray": true
34+
}
35+
}
36+
]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[
2+
{
3+
"destKey": "occurredAt",
4+
"sourceKeys": ["timestamp", "originalTimestamp"],
5+
"required": true
6+
},
7+
{
8+
"destKey": "opaqueUserId",
9+
"sourceKeys": "anonymousId",
10+
"required": true
11+
},
12+
{
13+
"destKey": "resolvedBidId",
14+
"sourceKeys": "properties.resolvedBidId",
15+
"required": false
16+
},
17+
{
18+
"destKey": "entity",
19+
"sourceKeys": "properties.entity",
20+
"required": false
21+
},
22+
{
23+
"destKey": "additionalAttribution",
24+
"sourceKeys": "properties.additionalAttribution",
25+
"required": false
26+
}
27+
]
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
const { ConfigCategory, mappingConfig } = require('./config');
2+
const { getItemPayloads, addFinalPayload } = require('./utils');
3+
const { constructPayload, generateUUID } = require('../../util');
4+
5+
const processImpressionsAndClicksUtility = {
6+
// Create event data object
7+
createEventData(basePayload, placementPayload, itemPayload, event) {
8+
return {
9+
topsortPayload: {
10+
...basePayload,
11+
placement: {
12+
...placementPayload,
13+
...itemPayload,
14+
},
15+
id: generateUUID(),
16+
},
17+
event,
18+
};
19+
},
20+
21+
// Process events with a product array
22+
processProductArray({
23+
products,
24+
basePayload,
25+
placementPayload,
26+
topsortEventName,
27+
finalPayloads,
28+
}) {
29+
const itemPayloads = getItemPayloads(products, mappingConfig[ConfigCategory.ITEM.name]);
30+
itemPayloads.forEach((itemPayload) => {
31+
const eventData = this.createEventData(
32+
basePayload,
33+
placementPayload,
34+
itemPayload,
35+
topsortEventName,
36+
);
37+
addFinalPayload(eventData, finalPayloads);
38+
});
39+
},
40+
41+
// Process events with a single product
42+
processSingleProduct({
43+
basePayload,
44+
placementPayload,
45+
message,
46+
topsortEventName,
47+
finalPayloads,
48+
}) {
49+
const itemPayload = constructPayload(message, mappingConfig[ConfigCategory.ITEM.name]);
50+
const eventData = this.createEventData(
51+
basePayload,
52+
placementPayload,
53+
itemPayload,
54+
topsortEventName,
55+
);
56+
57+
// Ensure messageId is used instead of generating a UUID for single product events
58+
eventData.topsortPayload.id = message.messageId;
59+
60+
// Add final payload with appropriate ID and other headers
61+
addFinalPayload(eventData, finalPayloads);
62+
},
63+
64+
processImpressionsAndClicks({
65+
isProductArrayAvailable,
66+
basePayload,
67+
topsortEventName,
68+
finalPayloads,
69+
products,
70+
message,
71+
placementPayload,
72+
}) {
73+
if (isProductArrayAvailable) {
74+
// If product array is available, process the event with multiple products
75+
this.processProductArray({
76+
basePayload,
77+
topsortEventName,
78+
finalPayloads,
79+
products,
80+
placementPayload,
81+
});
82+
} else {
83+
// Otherwise, process the event with a single product
84+
this.processSingleProduct({
85+
basePayload,
86+
topsortEventName,
87+
finalPayloads,
88+
message,
89+
placementPayload,
90+
});
91+
}
92+
},
93+
};
94+
95+
module.exports = { processImpressionsAndClicksUtility };
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
const { ConfigCategory, mappingConfig } = require('./config');
2+
const { getItemPayloads, addFinalPayload } = require('./utils');
3+
const { constructPayload, generateUUID } = require('../../util');
4+
5+
const processPurchaseEventUtility = {
6+
// Create event data object for purchase events
7+
createEventData(basePayload, items, event) {
8+
return {
9+
topsortPayload: {
10+
...basePayload,
11+
items,
12+
id: generateUUID(),
13+
},
14+
event,
15+
};
16+
},
17+
18+
// Function to process events with a product array for purchase events
19+
processProductArray(args) {
20+
const { products, basePayload, topsortEventName, finalPayloads } = args;
21+
const itemPayloads = getItemPayloads(
22+
products,
23+
mappingConfig[ConfigCategory.PURCHASE_ITEM.name],
24+
);
25+
const eventData = this.createEventData(basePayload, itemPayloads, topsortEventName);
26+
addFinalPayload(eventData, finalPayloads);
27+
},
28+
29+
// Function to process events with a single product for purchase events
30+
processSingleProduct(args) {
31+
const { basePayload, message, topsortEventName, finalPayloads } = args;
32+
const itemPayload = constructPayload(message, mappingConfig[ConfigCategory.PURCHASE_ITEM.name]);
33+
const eventData = this.createEventData(basePayload, [itemPayload], topsortEventName);
34+
35+
// Ensure messageId is used instead of generating a UUID for single product events
36+
eventData.topsortPayload.id = message.messageId;
37+
38+
// Add final payload with appropriate ID and other headers
39+
addFinalPayload(eventData, finalPayloads);
40+
},
41+
42+
// Function to process purchase events (either with a product array or single product)
43+
processPurchaseEvent(args) {
44+
if (args.isProductArrayAvailable) {
45+
// Process the event with multiple products (product array)
46+
this.processProductArray(args);
47+
} else {
48+
// Process the event with a single product
49+
this.processSingleProduct(args);
50+
}
51+
},
52+
};
53+
54+
module.exports = {
55+
processPurchaseEventUtility,
56+
};

0 commit comments

Comments
 (0)