diff --git a/package-lock.json b/package-lock.json index a219fc5199..1c9e84bf11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "@koa/router": "^12.0.0", "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", - "@rudderstack/integrations-lib": "^0.2.10", + "@rudderstack/integrations-lib": "^0.2.12", "@rudderstack/json-template-engine": "^0.18.0", "@rudderstack/workflow-engine": "^0.8.13", "@shopify/jest-koa-mocks": "^5.1.1", @@ -6602,8 +6602,9 @@ } }, "node_modules/@rudderstack/integrations-lib": { - "version": "0.2.10", - "license": "MIT", + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@rudderstack/integrations-lib/-/integrations-lib-0.2.12.tgz", + "integrity": "sha512-xy+T9SHFkSeVDd4svGOyrTtIGljZ/l4qUh5o5EQWk3dTStzaV9mKnbXLsG62kEO3aTmCVg+VYr4OPwZY2+6rxQ==", "dependencies": { "axios": "^1.4.0", "axios-mock-adapter": "^1.22.0", diff --git a/package.json b/package.json index 20c54ccbbb..4500384383 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@koa/router": "^12.0.0", "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", - "@rudderstack/integrations-lib": "^0.2.10", + "@rudderstack/integrations-lib": "^0.2.12", "@rudderstack/json-template-engine": "^0.18.0", "@rudderstack/workflow-engine": "^0.8.13", "@shopify/jest-koa-mocks": "^5.1.1", diff --git a/src/controllers/misc.ts b/src/controllers/misc.ts index e2efdab5db..7f8ec52825 100644 --- a/src/controllers/misc.ts +++ b/src/controllers/misc.ts @@ -21,7 +21,7 @@ export class MiscController { } public static features(ctx: Context) { - ctx.body = MiscService.getFetaures(); + ctx.body = MiscService.getFeatures(); ctx.status = 200; return ctx; } diff --git a/src/features.json b/src/features.json deleted file mode 100644 index 63862eefed..0000000000 --- a/src/features.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "routerTransform": { - "ACTIVE_CAMPAIGN": true, - "ALGOLIA": true, - "CANDU": true, - "DELIGHTED": true, - "DRIP": true, - "FB_CUSTOM_AUDIENCE": true, - "GA": true, - "GAINSIGHT": true, - "GAINSIGHT_PX": true, - "GOOGLESHEETS": true, - "GOOGLE_ADWORDS_ENHANCED_CONVERSIONS": true, - "GOOGLE_ADWORDS_REMARKETING_LISTS": true, - "GOOGLE_ADWORDS_OFFLINE_CONVERSIONS": true, - "HS": true, - "ITERABLE": true, - "KLAVIYO": true, - "KUSTOMER": true, - "MAILCHIMP": true, - "MAILMODO": true, - "MARKETO": true, - "OMETRIA": true, - "PARDOT": true, - "PINTEREST_TAG": true, - "PROFITWELL": true, - "SALESFORCE": true, - "SALESFORCE_OAUTH": true, - "SALESFORCE_OAUTH_SANDBOX": true, - "SFMC": true, - "SNAPCHAT_CONVERSION": true, - "TIKTOK_ADS": true, - "TRENGO": true, - "YAHOO_DSP": true, - "CANNY": true, - "LAMBDA": true, - "WOOTRIC": true, - "GOOGLE_CLOUD_FUNCTION": true, - "BQSTREAM": true, - "CLICKUP": true, - "FRESHMARKETER": true, - "FRESHSALES": true, - "MONDAY": true, - "CUSTIFY": true, - "USER": true, - "REFINER": true, - "FACEBOOK_OFFLINE_CONVERSIONS": true, - "MAILJET": true, - "SNAPCHAT_CUSTOM_AUDIENCE": true, - "MARKETO_STATIC_LIST": true, - "CAMPAIGN_MANAGER": true, - "SENDGRID": true, - "SENDINBLUE": true, - "ZENDESK": true, - "MP": true, - "TIKTOK_ADS_OFFLINE_EVENTS": true, - "CRITEO_AUDIENCE": true, - "CUSTOMERIO": true, - "BRAZE": true, - "OPTIMIZELY_FULLSTACK": true, - "TWITTER_ADS": true, - "CLEVERTAP": true, - "ORTTO": true, - "GLADLY": true, - "ONE_SIGNAL": true, - "TIKTOK_AUDIENCE": true, - "REDDIT": true, - "THE_TRADE_DESK": true, - "INTERCOM": true, - "NINETAILED": true, - "KOALA": true, - "LINKEDIN_ADS": true, - "BLOOMREACH": true, - "MOVABLE_INK": true, - "EMARSYS": true, - "KODDI": true, - "WUNDERKIND": true, - "CLICKSEND": true, - "ZOHO": true, - "CORDIAL": true, - "X_AUDIENCE": true, - "BLOOMREACH_CATALOG": true, - "SMARTLY": true, - "HTTP": true, - "AMAZON_AUDIENCE": true, - "INTERCOM_V2": true - }, - "regulations": [ - "BRAZE", - "AM", - "INTERCOM", - "CLEVERTAP", - "AF", - "MP", - "GA", - "ITERABLE", - "ENGAGE", - "CUSTIFY", - "SENDGRID", - "SPRIG", - "EMARSYS" - ], - "supportSourceTransformV1": true, - "supportTransformerProxyV1": true -} diff --git a/src/features.ts b/src/features.ts new file mode 100644 index 0000000000..9f60d44483 --- /dev/null +++ b/src/features.ts @@ -0,0 +1,116 @@ +interface FeaturesConfig { + routerTransform: Record; + regulations: string[]; + supportSourceTransformV1: boolean; + supportTransformerProxyV1: boolean; + upgradedToSourceTransformV2?: boolean; +} + +const defaultFeaturesConfig: FeaturesConfig = { + routerTransform: { + ACTIVE_CAMPAIGN: true, + ALGOLIA: true, + CANDU: true, + DELIGHTED: true, + DRIP: true, + FB_CUSTOM_AUDIENCE: true, + GA: true, + GAINSIGHT: true, + GAINSIGHT_PX: true, + GOOGLESHEETS: true, + GOOGLE_ADWORDS_ENHANCED_CONVERSIONS: true, + GOOGLE_ADWORDS_REMARKETING_LISTS: true, + GOOGLE_ADWORDS_OFFLINE_CONVERSIONS: true, + HS: true, + ITERABLE: true, + KLAVIYO: true, + KUSTOMER: true, + MAILCHIMP: true, + MAILMODO: true, + MARKETO: true, + OMETRIA: true, + PARDOT: true, + PINTEREST_TAG: true, + PROFITWELL: true, + SALESFORCE: true, + SALESFORCE_OAUTH: true, + SALESFORCE_OAUTH_SANDBOX: true, + SFMC: true, + SNAPCHAT_CONVERSION: true, + TIKTOK_ADS: true, + TRENGO: true, + YAHOO_DSP: true, + CANNY: true, + LAMBDA: true, + WOOTRIC: true, + GOOGLE_CLOUD_FUNCTION: true, + BQSTREAM: true, + CLICKUP: true, + FRESHMARKETER: true, + FRESHSALES: true, + MONDAY: true, + CUSTIFY: true, + USER: true, + REFINER: true, + FACEBOOK_OFFLINE_CONVERSIONS: true, + MAILJET: true, + SNAPCHAT_CUSTOM_AUDIENCE: true, + MARKETO_STATIC_LIST: true, + CAMPAIGN_MANAGER: true, + SENDGRID: true, + SENDINBLUE: true, + ZENDESK: true, + MP: true, + TIKTOK_ADS_OFFLINE_EVENTS: true, + CRITEO_AUDIENCE: true, + CUSTOMERIO: true, + BRAZE: true, + OPTIMIZELY_FULLSTACK: true, + TWITTER_ADS: true, + CLEVERTAP: true, + ORTTO: true, + GLADLY: true, + ONE_SIGNAL: true, + TIKTOK_AUDIENCE: true, + REDDIT: true, + THE_TRADE_DESK: true, + INTERCOM: true, + NINETAILED: true, + KOALA: true, + LINKEDIN_ADS: true, + BLOOMREACH: true, + MOVABLE_INK: true, + EMARSYS: true, + KODDI: true, + WUNDERKIND: true, + CLICKSEND: true, + ZOHO: true, + CORDIAL: true, + X_AUDIENCE: true, + BLOOMREACH_CATALOG: true, + SMARTLY: true, + HTTP: true, + AMAZON_AUDIENCE: true, + INTERCOM_V2: true, + }, + regulations: [ + 'BRAZE', + 'AM', + 'INTERCOM', + 'CLEVERTAP', + 'AF', + 'MP', + 'GA', + 'ITERABLE', + 'ENGAGE', + 'CUSTIFY', + 'SENDGRID', + 'SPRIG', + 'EMARSYS', + ], + supportSourceTransformV1: true, + supportTransformerProxyV1: true, + upgradedToSourceTransformV2: process.env.UPGRADED_TO_SOURCE_TRANSFORM_V2 === 'true' || false, // redundant but required to show that the default is false +}; + +export default defaultFeaturesConfig; diff --git a/src/services/__tests__/misc.test.ts b/src/services/__tests__/misc.test.ts index 5dcd948b34..f4befd4a04 100644 --- a/src/services/__tests__/misc.test.ts +++ b/src/services/__tests__/misc.test.ts @@ -1,4 +1,5 @@ import { DestHandlerMap } from '../../constants/destinationCanonicalNames'; +import defaultFeaturesConfig from '../../features'; import { MiscService } from '../misc'; describe('Misc tests', () => { @@ -24,3 +25,42 @@ describe('Misc tests', () => { ); }); }); + +describe('Misc | getFeatures', () => { + const originalEnv = process.env; + + beforeEach(() => { + // Reset environment variables and module cache before each test + process.env = { ...originalEnv }; + jest.resetModules(); + }); + + afterAll(() => { + // Restore the original environment variables after all tests + process.env = originalEnv; + }); + + function getMiscService() { + // Re-import config and featuresService after environment variables are set + const { MiscService: miscService } = require('../misc'); + return miscService; + } + + it('should return the default configuration as a JSON string', () => { + const miscService = getMiscService(); + const expectedConfig = JSON.stringify(defaultFeaturesConfig); + const result = miscService.getFeatures(); + expect(result).toBe(expectedConfig); + }); + + it('should return configuration with upgradedToSourceTransformV2 overridden by environment variable', () => { + process.env.UPGRADED_TO_SOURCE_TRANSFORM_V2 = 'true'; + const expectedConfig = { + ...defaultFeaturesConfig, + upgradedToSourceTransformV2: true, + }; + const miscService = getMiscService(); + const result = miscService.getFeatures(); + expect(result).toBe(JSON.stringify(expectedConfig)); + }); +}); diff --git a/src/services/misc.ts b/src/services/misc.ts index 09051edeec..334b54ba17 100644 --- a/src/services/misc.ts +++ b/src/services/misc.ts @@ -1,10 +1,9 @@ /* eslint-disable global-require, import/no-dynamic-require */ -import fs from 'fs'; import { Context } from 'koa'; -import path from 'path'; import { DestHandlerMap } from '../constants/destinationCanonicalNames'; import { getCPUProfile, getHeapProfile } from '../middleware'; import { Metadata } from '../types'; +import defaultFeaturesConfig from '../features'; export class MiscService { public static getDestHandler(dest: string, version: string) { @@ -62,9 +61,8 @@ export class MiscService { return process.env.npm_package_version || 'Version Info not found'; } - public static getFetaures() { - const obj = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../features.json'), 'utf8')); - return JSON.stringify(obj); + public static getFeatures() { + return JSON.stringify(defaultFeaturesConfig); } public static async getCPUProfile(seconds: number) { diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/transform.js b/src/v0/destinations/google_adwords_enhanced_conversions/transform.js index 007f16d7f8..13a294ea95 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/transform.js +++ b/src/v0/destinations/google_adwords_enhanced_conversions/transform.js @@ -2,7 +2,11 @@ const get = require('get-value'); const { cloneDeep, isNumber } = require('lodash'); -const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); +const { + InstrumentationError, + ConfigurationError, + isDefinedAndNotNull, +} = require('@rudderstack/integrations-lib'); const isString = require('lodash/isString'); const { constructPayload, @@ -11,6 +15,7 @@ const { removeHyphens, simpleProcessRouterDest, getAccessToken, + isDefined, } = require('../../util'); const { trackMapping, BASE_ENDPOINT } = require('./config'); @@ -38,6 +43,16 @@ const responseBuilder = async (metadata, message, { Config }, payload) => { const { event } = message; const { subAccount } = Config; let { customerId, loginCustomerId } = Config; + const { configData } = Config; + + if (isDefinedAndNotNull(configData)) { + const configDetails = JSON.parse(configData); + customerId = configDetails.customerId; + if (isDefined(configDetails.loginCustomerId)) { + loginCustomerId = configDetails.loginCustomerId; + } + } + if (isNumber(customerId)) { customerId = customerId.toString(); } @@ -63,6 +78,11 @@ const responseBuilder = async (metadata, message, { Config }, payload) => { response.headers['login-customer-id'] = filteredLoginCustomerId; } else throw new ConfigurationError(`LoginCustomerId is required as subAccount is true.`); + if (loginCustomerId) { + const filteredLoginCustomerId = removeHyphens(loginCustomerId); + response.headers['login-customer-id'] = filteredLoginCustomerId; + } + return response; }; @@ -71,8 +91,14 @@ const processTrackEvent = async (metadata, message, destination) => { const { Config } = destination; const { event } = message; const { listOfConversions } = Config; - if (listOfConversions.some((i) => i.conversions === event)) { - flag = 1; + if (listOfConversions && listOfConversions.length > 0) { + if (typeof listOfConversions[0] === 'string') { + if (listOfConversions.includes(event)) { + flag = 1; + } + } else if (listOfConversions.some((i) => i.conversions === event)) { + flag = 1; + } } if (event === undefined || event === '' || flag === 0) { throw new ConfigurationError( diff --git a/src/v0/destinations/google_adwords_remarketing_lists/recordTransform.js b/src/v0/destinations/google_adwords_remarketing_lists/recordTransform.js index b05ddb07a2..f8a2b0e586 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/recordTransform.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/recordTransform.js @@ -7,9 +7,11 @@ const { constructPayload, returnArrayOfSubarrays, getSuccessRespEvents, + isEventSentByVDMV1Flow, + isEventSentByVDMV2Flow, } = require('../../util'); const { populateConsentFromConfig } = require('../../util/googleUtils'); -const { populateIdentifiers, responseBuilder } = require('./util'); +const { populateIdentifiers, responseBuilder, getOperationAudienceId } = require('./util'); const { getErrorResponse, createFinalResponse } = require('../../util/recordUtils'); const { offlineDataJobsMapping, consentConfigMap } = require('./config'); @@ -19,6 +21,9 @@ const processRecordEventArray = ( destination, accessToken, developerToken, + audienceId, + typeOfList, + isHashRequired, operationType, ) => { let outputPayloads = {}; @@ -31,7 +36,12 @@ const processRecordEventArray = ( metadata.push(record.metadata); }); - const userIdentifiersList = populateIdentifiers(fieldsArray, destination); + const userIdentifiersList = populateIdentifiers( + fieldsArray, + destination, + typeOfList, + isHashRequired, + ); const outputPayload = constructPayload(message, offlineDataJobsMapping); outputPayload.operations = []; @@ -68,7 +78,7 @@ const processRecordEventArray = ( Object.values(outputPayloads).forEach((data) => { const consentObj = populateConsentFromConfig(destination.Config, consentConfigMap); toSendEvents.push( - responseBuilder(accessToken, developerToken, data, destination, message, consentObj), + responseBuilder(accessToken, developerToken, data, destination, audienceId, consentObj), ); }); @@ -77,12 +87,13 @@ const processRecordEventArray = ( return successResponse; }; -async function processRecordInputs(groupedRecordInputs) { - const { destination, message, metadata } = groupedRecordInputs[0]; +function preparepayload(events, config) { + const { destination, message, metadata } = events[0]; const accessToken = getAccessToken(metadata, 'access_token'); const developerToken = getValueFromMessage(metadata, 'secret.developer_token'); + const { audienceId, typeOfList, isHashRequired } = config; - const groupedRecordsByAction = lodash.groupBy(groupedRecordInputs, (record) => + const groupedRecordsByAction = lodash.groupBy(events, (record) => record.message.action?.toLowerCase(), ); @@ -97,6 +108,9 @@ async function processRecordInputs(groupedRecordInputs) { destination, accessToken, developerToken, + audienceId, + typeOfList, + isHashRequired, 'remove', ); } @@ -108,6 +122,9 @@ async function processRecordInputs(groupedRecordInputs) { destination, accessToken, developerToken, + audienceId, + typeOfList, + isHashRequired, 'add', ); } @@ -119,6 +136,9 @@ async function processRecordInputs(groupedRecordInputs) { destination, accessToken, developerToken, + audienceId, + typeOfList, + isHashRequired, 'add', ); } @@ -139,6 +159,45 @@ async function processRecordInputs(groupedRecordInputs) { return finalResponse; } +function processRecordInputsV0(groupedRecordInputs) { + const { destination, message } = groupedRecordInputs[0]; + const { audienceId, typeOfList, isHashRequired } = destination.Config; + + return preparepayload(groupedRecordInputs, { + audienceId: getOperationAudienceId(audienceId, message), + typeOfList, + isHashRequired, + }); +} + +function processRecordInputsV1(groupedRecordInputs) { + const { connection } = groupedRecordInputs[0]; + const { audienceId, typeOfList, isHashRequired } = connection.config.destination; + + const events = groupedRecordInputs.map((record) => ({ + ...record, + message: { + ...record.message, + fields: record.message.identifiers, + }, + })); + + return preparepayload(events, { + audienceId, + typeOfList, + isHashRequired, + }); +} + +function processRecordInputs(groupedRecordInputs) { + const event = groupedRecordInputs[0]; + // First check for rETL flow and second check for ES flow + if (isEventSentByVDMV1Flow(event) || !isEventSentByVDMV2Flow(event)) { + return processRecordInputsV0(groupedRecordInputs); + } + return processRecordInputsV1(groupedRecordInputs); +} + module.exports = { processRecordInputs, }; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/transform.js b/src/v0/destinations/google_adwords_remarketing_lists/transform.js index 3deb9be775..299ab94846 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/transform.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/transform.js @@ -12,7 +12,7 @@ const { const { populateConsentFromConfig } = require('../../util/googleUtils'); const { offlineDataJobsMapping, consentConfigMap } = require('./config'); const { processRecordInputs } = require('./recordTransform'); -const { populateIdentifiers, responseBuilder } = require('./util'); +const { populateIdentifiers, responseBuilder, getOperationAudienceId } = require('./util'); function extraKeysPresent(dictionary, keyList) { // eslint-disable-next-line no-restricted-syntax @@ -37,12 +37,18 @@ function extraKeysPresent(dictionary, keyList) { const createPayload = (message, destination) => { const { listData } = message.properties; const properties = ['add', 'remove']; + const { typeOfList, isHashRequired } = destination.Config; let outputPayloads = {}; const typeOfOperation = Object.keys(listData); typeOfOperation.forEach((key) => { if (properties.includes(key)) { - const userIdentifiersList = populateIdentifiers(listData[key], destination); + const userIdentifiersList = populateIdentifiers( + listData[key], + destination, + typeOfList, + isHashRequired, + ); if (userIdentifiersList.length === 0) { logger.info( `Google_adwords_remarketing_list]:: No attributes are present in the '${key}' property.`, @@ -113,8 +119,16 @@ const processEvent = async (metadata, message, destination) => { Object.values(createdPayload).forEach((data) => { const consentObj = populateConsentFromConfig(destination.Config, consentConfigMap); + const { audienceId } = destination.Config; response.push( - responseBuilder(accessToken, developerToken, data, destination, message, consentObj), + responseBuilder( + accessToken, + developerToken, + data, + destination, + getOperationAudienceId(audienceId, message), + consentObj, + ), ); }); return response; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/util.js b/src/v0/destinations/google_adwords_remarketing_lists/util.js index 3e04dd8f6f..f4c33a9a6f 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/util.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/util.js @@ -29,26 +29,24 @@ const hashEncrypt = (object) => { }); }; -const responseBuilder = (accessToken, developerToken, body, { Config }, message, consentBlock) => { +const responseBuilder = ( + accessToken, + developerToken, + body, + { Config }, + audienceId, + consentBlock, +) => { const payload = body; const response = defaultRequestConfig(); const filteredCustomerId = removeHyphens(Config.customerId); response.endpoint = `${BASE_ENDPOINT}/${filteredCustomerId}/offlineUserDataJobs`; response.body.JSON = removeUndefinedAndNullValues(payload); - let operationAudienceId = Config.audienceId || Config.listId; - const mappedToDestination = get(message, MappedToDestinationKey); - if (!operationAudienceId && mappedToDestination) { - const { objectType } = getDestinationExternalIDInfoForRetl( - message, - 'GOOGLE_ADWORDS_REMARKETING_LISTS', - ); - operationAudienceId = objectType; - } - if (!isDefinedAndNotNullAndNotEmpty(operationAudienceId)) { + if (!isDefinedAndNotNullAndNotEmpty(audienceId)) { throw new ConfigurationError('List ID is a mandatory field'); } response.params = { - listId: operationAudienceId, + listId: audienceId, customerId: filteredCustomerId, consent: consentBlock, }; @@ -69,14 +67,15 @@ const responseBuilder = (accessToken, developerToken, body, { Config }, message, * This function helps creates an array with proper mapping for userIdentiFier. * Logics: Here we are creating an array with all the attributes provided in the add/remove array * inside listData. - * @param {rudder event message properties listData add} attributeArray - * @param {rudder event destination} Config + * @param {Array} attributeArray rudder event message properties listData add + * @param {object} Config rudder event destination + * @param {string} typeOfList + * @param {boolean} isHashRequired * @returns */ -const populateIdentifiers = (attributeArray, { Config }) => { +const populateIdentifiers = (attributeArray, { Config }, typeOfList, isHashRequired) => { const userIdentifier = []; - const { typeOfList } = Config; - const { isHashRequired, userSchema } = Config; + const { userSchema } = Config; let attribute; if (TYPEOFLIST[typeOfList]) { attribute = TYPEOFLIST[typeOfList]; @@ -116,7 +115,21 @@ const populateIdentifiers = (attributeArray, { Config }) => { return userIdentifier; }; +const getOperationAudienceId = (audienceId, message) => { + let operationAudienceId = audienceId; + const mappedToDestination = get(message, MappedToDestinationKey); + if (!operationAudienceId && mappedToDestination) { + const { objectType } = getDestinationExternalIDInfoForRetl( + message, + 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ); + operationAudienceId = objectType; + } + return operationAudienceId; +}; + module.exports = { populateIdentifiers, responseBuilder, + getOperationAudienceId, }; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/util.test.js b/src/v0/destinations/google_adwords_remarketing_lists/util.test.js index a5897776c0..0b74b07b8e 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/util.test.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/util.test.js @@ -1,4 +1,4 @@ -const { populateIdentifiers, responseBuilder } = require('./util'); +const { populateIdentifiers, responseBuilder, getOperationAudienceId } = require('./util'); const { API_VERSION } = require('./config'); const accessToken = 'abcd1234'; const developerToken = 'ijkl9101'; @@ -29,7 +29,7 @@ const body = { const baseDestination = { Config: { rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', + audienceId: '7090784486', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -150,7 +150,7 @@ describe('GARL utils test', () => { developerToken, body, baseDestination, - message, + getOperationAudienceId(baseDestination.Config.audienceId, message), consentBlock, ); expect(response).toEqual(expectedResponse); @@ -166,7 +166,7 @@ describe('GARL utils test', () => { developerToken, body, destination2, - message, + getOperationAudienceId(baseDestination.Config.audienceId, message), consentBlock, ); expect(response).toEqual(); @@ -178,13 +178,13 @@ describe('GARL utils test', () => { it('Should throw error if operationAudienceId is not defined', () => { try { const destination1 = Object.create(baseDestination); - destination1.Config.listId = ''; + destination1.Config.audienceId = ''; const response = responseBuilder( accessToken, developerToken, body, destination1, - message, + getOperationAudienceId(baseDestination.Config.audienceId, message), consentBlock, ); expect(response).toEqual(); @@ -196,7 +196,13 @@ describe('GARL utils test', () => { describe('populateIdentifiers function tests', () => { it('Should hash and return identifiers for a given list of attributes', () => { - const identifier = populateIdentifiers(attributeArray, baseDestination); + const { typeOfList, isHashRequired } = baseDestination.Config; + const identifier = populateIdentifiers( + attributeArray, + baseDestination, + typeOfList, + isHashRequired, + ); expect(identifier).toEqual(hashedArray); }); }); diff --git a/src/v0/destinations/twitter_ads/data/TwitterAdsTrackConfig.json b/src/v0/destinations/twitter_ads/data/TwitterAdsTrackConfig.json index 9394e25c0a..1eaf40db49 100644 --- a/src/v0/destinations/twitter_ads/data/TwitterAdsTrackConfig.json +++ b/src/v0/destinations/twitter_ads/data/TwitterAdsTrackConfig.json @@ -44,5 +44,13 @@ "destKey": "contents", "sourceKeys": "properties.contents", "required": false + }, + { + "destKey": "ip_address", + "sourceKeys": ["context.ip", "request_ip"] + }, + { + "destKey": "user_agent", + "sourceKeys": "context.userAgent" } ] diff --git a/src/v0/destinations/twitter_ads/transform.js b/src/v0/destinations/twitter_ads/transform.js index 268dca3636..71536be2d9 100644 --- a/src/v0/destinations/twitter_ads/transform.js +++ b/src/v0/destinations/twitter_ads/transform.js @@ -131,6 +131,20 @@ function processTrack(message, metadata, destination) { identifiers.push({ twclid: message.properties.twclid }); } + if (message.properties.ip_address) { + const ipAddress = message.properties.ip_address.trim(); + if (ipAddress) { + identifiers.push({ ip_address: ipAddress }); + } + } + + if (message.properties.user_agent) { + const userAgent = message.properties.user_agent.trim(); + if (userAgent) { + identifiers.push({ user_agent: userAgent }); + } + } + requestJson = populateContents(requestJson); requestJson.identifiers = identifiers; @@ -149,9 +163,15 @@ function validateRequest(message) { ); } - if (!properties.email && !properties.phone && !properties.twclid) { + if ( + !properties.email && + !properties.phone && + !properties.twclid && + !properties.ip_address && + !properties.user_agent + ) { throw new InstrumentationError( - '[TWITTER ADS]: one of twclid, phone or email must be present in properties.', + '[TWITTER ADS]: one of twclid, phone, email, ip_address or user_agent must be present in properties.', ); } } diff --git a/src/v0/util/index.js b/src/v0/util/index.js index f034ab802b..1676498fdb 100644 --- a/src/v0/util/index.js +++ b/src/v0/util/index.js @@ -10,7 +10,7 @@ const Handlebars = require('handlebars'); const fs = require('fs'); const path = require('path'); const lodash = require('lodash'); -const set = require('set-value'); +const { setValue: set } = require('@rudderstack/integrations-lib'); const get = require('get-value'); const uaParser = require('ua-parser-js'); const moment = require('moment-timezone'); diff --git a/test/apitests/service.api.test.ts b/test/apitests/service.api.test.ts index 2a0db6978e..9c1d96e7fe 100644 --- a/test/apitests/service.api.test.ts +++ b/test/apitests/service.api.test.ts @@ -8,6 +8,7 @@ import request from 'supertest'; import networkHandlerFactory from '../../src/adapters/networkHandlerFactory'; import { FetchHandler } from '../../src/helpers/fetchHandlers'; import { applicationRoutes } from '../../src/routes'; +import defaultFeaturesConfig from '../../src/features'; let server: any; const OLD_ENV = process.env; @@ -43,12 +44,9 @@ const getDataFromPath = (pathInput) => { describe('features tests', () => { test('successful features response', async () => { - const expectedData = JSON.parse( - fs.readFileSync(path.resolve(__dirname, '../../src/features.json'), 'utf8'), - ); const response = await request(server).get('/features'); expect(response.status).toEqual(200); - expect(JSON.parse(response.text)).toEqual(expectedData); + expect(JSON.parse(response.text)).toEqual(defaultFeaturesConfig); }); test('features regulations should be array', async () => { diff --git a/test/integrations/destinations/ga4/processor/pageTestData.ts b/test/integrations/destinations/ga4/processor/pageTestData.ts index fa0b187aea..672f7e8f63 100644 --- a/test/integrations/destinations/ga4/processor/pageTestData.ts +++ b/test/integrations/destinations/ga4/processor/pageTestData.ts @@ -301,4 +301,216 @@ export const pageTestData: ProcessorTestData[] = [ }, mockFns: defaultMockFns, }, + { + id: 'ga4-page-test-4', + name: 'ga4', + description: + 'Scenario to test setting of reserved properties like constructor, __proto__, prototype in page call', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and event payload should not fail due to reserved properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + apiSecret: 'api_secr', + debugMode: false, + typesOfClient: 'gtag', + measurementId: 'meas_id', + firebaseAppId: '', + whitelistedEvents: [ + { + eventName: '', + }, + ], + blacklistedEvents: [ + { + eventName: '', + }, + ], + eventFilteringOption: 'disable', + piiPropertiesToIgnore: [ + { + piiProperty: '', + }, + ], + sdkBaseUrl: 'https://www.googletagmanager.com', + serverContainerUrl: '', + debugView: true, + useNativeSDK: false, + connectionMode: 'cloud', + capturePageView: 'rs', + useNativeSDKToSend: false, + extendPageViewParams: false, + overrideClientAndSessionId: false, + eventDelivery: false, + }, + ID: '2ncdvkljndsvkuiurf', + WorkspaceID: 'wspId', + DestinationDefinition: { + ...destination.DestinationDefinition, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'my ga4', + Enabled: true, + }, + message: { + name: '', + type: 'page', + sentAt: '2022-04-29T05:17:09Z', + userId: '', + channel: 'web', + context: { + os: { + name: '', + version: '', + }, + app: { + name: 'RudderLabs JavaScript SDK', + version: '3.7.6', + namespace: 'com.rudderlabs.javascript', + installType: 'npm', + }, + page: { + url: 'https://somewebsite.com/?constructor.prototype.tenable_propexxx=tenable_something', + path: '/', + title: 'Mercedes-Benz Tire Center', + search: '?constructor.prototype.tenable_propexxx=tenable_something', + tab_url: + 'https://somewebsite.com/?constructor.prototype.tenable_propexxx=tenable_something', + referrer: '$direct', + initial_referrer: '$direct', + referring_domain: '', + initial_referring_domain: '', + }, + locale: 'en-US', + screen: { + width: 800, + height: 600, + density: 1, + innerWidth: 1600, + innerHeight: 1200, + }, + traits: {}, + library: { + name: 'RudderLabs JavaScript SDK', + version: '3.7.6', + }, + campaign: {}, + timezone: 'GMT+0000', + sessionId: 123465, + userAgent: + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.207 Safari/537.36', + }, + rudderId: '7d02bb53-ff1a-46a2-9cb1-1ea78dcd4ca8', + timestamp: '2022-04-29T05:17:09Z', + properties: { + url: 'https://somewebsite.com/?constructor.prototype.tenable_propexxx=tenable_something', + path: '/', + title: 'Mercedes-Benz Tire Center', + search: '?constructor.prototype.tenable_propexxx=tenable_something', + tab_url: + 'https://somewebsite.com/?constructor.prototype.tenable_propexxx=tenable_something', + vehicle: { + make: '', + trim: '', + year: '', + model: '', + ratio: '', + width: '', + option: '', + diameter: '', + }, + national: true, + referrer: '$direct', + search_type: 'Vehicle', + initial_referrer: '$direct', + oem_program_code: 'CODE', + referring_domain: '', + initial_referring_domain: '', + 'constructor.prototype.tenable_propexxx': 'tenable_something', + }, + receivedAt: '2022-04-29T05:17:09Z', + request_ip: '34.201.223.160', + anonymousId: 'f577a7e1-6c76-49c3-8312-12846471e025', + integrations: { + All: true, + }, + originalTimestamp: '2022-04-29T05:17:09Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + XML: {}, + FORM: {}, + JSON: { + events: [ + { + name: 'page_view', + params: { + url: 'https://somewebsite.com/?constructor.prototype.tenable_propexxx=tenable_something', + path: '/', + title: 'Mercedes-Benz Tire Center', + search: '?constructor.prototype.tenable_propexxx=tenable_something', + tab_url: + 'https://somewebsite.com/?constructor.prototype.tenable_propexxx=tenable_something', + national: true, + referrer: '$direct', + page_title: 'Mercedes-Benz Tire Center', + session_id: 123465, + search_type: 'Vehicle', + page_location: + 'https://somewebsite.com/?constructor.prototype.tenable_propexxx=tenable_something', + page_referrer: '$direct', + initial_referrer: '$direct', + oem_program_code: 'CODE', + engagement_time_msec: 1, + 'constructor.prototype.tenable_propexxx': 'tenable_something', + }, + }, + ], + client_id: 'f577a7e1-6c76-49c3-8312-12846471e025', + timestamp_micros: 1651209429000000, + }, + JSON_ARRAY: {}, + }, + type: 'REST', + files: {}, + method: 'POST', + params: { + api_secret: 'api_secr', + measurement_id: 'meas_id', + }, + userId: '', + headers: { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', + }, + version: '1', + endpoint: 'https://www.google-analytics.com/mp/collect', + }, + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, ]; diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/processor/data.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/processor/data.ts index 40d8370fcb..87fad8b9a5 100644 --- a/test/integrations/destinations/google_adwords_enhanced_conversions/processor/data.ts +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/processor/data.ts @@ -1720,4 +1720,194 @@ export const data = [ }, }, }, + { + name: 'google_adwords_enhanced_conversions', + description: 'Success test with configDetails', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + destination: { + Config: { + rudderAccountId: '25u5whFH7gVTnCiAjn4ykoCLGoC', + listOfConversions: ['Page View', 'Product Added'], + authStatus: 'active', + configData: '{"customerId": "1234567890", "loginCustomerId": ""}', + }, + }, + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + phone: '912382193', + firstName: 'John', + lastName: 'Gomes', + city: 'London', + state: 'UK', + countryCode: 'us', + streetAddress: '71 Cherry Court SOUTHAMPTON SO53 5PD UK', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + event: 'Page View', + type: 'track', + messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', + originalTimestamp: '2019-10-14T11:15:18.299Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { + gclid: 'gclid1234', + conversionDateTime: '2022-01-01 12:32:45-08:00', + adjustedValue: '10', + currency: 'INR', + adjustmentDateTime: '2022-01-01 12:32:45-08:00', + partialFailure: true, + campaignId: '1', + templateId: '0', + order_id: 10000, + total: 1000, + products: [ + { + product_id: '507f1f77bcf86cd799439011', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + price: '19', + position: '1', + category: 'cars', + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg', + quantity: '2', + }, + { + product_id: '507f1f77bcf86cd7994390112', + sku: '45790-322', + name: 'Monopoly: 3rd Edition2', + price: '192', + quantity: 22, + position: '12', + category: 'Cars2', + url: 'https://www.example.com/product/path2', + image_url: 'https://www.example.com/product/path.jpg2', + }, + ], + }, + integrations: { + All: true, + }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://googleads.googleapis.com/${API_VERSION}/customers/1234567890:uploadConversionAdjustments`, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + }, + params: { + event: 'Page View', + customerId: '1234567890', + }, + body: { + JSON: { + conversionAdjustments: [ + { + gclidDateTimePair: { + gclid: 'gclid1234', + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + restatementValue: { + adjustedValue: 10, + currencyCode: 'INR', + }, + orderId: '10000', + adjustmentDateTime: '2022-01-01 12:32:45-08:00', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + userIdentifiers: [ + { + hashedPhoneNumber: + '04387707e6cbed8c4538c81cc570ed9252d579469f36c273839b26d784e4bdbe', + }, + { + addressInfo: { + hashedFirstName: + 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', + hashedLastName: + '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', + state: 'UK', + city: 'London', + countryCode: 'us', + hashedStreetAddress: + '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', + }, + }, + ], + adjustmentType: 'ENHANCEMENT', + }, + ], + partialFailure: true, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + statusCode: 200, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/router/data.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/router/data.ts index 89ce06818b..33cb4a832f 100644 --- a/test/integrations/destinations/google_adwords_enhanced_conversions/router/data.ts +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/router/data.ts @@ -413,6 +413,91 @@ const events = [ sentAt: '2019-10-14T11:15:53.296Z', }, }, + { + metadata: { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + jobId: 6, + userId: 'u1', + }, + destination: { + Config: { + rudderAccountId: '25u5whFH7gVTnCiAjn4ykoCLGoC', + configData: '{"customerId":"1234567890", "loginCustomerId":"65656565"}', + listOfConversions: [{ conversions: 'Page View' }, { conversions: 'Product Added' }], + authStatus: 'active', + }, + }, + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + phone: '912382193', + firstName: 'John', + lastName: 'Gomes', + city: 'London', + state: 'UK', + streetAddress: '71 Cherry Court SOUTHAMPTON SO53 5PD UK', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + }, + event: 'Page View', + type: 'track', + messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', + originalTimestamp: '2019-10-14T11:15:18.299Z', + userId: '12345', + properties: { + gclid: 'gclid1234', + conversionDateTime: '2022-01-01 12:32:45-08:00', + adjustedValue: '10', + currency: 'INR', + adjustmentDateTime: '2022-01-01 12:32:45-08:00', + partialFailure: true, + campaignId: '1', + templateId: '0', + order_id: 10000, + total: 1000, + products: [ + { + product_id: '507f1f77bcf86cd799439011', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + price: '19', + position: '1', + category: 'cars', + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg', + quantity: '2', + }, + { + product_id: '507f1f77bcf86cd7994390112', + sku: '45790-322', + name: 'Monopoly: 3rd Edition2', + price: '192', + quantity: 22, + position: '12', + category: 'Cars2', + url: 'https://www.example.com/product/path2', + image_url: 'https://www.example.com/product/path.jpg2', + }, + ], + }, + integrations: { All: true }, + name: 'ApplicationLoaded', + }, + }, ]; const invalidRtTfCases = [ @@ -807,6 +892,86 @@ export const data = [ }, }, }, + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://googleads.googleapis.com/${API_VERSION}/customers/1234567890:uploadConversionAdjustments`, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '65656565', + }, + params: { event: 'Page View', customerId: '1234567890' }, + body: { + JSON: { + partialFailure: true, + conversionAdjustments: [ + { + gclidDateTimePair: { + gclid: 'gclid1234', + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + restatementValue: { adjustedValue: 10, currencyCode: 'INR' }, + orderId: '10000', + adjustmentDateTime: '2022-01-01 12:32:45-08:00', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + userIdentifiers: [ + { + hashedPhoneNumber: + '04387707e6cbed8c4538c81cc570ed9252d579469f36c273839b26d784e4bdbe', + }, + { + addressInfo: { + hashedFirstName: + 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', + hashedLastName: + '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', + state: 'UK', + city: 'London', + hashedStreetAddress: + '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', + }, + }, + ], + adjustmentType: 'ENHANCEMENT', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [ + { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + jobId: 6, + userId: 'u1', + }, + ], + batched: false, + statusCode: 200, + destination: { + Config: { + configData: '{"customerId":"1234567890", "loginCustomerId":"65656565"}', + rudderAccountId: '25u5whFH7gVTnCiAjn4ykoCLGoC', + listOfConversions: [ + { conversions: 'Page View' }, + { conversions: 'Product Added' }, + ], + authStatus: 'active', + }, + }, + }, ], }, }, diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/processor/data.ts b/test/integrations/destinations/google_adwords_remarketing_lists/processor/data.ts index e20ed89545..4398bc14e1 100644 --- a/test/integrations/destinations/google_adwords_remarketing_lists/processor/data.ts +++ b/test/integrations/destinations/google_adwords_remarketing_lists/processor/data.ts @@ -20,7 +20,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -151,7 +151,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '769-372-9833', loginCustomerId: '870-483-0944', subAccount: true, @@ -269,7 +269,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -387,7 +387,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -495,7 +495,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -585,7 +585,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -673,7 +673,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -749,7 +749,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: true, @@ -845,7 +845,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -2740,7 +2740,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -2993,7 +2993,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -6724,7 +6724,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -6880,7 +6880,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -6977,7 +6977,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -10708,7 +10708,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -10961,7 +10961,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -11207,7 +11207,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -11328,7 +11328,7 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', + audienceId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -11412,7 +11412,6 @@ export const data = [ destination: { Config: { rudderAccountId: 'rudder-acc-id', - listId: 'list111', customerId: '7693729833', loginCustomerId: '', subAccount: false, diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/router/audience.ts b/test/integrations/destinations/google_adwords_remarketing_lists/router/audience.ts index e0b534bb15..2158a57a5d 100644 --- a/test/integrations/destinations/google_adwords_remarketing_lists/router/audience.ts +++ b/test/integrations/destinations/google_adwords_remarketing_lists/router/audience.ts @@ -4,7 +4,7 @@ import { generateGoogleOAuthMetadata } from '../../../testUtils'; const destination: Destination = { Config: { rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', + audienceId: '7090784486', customerId: '7693729833', loginCustomerId: '', subAccount: false, diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/router/data.ts b/test/integrations/destinations/google_adwords_remarketing_lists/router/data.ts index f5789bf7ef..a5e28996b1 100644 --- a/test/integrations/destinations/google_adwords_remarketing_lists/router/data.ts +++ b/test/integrations/destinations/google_adwords_remarketing_lists/router/data.ts @@ -92,7 +92,7 @@ export const data = [ destination: { Config: { rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', + audienceId: '7090784486', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -187,7 +187,7 @@ export const data = [ destination: { Config: { rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', + audienceId: '7090784486', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -332,7 +332,7 @@ export const data = [ destination: { Config: { rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', + audienceId: '7090784486', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -451,7 +451,7 @@ export const data = [ destination: { Config: { rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', + audienceId: '7090784486', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -578,7 +578,7 @@ export const data = [ destination: { Config: { rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', + audienceId: '7090784486', customerId: '7693729833', loginCustomerId: '', subAccount: false, @@ -675,7 +675,7 @@ export const data = [ destination: { Config: { rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', + audienceId: '7090784486', customerId: '7693729833', loginCustomerId: '', subAccount: false, diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/router/record.ts b/test/integrations/destinations/google_adwords_remarketing_lists/router/record.ts index bedf112866..de76aae17c 100644 --- a/test/integrations/destinations/google_adwords_remarketing_lists/router/record.ts +++ b/test/integrations/destinations/google_adwords_remarketing_lists/router/record.ts @@ -4,7 +4,7 @@ import { generateGoogleOAuthMetadata } from '../../../testUtils'; const destination: Destination = { Config: { rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', + audienceId: '7090784486', customerId: '7693729833', loginCustomerId: '', subAccount: false, diff --git a/test/integrations/destinations/twitter_ads/processor/data.ts b/test/integrations/destinations/twitter_ads/processor/data.ts index b6a7512880..0ae21ffc98 100644 --- a/test/integrations/destinations/twitter_ads/processor/data.ts +++ b/test/integrations/destinations/twitter_ads/processor/data.ts @@ -131,6 +131,7 @@ export const data = [ conversion_time: '2023-06-01T06:03:08.739Z', number_items: 2, price_currency: 'USD', + user_agent: 'chrome', value: '25', conversion_id: '213123', contents: [ @@ -290,7 +291,8 @@ export const data = [ }, }, statusCode: 400, - error: '[TWITTER ADS]: one of twclid, phone or email must be present in properties.', + error: + '[TWITTER ADS]: one of twclid, phone, email, ip_address or user_agent must be present in properties.', statTags: { errorCategory: 'dataValidation', errorType: 'instrumentation', @@ -424,6 +426,7 @@ export const data = [ conversion_time: '2023-06-01T06:03:08.739Z', number_items: 2, price_currency: 'USD', + user_agent: 'chrome', value: '25.55', conversion_id: '213123', identifiers: [ @@ -842,6 +845,7 @@ export const data = [ conversion_time: '2023-06-01T06:03:08.739Z', number_items: 2, price_currency: 'USD', + user_agent: 'chrome', value: '25', conversion_id: '213123', contents: [ @@ -894,6 +898,233 @@ export const data = [ }, }, }, + { + name: 'twitter_ads', + description: 'Test case for track event with ip_address as an identifier', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Home Page Viewed', + channel: 'web', + context: { + source: 'test', + userAgent: 'chrome', + traits: { + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + email: 'abc@gmail.com', + phone: '+1234589947', + ge: 'male', + }, + device: { + advertisingId: 'abc123', + }, + library: { + name: 'rudder-sdk-ruby-sync', + version: '1.0.6', + }, + }, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: { + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', + ip_address: '8.25.197.25', + }, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { + All: true, + }, + }, + metadata: { + secret: { + consumerKey: 'qwe', + consumerSecret: 'fdghv', + accessToken: 'dummyAccessToken', + accessTokenSecret: 'testAccessTokenSecret', + }, + }, + destination: { + Config: { + pixelId: 'dummyPixelId', + rudderAccountId: '2EOknn1JNH7WK1MfNku4fGYKkRK', + twitterAdsEventNames: [ + { + rudderEventName: 'ABC Searched', + twitterEventId: 'tw-234234324234', + }, + { + rudderEventName: 'Home Page Viewed', + twitterEventId: 'tw-odt2o-odt2q', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://ads-api.twitter.com/12/measurement/conversions/dummyPixelId', + headers: { + Authorization: authHeaderConstant, + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + conversions: [ + { + conversion_time: '2020-08-14T05:30:30.118Z', + user_agent: 'chrome', + event_id: 'tw-odt2o-odt2q', + identifiers: [ + { + ip_address: '8.25.197.25', + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: { + secret: { + consumerKey: 'qwe', + consumerSecret: 'fdghv', + accessToken: 'dummyAccessToken', + accessTokenSecret: 'testAccessTokenSecret', + }, + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'twitter_ads', + description: 'Test case for track event with user_agent as an identifier', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Home Page Viewed', + channel: 'web', + context: { + source: 'test', + traits: { + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + email: 'abc@gmail.com', + phone: '+1234589947', + ge: 'male', + }, + }, + properties: { + affiliation: 'Google Store', + user_agent: + ' Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36.', + }, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { + All: true, + }, + }, + metadata: { + secret: { + accessTokenSecret: 'testAccessTokenSecret', + }, + }, + destination: { + Config: { + pixelId: 'dummyPixelId', + rudderAccountId: '2EOknn1JNH7WK1MfNku4fGYKkRK', + twitterAdsEventNames: [ + { + rudderEventName: 'ABC Searched', + twitterEventId: 'tw-234234324234', + }, + { + rudderEventName: 'Home Page Viewed', + twitterEventId: 'tw-odt2o-odt2q', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://ads-api.twitter.com/12/measurement/conversions/dummyPixelId', + headers: { + Authorization: authHeaderConstant, + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + conversions: [ + { + event_id: 'tw-odt2o-odt2q', + identifiers: [ + { + user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36.', + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: { + secret: { + accessTokenSecret: 'testAccessTokenSecret', + }, + }, + statusCode: 200, + }, + ], + }, + }, + }, ].map((tc) => ({ ...tc, mockFns: (_) => { diff --git a/test/integrations/destinations/twitter_ads/router/data.ts b/test/integrations/destinations/twitter_ads/router/data.ts index ce9aea6595..7e8061dd7e 100644 --- a/test/integrations/destinations/twitter_ads/router/data.ts +++ b/test/integrations/destinations/twitter_ads/router/data.ts @@ -146,6 +146,7 @@ export const data = [ ], number_items: 2, price_currency: 'USD', + user_agent: 'chrome', value: '25', }, ],