diff --git a/src/cdk/v2/destinations/smartly/procWorkflow.yaml b/src/cdk/v2/destinations/smartly/procWorkflow.yaml index 43c4783783..56f360d6c2 100644 --- a/src/cdk/v2/destinations/smartly/procWorkflow.yaml +++ b/src/cdk/v2/destinations/smartly/procWorkflow.yaml @@ -23,7 +23,8 @@ steps: - name: preparePayload template: | const payload = $.removeUndefinedAndNullValues($.constructPayload(.message, $.TRACK_CONFIG)); - $.context.payloadList = $.getPayloads(.message.event, .destination.Config, payload) + $.verifyAdInteractionTime(payload.ad_interaction_time); + $.context.payloadList = $.getPayloads(.message.event_name, .destination.Config, payload) - name: buildResponse template: | const response = $.buildResponseList($.context.payloadList) diff --git a/src/cdk/v2/destinations/smartly/utils.js b/src/cdk/v2/destinations/smartly/utils.js index b2d6fd3f87..6b4b3ce6d5 100644 --- a/src/cdk/v2/destinations/smartly/utils.js +++ b/src/cdk/v2/destinations/smartly/utils.js @@ -1,5 +1,6 @@ const { BatchUtils } = require('@rudderstack/workflow-engine'); const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const moment = require('moment'); const config = require('./config'); const { getHashFromArrayWithDuplicate, @@ -31,6 +32,38 @@ const getPayloads = (event, Config, payload) => { return payloadLists; }; +const verifyAdInteractionTime = (adInteractionTime) => { + if (isDefinedAndNotNull(adInteractionTime)) { + let adInteractionTimeMoment; + + // Handle both UNIX timestamps and UTC date strings + if (typeof adInteractionTime === 'number') { + adInteractionTimeMoment = moment.unix(adInteractionTime); // Parse as UNIX timestamp + } else { + adInteractionTimeMoment = moment.utc(adInteractionTime); // Parse as UTC date string + } + + const currentMoment = moment.utc(); // Current time in UTC + + // Calculate the time difference in days + const diffDaysPast = currentMoment.diff(adInteractionTimeMoment, 'days'); + const diffDaysFuture = adInteractionTimeMoment.diff(currentMoment, 'days'); + + // Define the day range: 3 years (1095 days) in the past and 1 year (365 days) in the future + const maxDaysPast = 3 * 365 + 1; // 1095 days + 1 day for a leap year + const maxDaysFuture = 1 * 365 + 1; // 365 days + 1 day for a leap year + + // Check if adInteractionTime is within the allowed range + const isWithinAllowedRange = diffDaysPast <= maxDaysPast && diffDaysFuture <= maxDaysFuture; + + if (!isWithinAllowedRange) { + throw new InstrumentationError( + 'ad_interaction_time must be within one year in the future and three years in the past.', + ); + } + } +}; + const buildResponseList = (payloadList) => payloadList.map((payload) => { const response = defaultRequestConfig(); @@ -84,4 +117,4 @@ const batchResponseBuilder = (events) => { return response; }; -module.exports = { batchResponseBuilder, getPayloads, buildResponseList }; +module.exports = { batchResponseBuilder, getPayloads, buildResponseList, verifyAdInteractionTime }; diff --git a/src/cdk/v2/destinations/smartly/utils.test.js b/src/cdk/v2/destinations/smartly/utils.test.js new file mode 100644 index 0000000000..a3c0071eda --- /dev/null +++ b/src/cdk/v2/destinations/smartly/utils.test.js @@ -0,0 +1,66 @@ +const moment = require('moment'); +const { InstrumentationError } = require('@rudderstack/workflow-engine'); +const { verifyAdInteractionTime } = require('./utils'); + +describe('verifyAdInteractionTime', () => { + it('should pass when adInteractionTime is 2 years in the past (UNIX timestamp)', () => { + // 2 years ago from now + const adInteractionTime = moment().subtract(2, 'years').unix(); + expect(() => verifyAdInteractionTime(adInteractionTime)).not.toThrow(); + }); + + it('should pass when adInteractionTime is 10 months in the future (UNIX timestamp)', () => { + // 10 months in the future from now + const adInteractionTime = moment().add(10, 'months').unix(); + expect(() => verifyAdInteractionTime(adInteractionTime)).not.toThrow(); + }); + + it('should fail when adInteractionTime is 4 years in the past (UNIX timestamp)', () => { + // 4 years ago from now + const adInteractionTime = moment().subtract(4, 'years').unix(); + expect(() => verifyAdInteractionTime(adInteractionTime)).toThrow( + 'ad_interaction_time must be within one year in the future and three years in the past.', + ); + }); + + it('should fail when adInteractionTime is 2 years in the future (UNIX timestamp)', () => { + // 2 years in the future from now + const adInteractionTime = moment().add(2, 'years').unix(); + expect(() => verifyAdInteractionTime(adInteractionTime)).toThrow( + 'ad_interaction_time must be within one year in the future and three years in the past.', + ); + }); + + it('should pass when adInteractionTime is exactly 3 years in the past (UTC date string)', () => { + // Exactly 3 years ago from now + const adInteractionTime = moment.utc().subtract(3, 'years').toISOString(); + expect(() => verifyAdInteractionTime(adInteractionTime)).not.toThrow(); + }); + + it('should pass when adInteractionTime is exactly 1 year in the future (UTC date string)', () => { + // Exactly 1 year in the future from now + const adInteractionTime = moment.utc().add(1, 'year').toISOString(); + expect(() => verifyAdInteractionTime(adInteractionTime)).not.toThrow(); + }); + + it('should fail when adInteractionTime is 4 years in the past (UTC date string)', () => { + // 4 years ago from now + const adInteractionTime = moment.utc().subtract(4, 'years').toISOString(); + expect(() => verifyAdInteractionTime(adInteractionTime)).toThrow( + 'ad_interaction_time must be within one year in the future and three years in the past.', + ); + }); + + it('should fail when adInteractionTime is 2 years in the future (UTC date string)', () => { + // 2 years in the future from now + const adInteractionTime = moment.utc().add(2, 'years').toISOString(); + expect(() => verifyAdInteractionTime(adInteractionTime)).toThrow( + 'ad_interaction_time must be within one year in the future and three years in the past.', + ); + }); + + it('should not throw an error if adInteractionTime is null or undefined', () => { + expect(() => verifyAdInteractionTime(null)).not.toThrow(); + expect(() => verifyAdInteractionTime(undefined)).not.toThrow(); + }); +}); diff --git a/test/integrations/destinations/smartly/processor/data.ts b/test/integrations/destinations/smartly/processor/data.ts index 4db0c79f78..a94f6b220f 100644 --- a/test/integrations/destinations/smartly/processor/data.ts +++ b/test/integrations/destinations/smartly/processor/data.ts @@ -1,4 +1,9 @@ import { trackTestData } from './track'; import { validationFailures } from './validation'; -export const data = [...trackTestData, ...validationFailures]; +export const mockFns = (_) => { + // @ts-ignore + jest.useFakeTimers().setSystemTime(new Date('2024-02-01')); +}; + +export const data = [...trackTestData, ...validationFailures].map((d) => ({ ...d, mockFns })); diff --git a/test/integrations/destinations/smartly/processor/track.ts b/test/integrations/destinations/smartly/processor/track.ts index b934e71e8f..08b3f41b0e 100644 --- a/test/integrations/destinations/smartly/processor/track.ts +++ b/test/integrations/destinations/smartly/processor/track.ts @@ -1,3 +1,5 @@ +const moment = require('moment'); +import { verifyAdInteractionTime } from '../../../../../src/cdk/v2/destinations/smartly/utils'; import { destination } from '../commonConfig'; export const trackTestData = [ @@ -13,11 +15,11 @@ export const trackTestData = [ { destination, message: { - event: 'Add to cart', + event_name: 'Add to cart', properties: { platform: 'meta', - ad_unit_id: 228287, - ad_interaction_time: '1650626278', + ad_unit_id: '228287', + ad_interaction_time: '1612137600', email: 'eventIdn01@sample.com', }, type: 'track', @@ -44,16 +46,16 @@ export const trackTestData = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://s2s.smartly.io/events', + endpoint: 'https://s2s.smartly.io/events/batch', headers: {}, params: {}, body: { JSON: { platform: 'meta', - ad_unit_id: 228287, - ad_interaction_time: '1650626278', + ad_unit_id: '228287', + ad_interaction_time: '1612137600', conversions: '1', - event: 'Add to cart', + event_name: 'Add to cart', }, JSON_ARRAY: {}, XML: {}, diff --git a/test/integrations/destinations/smartly/processor/validation.ts b/test/integrations/destinations/smartly/processor/validation.ts index c080905549..d0a657b011 100644 --- a/test/integrations/destinations/smartly/processor/validation.ts +++ b/test/integrations/destinations/smartly/processor/validation.ts @@ -17,13 +17,13 @@ export const validationFailures = [ destination, message: { type: 'track', - event: 'product purchased', + event_name: 'product purchased', sentAt: '2021-01-25T16:12:02.048Z', userId: 'john123', properties: { products: [{}], ad_unit_id: '22123387', - ad_interaction_time: '1650626278', + ad_interaction_time: '1690867200', }, integrations: { All: true, @@ -72,7 +72,7 @@ export const validationFailures = [ destination, message: { type: 'group', - event: 'purchase', + event_name: 'purchase', sentAt: '2021-01-25T16:12:02.048Z', userId: 'john67', channel: 'mobile', @@ -81,7 +81,7 @@ export const validationFailures = [ properties: { platform: 'snapchat', ad_unit_id: '2653387', - ad_interaction_time: '1650646278', + ad_interaction_time: '1690867200', }, anonymousId: 'anon_123', integrations: { @@ -138,7 +138,7 @@ export const validationFailures = [ properties: { platform: 'snapchat', ad_unit_id: '2653387', - ad_interaction_time: '1650646278', + ad_interaction_time: '1675094400', }, anonymousId: 'anon_123', integrations: { diff --git a/test/integrations/destinations/smartly/router/data.ts b/test/integrations/destinations/smartly/router/data.ts index 88e417fd00..ff500eab4c 100644 --- a/test/integrations/destinations/smartly/router/data.ts +++ b/test/integrations/destinations/smartly/router/data.ts @@ -18,12 +18,12 @@ export const data = [ { message: { type: 'track', - event: 'product list viewed', + event_name: 'product list viewed', properties: { platform: 'meta', conversions: 1, - ad_unit_id: 221187, - ad_interaction_time: '1652826278', + ad_unit_id: "221187", + ad_interaction_time: '1690867200', }, }, metadata: { jobId: 2, userId: 'u2' }, @@ -32,12 +32,12 @@ export const data = [ { message: { type: 'track', - event: 'add to cart', + event_name: 'add to cart', properties: { conversions: 3, platform: 'snapchat', - ad_unit_id: 77187, - ad_interaction_time: '2752826278', + ad_unit_id: "77187", + ad_interaction_time: '1690867200', }, }, metadata: { jobId: 3, userId: 'u3' }, @@ -61,24 +61,24 @@ export const data = [ events: [ { conversions: 1, - ad_unit_id: 221187, + ad_unit_id: "221187", platform: 'meta', - ad_interaction_time: '1652826278', - event: 'event1', + ad_interaction_time: '1690867200', + event_name: 'event1', }, { conversions: 1, - ad_unit_id: 221187, + ad_unit_id: "221187", platform: 'meta', - ad_interaction_time: '1652826278', - event: 'event2', + ad_interaction_time: '1690867200', + event_name: 'event2', }, { conversions: 3, - ad_unit_id: 77187, + ad_unit_id: "77187", platform: 'snapchat', - ad_interaction_time: '2752826278', - event: 'add to cart', + ad_interaction_time: '1690867200', + event_name: 'add to cart', }, ], }, @@ -132,12 +132,12 @@ export const data = [ { message: { type: 'track', - event: 'product list viewed', + event_name: 'product list viewed', properties: { platform: 'meta', conversions: 1, - ad_unit_id: 221187, - ad_interaction_time: '1652826278', + ad_unit_id: "221187", + ad_interaction_time: '1690867200', }, }, metadata: { jobId: 11, userId: 'u1' }, @@ -146,14 +146,14 @@ export const data = [ { message: { type: 'track', - event: 'purchase', + event_name: 'purchase', userId: 'testuserId1', integrations: { All: true }, properties: { conversions: 3, platform: 'snapchat', - ad_unit_id: 77187, - ad_interaction_time: '2752826278', + ad_unit_id: "77187", + ad_interaction_time: '1690867200', }, }, metadata: { jobId: 13, userId: 'u1' }, @@ -167,8 +167,8 @@ export const data = [ properties: { conversions: 3, platform: 'snapchat', - ad_unit_id: 12387, - ad_interaction_time: '2752826278', + ad_unit_id: "12387", + ad_interaction_time: '1690867200', }, }, metadata: { jobId: 14, userId: 'u1' }, @@ -177,13 +177,13 @@ export const data = [ { message: { type: 'track', - event: 'random event', + event_name: 'random event', userId: 'testuserId1', integrations: { All: true }, properties: { conversions: 3, - ad_unit_id: 77187, - ad_interaction_time: '2752826278', + ad_unit_id: "77187", + ad_interaction_time: '1690867200', }, }, metadata: { jobId: 15, userId: 'u1' }, @@ -192,14 +192,14 @@ export const data = [ { message: { type: 'track', - event: 'add to cart', + event_name: 'add to cart', userId: 'testuserId1', integrations: { All: true }, properties: { conversions: 3, platform: 'tiktok', - ad_unit_id: 789187, - ad_interaction_time: '2752826278', + ad_unit_id: "789187", + ad_interaction_time: '1690867200', }, }, metadata: { jobId: 16, userId: 'u1' }, @@ -246,16 +246,16 @@ export const data = [ { platform: 'meta', conversions: 1, - event: 'event1', - ad_unit_id: 221187, - ad_interaction_time: '1652826278', + event_name: 'event1', + ad_unit_id: "221187", + ad_interaction_time: '1690867200', }, { platform: 'meta', conversions: 1, - event: 'event2', - ad_unit_id: 221187, - ad_interaction_time: '1652826278', + event_name: 'event2', + ad_unit_id: "221187", + ad_interaction_time: '1690867200', }, ], }, @@ -286,17 +286,17 @@ export const data = [ events: [ { conversions: 3, - event: 'purchase', + event_name: 'purchase', platform: 'snapchat', - ad_unit_id: 77187, - ad_interaction_time: '2752826278', + ad_unit_id: "77187", + ad_interaction_time: '1690867200', }, { conversions: 3, - event: 'add to cart', + event_name: 'add to cart', platform: 'tiktok', - ad_unit_id: 789187, - ad_interaction_time: '2752826278', + ad_unit_id: "789187", + ad_interaction_time: '1690867200', }, ], },