Skip to content

Commit 741f0c6

Browse files
aanshi07Sai Sankeerth
andauthored
feat: onboard tune destination (#3795)
* feat: onboard tune destination * chore: test cases added * chore: file name updated * chore: import fix * chore: updated mappings * chore: updated transform.js * chore: updated test cases * chore: small fix * chore: router test cases added * chore: minor fix --------- Co-authored-by: Sai Sankeerth <[email protected]>
1 parent 21b1039 commit 741f0c6

File tree

4 files changed

+511
-0
lines changed

4 files changed

+511
-0
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
const get = require('get-value');
2+
const { InstrumentationError } = require('@rudderstack/integrations-lib');
3+
const {
4+
defaultRequestConfig,
5+
simpleProcessRouterDest,
6+
getHashFromArray,
7+
isDefinedAndNotNull,
8+
isNotEmpty,
9+
} = require('../../util');
10+
11+
const mapPropertiesWithNestedSupport = (msg, properties, mappings) => {
12+
const mappedObj = {}; // Create a new object for parameters
13+
Object.entries(mappings).forEach(([key, value]) => {
14+
const keyStr = `${key}`;
15+
const args = { object: properties, key: keyStr };
16+
if (args.key.split('.').length > 1) {
17+
// Handle nested keys
18+
args.object = msg; // This line modifies the object property of args
19+
}
20+
const data = get(args.object, args.key);
21+
if (isDefinedAndNotNull(data) && isNotEmpty(data)) {
22+
mappedObj[value] = data; // Map to the corresponding destination key
23+
}
24+
});
25+
return mappedObj; // Return the new params object
26+
};
27+
28+
const responseBuilder = (message, { Config }) => {
29+
const { tuneEvents } = Config; // Extract tuneEvents from config
30+
const { properties, event: messageEvent } = message; // Destructure properties and event from message
31+
32+
// Find the relevant tune event based on the message's event name
33+
const tuneEvent = tuneEvents.find((event) => event.eventName === messageEvent);
34+
35+
if (tuneEvent) {
36+
const standardHashMap = getHashFromArray(tuneEvent.standardMapping, 'from', 'to', false);
37+
const advSubIdHashMap = getHashFromArray(tuneEvent.advSubIdMapping, 'from', 'to', false);
38+
const advUniqueIdHashMap = getHashFromArray(tuneEvent.advUniqueIdMapping, 'from', 'to', false);
39+
40+
const params = {
41+
...mapPropertiesWithNestedSupport(message, properties, standardHashMap),
42+
...mapPropertiesWithNestedSupport(message, properties, advSubIdHashMap),
43+
...mapPropertiesWithNestedSupport(message, properties, advUniqueIdHashMap),
44+
};
45+
46+
// Prepare the response
47+
const response = defaultRequestConfig();
48+
response.params = params; // Set only the mapped params
49+
response.endpoint = tuneEvent.url; // Use the user-defined URL
50+
51+
return response;
52+
}
53+
54+
throw new InstrumentationError('No matching tune event found for the provided event.', 400);
55+
};
56+
57+
const processEvent = (message, destination) => {
58+
// Validate message type
59+
if (!isDefinedAndNotNull(message.type) || typeof message.type !== 'string') {
60+
throw new InstrumentationError(
61+
'Message Type is not present or is not a string. Aborting message.',
62+
400,
63+
);
64+
}
65+
const messageType = message.type.toLowerCase();
66+
67+
// Initialize response variable
68+
let response;
69+
70+
// Process 'track' messages using the responseBuilder
71+
if (messageType === 'track') {
72+
response = responseBuilder(message, destination);
73+
} else {
74+
throw new InstrumentationError('Message type not supported. Only "track" is allowed.', 400);
75+
}
76+
77+
return response;
78+
};
79+
80+
const process = (event) => processEvent(event.message, event.destination);
81+
82+
const processRouterDest = async (inputs, reqMetadata) => {
83+
const respList = await simpleProcessRouterDest(inputs, process, reqMetadata);
84+
return respList;
85+
};
86+
87+
module.exports = {
88+
process,
89+
processRouterDest,
90+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { trackTestdata } from './trackTestData';
2+
3+
export const data = [...trackTestdata];
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
import { Destination } from '../../../../../src/types';
2+
import { ProcessorTestData } from '../../../testTypes';
3+
import {
4+
generateMetadata,
5+
generateSimplifiedTrackPayload,
6+
overrideDestination,
7+
transformResultBuilder,
8+
} from '../../../testUtils';
9+
10+
const destination: Destination = {
11+
ID: '123',
12+
Name: 'tune',
13+
DestinationDefinition: {
14+
ID: '123',
15+
Name: 'tune',
16+
DisplayName: 'tune',
17+
Config: {},
18+
},
19+
Config: {
20+
connectionMode: {
21+
web: 'cloud',
22+
},
23+
consentManagement: {},
24+
oneTrustCookieCategories: {},
25+
ketchConsentPurposes: {},
26+
tuneEvents: [
27+
{
28+
url: 'https://demo.go2cloud.org/aff_l?offer_id=45&aff_id=1029',
29+
eventName: 'Product added',
30+
standardMapping: [
31+
{ to: 'aff_id', from: 'affId' },
32+
{ to: 'promo_code', from: 'promoCode' },
33+
{ to: 'security_token', from: 'securityToken' },
34+
{ to: 'status', from: 'status' },
35+
{ to: 'transaction_id', from: 'mytransactionId' },
36+
],
37+
advSubIdMapping: [{ from: 'context.traits.ip', to: 'adv_sub2' }],
38+
advUniqueIdMapping: [{ from: 'context.traits.customProperty1', to: 'adv_unique1' }],
39+
},
40+
],
41+
},
42+
Enabled: true,
43+
WorkspaceID: '123',
44+
Transformations: [],
45+
};
46+
47+
export const trackTestdata: ProcessorTestData[] = [
48+
{
49+
id: 'Test 0',
50+
name: 'tune',
51+
description: 'Track call with standard properties mapping',
52+
scenario: 'Business',
53+
successCriteria:
54+
'The response should have a status code of 200 and correctly map the properties to the specified parameters.',
55+
feature: 'processor',
56+
module: 'destination',
57+
version: 'v0',
58+
input: {
59+
request: {
60+
body: [
61+
{
62+
message: generateSimplifiedTrackPayload({
63+
type: 'track',
64+
event: 'Product added',
65+
properties: {
66+
securityToken: '1123',
67+
mytransactionId: 'test-123',
68+
},
69+
context: {
70+
traits: {
71+
customProperty1: 'customValue',
72+
firstName: 'David',
73+
logins: 2,
74+
ip: '0.0.0.0',
75+
},
76+
},
77+
anonymousId: 'david_bowie_anonId',
78+
}),
79+
metadata: generateMetadata(1),
80+
destination,
81+
},
82+
],
83+
},
84+
},
85+
output: {
86+
response: {
87+
status: 200,
88+
body: [
89+
{
90+
output: transformResultBuilder({
91+
method: 'POST',
92+
endpoint: 'https://demo.go2cloud.org/aff_l?offer_id=45&aff_id=1029',
93+
event: 'Product added',
94+
headers: {},
95+
params: {
96+
security_token: '1123',
97+
transaction_id: 'test-123',
98+
adv_sub2: '0.0.0.0',
99+
adv_unique1: 'customValue',
100+
},
101+
userId: '',
102+
JSON: {},
103+
}),
104+
metadata: generateMetadata(1),
105+
statusCode: 200,
106+
},
107+
],
108+
},
109+
},
110+
},
111+
{
112+
id: 'Test 1',
113+
name: 'tune',
114+
description: 'Test case for handling a missing tune event for a given event name',
115+
scenario: 'Business',
116+
successCriteria:
117+
'The response should return a 400 status code with an appropriate error message indicating no matching tune event was found.',
118+
feature: 'processor',
119+
module: 'destination',
120+
version: 'v0',
121+
input: {
122+
request: {
123+
body: [
124+
{
125+
message: generateSimplifiedTrackPayload({
126+
type: 'track',
127+
event: 'Purchase event',
128+
properties: {
129+
securityToken: '1123',
130+
mytransactionId: 'test-123',
131+
},
132+
context: {
133+
traits: {
134+
customProperty1: 'customValue',
135+
firstName: 'David',
136+
logins: 2,
137+
},
138+
},
139+
anonymousId: 'david_bowie_anonId',
140+
}),
141+
metadata: generateMetadata(1),
142+
destination,
143+
},
144+
],
145+
},
146+
},
147+
output: {
148+
response: {
149+
status: 200,
150+
body: [
151+
{
152+
error: 'No matching tune event found for the provided event.',
153+
statTags: {
154+
destType: 'TUNE',
155+
destinationId: 'default-destinationId',
156+
errorCategory: 'dataValidation',
157+
errorType: 'instrumentation',
158+
feature: 'processor',
159+
implementation: 'native',
160+
module: 'destination',
161+
workspaceId: 'default-workspaceId',
162+
},
163+
metadata: generateMetadata(1),
164+
statusCode: 400,
165+
},
166+
],
167+
},
168+
},
169+
},
170+
{
171+
id: 'Test 2',
172+
name: 'tune',
173+
description: 'Incorrect message type',
174+
scenario: 'Business',
175+
successCriteria: 'The response should return a 400 status code due to invalid message type.',
176+
feature: 'processor',
177+
module: 'destination',
178+
version: 'v0',
179+
input: {
180+
request: {
181+
body: [
182+
{
183+
message: {
184+
type: 'abc',
185+
event: 'Product added',
186+
properties: {
187+
securityToken: '1123',
188+
mytransactionId: 'test-123',
189+
},
190+
context: {
191+
traits: {
192+
customProperty1: 'customValue',
193+
firstName: 'David',
194+
logins: 2,
195+
},
196+
},
197+
anonymousId: 'david_bowie_anonId',
198+
},
199+
metadata: generateMetadata(1),
200+
destination,
201+
},
202+
],
203+
},
204+
},
205+
output: {
206+
response: {
207+
status: 200,
208+
body: [
209+
{
210+
error: 'Message type not supported. Only "track" is allowed.',
211+
statTags: {
212+
destType: 'TUNE',
213+
destinationId: 'default-destinationId',
214+
errorCategory: 'dataValidation',
215+
errorType: 'instrumentation',
216+
feature: 'processor',
217+
implementation: 'native',
218+
module: 'destination',
219+
workspaceId: 'default-workspaceId',
220+
},
221+
metadata: generateMetadata(1),
222+
statusCode: 400,
223+
},
224+
],
225+
},
226+
},
227+
},
228+
];

0 commit comments

Comments
 (0)