diff --git a/src/v0/destinations/fb_custom_audience/recordTransform.js b/src/v0/destinations/fb_custom_audience/recordTransform.js index 0f7b65c0bf..62d4bd568b 100644 --- a/src/v0/destinations/fb_custom_audience/recordTransform.js +++ b/src/v0/destinations/fb_custom_audience/recordTransform.js @@ -1,11 +1,7 @@ /* eslint-disable no-const-assign */ const lodash = require('lodash'); const get = require('get-value'); -const { - InstrumentationError, - ConfigurationError, - getErrorRespEvents, -} = require('@rudderstack/integrations-lib'); +const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); const { schemaFields } = require('./config'); const { MappedToDestinationKey } = require('../../../constants'); const stats = require('../../../util/stats'); @@ -15,8 +11,8 @@ const { checkSubsetOfArray, returnArrayOfSubarrays, getSuccessRespEvents, - generateErrorObject, } = require('../../util'); +const { getErrorResponse, createFinalResponse } = require('../../util/recordUtils'); const { ensureApplicableFormat, getUpdatedDataElement, @@ -26,19 +22,6 @@ const { getDataSource, } = require('./util'); -function getErrorMetaData(inputs, acceptedOperations) { - const metadata = []; - // eslint-disable-next-line no-restricted-syntax - for (const key in inputs) { - if (!acceptedOperations.includes(key)) { - inputs[key].forEach((input) => { - metadata.push(input.metadata); - }); - } - } - return metadata; -} - const processRecordEventArray = ( recordChunksArray, userSchema, @@ -177,8 +160,6 @@ async function processRecordInputs(groupedRecordInputs) { record.message.action?.toLowerCase(), ); - const finalResponse = []; - let insertResponse; let deleteResponse; let updateResponse; @@ -238,32 +219,14 @@ async function processRecordInputs(groupedRecordInputs) { ); } - const eventTypes = ['update', 'insert', 'delete']; - const errorMetaData = []; - const errorMetaDataObject = getErrorMetaData(groupedRecordsByAction, eventTypes); - if (errorMetaDataObject.length > 0) { - errorMetaData.push(errorMetaDataObject); - } + const errorResponse = getErrorResponse(groupedRecordsByAction); - const error = new InstrumentationError('Invalid action type in record event'); - const errorObj = generateErrorObject(error); - const errorResponseList = errorMetaData.map((metadata) => - getErrorRespEvents(metadata, errorObj.status, errorObj.message, errorObj.statTags), + const finalResponse = createFinalResponse( + deleteResponse, + insertResponse, + updateResponse, + errorResponse, ); - - if (deleteResponse && deleteResponse.batchedRequest.length > 0) { - finalResponse.push(deleteResponse); - } - if (insertResponse && insertResponse.batchedRequest.length > 0) { - finalResponse.push(insertResponse); - } - if (updateResponse && updateResponse.batchedRequest.length > 0) { - finalResponse.push(updateResponse); - } - if (errorResponseList.length > 0) { - finalResponse.push(...errorResponseList); - } - if (finalResponse.length === 0) { throw new InstrumentationError( 'Missing valid parameters, unable to generate transformed payload', diff --git a/src/v0/destinations/google_adwords_remarketing_lists/config.js b/src/v0/destinations/google_adwords_remarketing_lists/config.js index f8983699c6..c7b97e0e6c 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/config.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/config.js @@ -1,6 +1,6 @@ const { getMappingConfig } = require('../../util'); -const BASE_ENDPOINT = 'https://googleads.googleapis.com/v15/customers'; +const BASE_ENDPOINT = 'https://googleads.googleapis.com/v16/customers'; const CONFIG_CATEGORIES = { AUDIENCE_LIST: { type: 'audienceList', name: 'offlineDataJobs' }, ADDRESSINFO: { type: 'addressInfo', name: 'addressInfo' }, diff --git a/src/v0/destinations/google_adwords_remarketing_lists/recordTransform.js b/src/v0/destinations/google_adwords_remarketing_lists/recordTransform.js new file mode 100644 index 0000000000..18e7f96821 --- /dev/null +++ b/src/v0/destinations/google_adwords_remarketing_lists/recordTransform.js @@ -0,0 +1,144 @@ +/* eslint-disable no-const-assign */ +const lodash = require('lodash'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { + getValueFromMessage, + getAccessToken, + constructPayload, + returnArrayOfSubarrays, + getSuccessRespEvents, +} = require('../../util'); +const { populateConsentFromConfig } = require('../../util/googleUtils'); +const { populateIdentifiers, responseBuilder } = require('./util'); +const { getErrorResponse, createFinalResponse } = require('../../util/recordUtils'); +const { offlineDataJobsMapping, consentConfigMap } = require('./config'); + +const processRecordEventArray = ( + records, + message, + destination, + accessToken, + developerToken, + operationType, +) => { + let outputPayloads = {}; + // ** only send it if identifier > 0 + + const fieldsArray = []; + const metadata = []; + records.forEach((record) => { + fieldsArray.push(record.message.fields); + metadata.push(record.metadata); + }); + + const userIdentifiersList = populateIdentifiers(fieldsArray, destination); + + const outputPayload = constructPayload(message, offlineDataJobsMapping); + outputPayload.operations = []; + // breaking the userIdentiFier array in chunks of 20 + const userIdentifierChunks = returnArrayOfSubarrays(userIdentifiersList, 20); + // putting each chunk in different create/remove operations + switch (operationType) { + case 'add': + // for add operation + userIdentifierChunks.forEach((element) => { + const operations = { + create: {}, + }; + operations.create.userIdentifiers = element; + outputPayload.operations.push(operations); + }); + outputPayloads = { ...outputPayloads, create: outputPayload }; + break; + case 'remove': + // for remove operation + userIdentifierChunks.forEach((element) => { + const operations = { + remove: {}, + }; + operations.remove.userIdentifiers = element; + outputPayload.operations.push(operations); + }); + outputPayloads = { ...outputPayloads, remove: outputPayload }; + break; + default: + } + + const toSendEvents = []; + Object.values(outputPayloads).forEach((data) => { + const consentObj = populateConsentFromConfig(destination.Config, consentConfigMap); + toSendEvents.push( + responseBuilder(accessToken, developerToken, data, destination, message, consentObj), + ); + }); + + const successResponse = getSuccessRespEvents(toSendEvents, metadata, destination, true); + + return successResponse; +}; + +async function processRecordInputs(groupedRecordInputs) { + const { destination, message, metadata } = groupedRecordInputs[0]; + const accessToken = getAccessToken(metadata, 'accessToken'); + const developerToken = getValueFromMessage(metadata, 'secret.developer_token'); + + const groupedRecordsByAction = lodash.groupBy(groupedRecordInputs, (record) => + record.message.action?.toLowerCase(), + ); + + let insertResponse; + let deleteResponse; + let updateResponse; + + if (groupedRecordsByAction.delete) { + deleteResponse = processRecordEventArray( + groupedRecordsByAction.delete, + message, + destination, + accessToken, + developerToken, + 'remove', + ); + } + + if (groupedRecordsByAction.insert) { + insertResponse = processRecordEventArray( + groupedRecordsByAction.insert, + message, + destination, + accessToken, + developerToken, + 'add', + ); + } + + if (groupedRecordsByAction.update) { + updateResponse = processRecordEventArray( + groupedRecordsByAction.update, + message, + destination, + accessToken, + developerToken, + 'add', + ); + } + + const errorResponse = getErrorResponse(groupedRecordsByAction); + const finalResponse = createFinalResponse( + deleteResponse, + insertResponse, + updateResponse, + errorResponse, + ); + if (finalResponse.length === 0) { + throw new InstrumentationError( + 'Missing valid parameters, unable to generate transformed payload', + ); + } + + return finalResponse; +} + +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 b0dfaa0c35..d879a39c63 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/transform.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/transform.js @@ -1,138 +1,29 @@ -const sha256 = require('sha256'); -const get = require('get-value'); +const lodash = require('lodash'); const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); const logger = require('../../../logger'); const { - isDefinedAndNotNullAndNotEmpty, returnArrayOfSubarrays, constructPayload, - defaultRequestConfig, getValueFromMessage, - removeUndefinedAndNullValues, - removeHyphens, simpleProcessRouterDest, - getDestinationExternalIDInfoForRetl, getAccessToken, } = require('../../util'); const { populateConsentFromConfig } = require('../../util/googleUtils'); - -const { - offlineDataJobsMapping, - addressInfoMapping, - BASE_ENDPOINT, - attributeMapping, - hashAttributes, - TYPEOFLIST, - consentConfigMap, -} = require('./config'); -const { JSON_MIME_TYPE } = require('../../util/constant'); -const { MappedToDestinationKey } = require('../../../constants'); - -const hashEncrypt = (object) => { - Object.keys(object).forEach((key) => { - if (hashAttributes.includes(key) && object[key]) { - // eslint-disable-next-line no-param-reassign - object[key] = sha256(object[key]); +const { offlineDataJobsMapping, consentConfigMap } = require('./config'); +const { processRecordInputs } = require('./recordTransform'); +const { populateIdentifiers, responseBuilder } = require('./util'); + +function extraKeysPresent(dictionary, keyList) { + // eslint-disable-next-line no-restricted-syntax + for (const key in dictionary) { + if (!keyList.includes(key)) { + return true; } - }); -}; - -/** - * This function is used for building the response. It create a default rudder response - * and populate headers, params and body.JSON - * @param {*} metadata - * @param {*} body - * @param {*} param2 - * @returns - */ -const responseBuilder = (metadata, body, { Config }, message, consentBlock) => { - const payload = body; - const response = defaultRequestConfig(); - const filteredCustomerId = removeHyphens(Config.customerId); - response.endpoint = `${BASE_ENDPOINT}/${filteredCustomerId}/offlineUserDataJobs`; - response.body.JSON = removeUndefinedAndNullValues(payload); - const accessToken = getAccessToken(metadata, 'access_token'); - 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)) { - throw new ConfigurationError('List ID is a mandatory field'); } - response.params = { - listId: operationAudienceId, - customerId: filteredCustomerId, - consent: consentBlock, - }; - response.headers = { - Authorization: `Bearer ${accessToken}`, - 'Content-Type': JSON_MIME_TYPE, - 'developer-token': getValueFromMessage(metadata, 'secret.developer_token'), - }; - if (Config.subAccount) - if (Config.loginCustomerId) { - const filteredLoginCustomerId = removeHyphens(Config.loginCustomerId); - response.headers['login-customer-id'] = filteredLoginCustomerId; - } else throw new ConfigurationError(`loginCustomerId is required as subAccount is true.`); - return response; -}; -/** - * 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 - * @returns - */ + return false; +} -const populateIdentifiers = (attributeArray, { Config }) => { - const userIdentifier = []; - const { typeOfList } = Config; - const { isHashRequired, userSchema } = Config; - let attribute; - if (TYPEOFLIST[typeOfList]) { - attribute = TYPEOFLIST[typeOfList]; - } else { - attribute = userSchema; - } - if (isDefinedAndNotNullAndNotEmpty(attributeArray)) { - // traversing through every element in the add array - attributeArray.forEach((element, index) => { - if (isHashRequired) { - hashEncrypt(element); - } - // checking if the attribute is an array or not for generic type list - if (!Array.isArray(attribute)) { - if (element[attribute]) { - userIdentifier.push({ [attribute]: element[attribute] }); - } else { - logger.info(` ${attribute} is not present in index:`, index); - } - } else { - attribute.forEach((attributeElement, index2) => { - if (attributeElement === 'addressInfo') { - const addressInfo = constructPayload(element, addressInfoMapping); - // checking if addressInfo object is empty or not. - if (isDefinedAndNotNullAndNotEmpty(addressInfo)) userIdentifier.push({ addressInfo }); - } else if (element[`${attributeElement}`]) { - userIdentifier.push({ - [`${attributeMapping[attributeElement]}`]: element[`${attributeElement}`], - }); - } else { - logger.info(` ${attribute[index2]} is not present in index:`, index); - } - }); - } - }); - } - return userIdentifier; -}; /** * This function helps to create different operations by breaking the * userIdentiFier Array in chunks of 20. @@ -143,7 +34,6 @@ const populateIdentifiers = (attributeArray, { Config }) => { * @param {rudder event destination} destination * @returns */ - const createPayload = (message, destination) => { const { listData } = message.properties; const properties = ['add', 'remove']; @@ -218,9 +108,14 @@ const processEvent = async (metadata, message, destination) => { ); } + const accessToken = getAccessToken(metadata, 'accessToken'); + const developerToken = getValueFromMessage(metadata, 'secret.developer_token'); + Object.values(createdPayload).forEach((data) => { const consentObj = populateConsentFromConfig(destination.Config, consentConfigMap); - response.push(responseBuilder(metadata, data, destination, message, consentObj)); + response.push( + responseBuilder(accessToken, developerToken, data, destination, message, consentObj), + ); }); return response; } @@ -231,7 +126,29 @@ const processEvent = async (metadata, message, destination) => { const process = async (event) => processEvent(event.metadata, event.message, event.destination); const processRouterDest = async (inputs, reqMetadata) => { - const respList = await simpleProcessRouterDest(inputs, process, reqMetadata); + const respList = []; + const groupedInputs = lodash.groupBy(inputs, (input) => input.message.type?.toLowerCase()); + let transformedRecordEvent = []; + let transformedAudienceEvent = []; + + const eventTypes = ['record', 'audiencelist']; + if (extraKeysPresent(groupedInputs, eventTypes)) { + throw new ConfigurationError('unsupported events present in the event'); + } + + if (groupedInputs.record) { + transformedRecordEvent = await processRecordInputs(groupedInputs.record, reqMetadata); + } + + if (groupedInputs.audiencelist) { + transformedAudienceEvent = await simpleProcessRouterDest( + groupedInputs.audiencelist, + process, + reqMetadata, + ); + } + + respList.push(...transformedRecordEvent, ...transformedAudienceEvent); return respList; }; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/util.js b/src/v0/destinations/google_adwords_remarketing_lists/util.js new file mode 100644 index 0000000000..3e04dd8f6f --- /dev/null +++ b/src/v0/destinations/google_adwords_remarketing_lists/util.js @@ -0,0 +1,122 @@ +const get = require('get-value'); +const sha256 = require('sha256'); +const { ConfigurationError } = require('@rudderstack/integrations-lib'); +const { + isDefinedAndNotNullAndNotEmpty, + constructPayload, + defaultRequestConfig, + removeHyphens, + removeUndefinedAndNullValues, + getDestinationExternalIDInfoForRetl, +} = require('../../util'); +const logger = require('../../../logger'); +const { MappedToDestinationKey } = require('../../../constants'); +const { JSON_MIME_TYPE } = require('../../util/constant'); +const { + addressInfoMapping, + attributeMapping, + TYPEOFLIST, + BASE_ENDPOINT, + hashAttributes, +} = require('./config'); + +const hashEncrypt = (object) => { + Object.keys(object).forEach((key) => { + if (hashAttributes.includes(key) && object[key]) { + // eslint-disable-next-line no-param-reassign + object[key] = sha256(object[key]); + } + }); +}; + +const responseBuilder = (accessToken, developerToken, body, { Config }, message, 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)) { + throw new ConfigurationError('List ID is a mandatory field'); + } + response.params = { + listId: operationAudienceId, + customerId: filteredCustomerId, + consent: consentBlock, + }; + response.headers = { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': JSON_MIME_TYPE, + 'developer-token': developerToken, + }; + if (Config.subAccount) + if (Config.loginCustomerId) { + const filteredLoginCustomerId = removeHyphens(Config.loginCustomerId); + response.headers['login-customer-id'] = filteredLoginCustomerId; + } else throw new ConfigurationError(`loginCustomerId is required as subAccount is true.`); + return response; +}; + +/** + * 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 + * @returns + */ +const populateIdentifiers = (attributeArray, { Config }) => { + const userIdentifier = []; + const { typeOfList } = Config; + const { isHashRequired, userSchema } = Config; + let attribute; + if (TYPEOFLIST[typeOfList]) { + attribute = TYPEOFLIST[typeOfList]; + } else { + attribute = userSchema; + } + if (isDefinedAndNotNullAndNotEmpty(attributeArray)) { + // traversing through every element in the add array + attributeArray.forEach((element, index) => { + if (isHashRequired) { + hashEncrypt(element); + } + // checking if the attribute is an array or not for generic type list + if (!Array.isArray(attribute)) { + if (element[attribute]) { + userIdentifier.push({ [attribute]: element[attribute] }); + } else { + logger.info(` ${attribute} is not present in index:`, index); + } + } else { + attribute.forEach((attributeElement, index2) => { + if (attributeElement === 'addressInfo') { + const addressInfo = constructPayload(element, addressInfoMapping); + // checking if addressInfo object is empty or not. + if (isDefinedAndNotNullAndNotEmpty(addressInfo)) userIdentifier.push({ addressInfo }); + } else if (element[`${attributeElement}`]) { + userIdentifier.push({ + [`${attributeMapping[attributeElement]}`]: element[`${attributeElement}`], + }); + } else { + logger.info(` ${attribute[index2]} is not present in index:`, index); + } + }); + } + }); + } + return userIdentifier; +}; + +module.exports = { + populateIdentifiers, + responseBuilder, +}; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/util.test.js b/src/v0/destinations/google_adwords_remarketing_lists/util.test.js new file mode 100644 index 0000000000..e9fe90c317 --- /dev/null +++ b/src/v0/destinations/google_adwords_remarketing_lists/util.test.js @@ -0,0 +1,203 @@ +const { populateIdentifiers, responseBuilder } = require('./util'); + +const accessToken = 'abcd1234'; +const developerToken = 'ijkl9101'; +const body = { + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, + ], + }, + }, + ], +}; +const baseDestination = { + Config: { + rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', + listId: '7090784486', + customerId: '7693729833', + loginCustomerId: '', + subAccount: false, + userSchema: ['email', 'phone', 'addressInfo'], + isHashRequired: true, + typeOfList: 'General', + }, +}; +const consentBlock = { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', +}; +const message = { + action: 'insert', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + phone: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + firstName: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + lastName: 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + country: 'US', + postalCode: '1245', + }, + type: 'record', +}; +const expectedResponse = { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl9101', + }, + params: { + listId: '7090784486', + customerId: '7693729833', + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, + }, + body: { + JSON: { + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: + '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: + 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, + ], + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, +}; +const attributeArray = [ + { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, +]; + +const hashedArray = [ + { + hashedEmail: 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, +]; + +describe('GARL utils test', () => { + describe('responseBuilder function tests', () => { + it('Should return correct response for given payload', () => { + const response = responseBuilder( + accessToken, + developerToken, + body, + baseDestination, + message, + consentBlock, + ); + expect(response).toEqual(expectedResponse); + }); + + it('Should throw error if subaccount is true and loginCustomerId is not defined', () => { + try { + const destination2 = Object.create(baseDestination); + destination2.Config.subAccount = true; + destination2.Config.loginCustomerId = ''; + const response = responseBuilder( + accessToken, + developerToken, + body, + destination2, + message, + consentBlock, + ); + expect(response).toEqual(); + } catch (error) { + expect(error.message).toEqual(`loginCustomerId is required as subAccount is true.`); + } + }); + + it('Should throw error if operationAudienceId is not defined', () => { + try { + const destination1 = Object.create(baseDestination); + destination1.Config.listId = ''; + const response = responseBuilder( + accessToken, + developerToken, + body, + destination1, + message, + consentBlock, + ); + expect(response).toEqual(); + } catch (error) { + expect(error.message).toEqual(`List ID is a mandatory field`); + } + }); + }); + + describe('populateIdentifiers function tests', () => { + it('Should hash and return identifiers for a given list of attributes', () => { + const identifier = populateIdentifiers(attributeArray, baseDestination); + expect(identifier).toEqual(hashedArray); + }); + }); +}); diff --git a/src/v0/util/recordUtils.js b/src/v0/util/recordUtils.js new file mode 100644 index 0000000000..a3dd65ef33 --- /dev/null +++ b/src/v0/util/recordUtils.js @@ -0,0 +1,55 @@ +const { InstrumentationError, getErrorRespEvents } = require('@rudderstack/integrations-lib'); +const { generateErrorObject } = require('./index'); + +const eventTypes = ['update', 'insert', 'delete']; + +function getErrorMetaData(inputs, acceptedOperations) { + const metadata = []; + // eslint-disable-next-line no-restricted-syntax + for (const key in inputs) { + if (!acceptedOperations.includes(key)) { + inputs[key].forEach((input) => { + metadata.push(input.metadata); + }); + } + } + return metadata; +} + +function getErrorResponse(groupedRecordsByAction) { + const errorMetaData = []; + const errorMetaDataObject = getErrorMetaData(groupedRecordsByAction, eventTypes); + if (errorMetaDataObject.length > 0) { + errorMetaData.push(errorMetaDataObject); + } + + const error = new InstrumentationError('Invalid action type in record event'); + const errorObj = generateErrorObject(error); + const errorResponseList = errorMetaData.map((data) => + getErrorRespEvents(data, errorObj.status, errorObj.message, errorObj.statTags), + ); + + return errorResponseList; +} + +function createFinalResponse(deleteResponse, insertResponse, updateResponse, errorResponseList) { + const finalResponse = []; + if (deleteResponse && deleteResponse.batchedRequest.length > 0) { + finalResponse.push(deleteResponse); + } + if (insertResponse && insertResponse.batchedRequest.length > 0) { + finalResponse.push(insertResponse); + } + if (updateResponse && updateResponse.batchedRequest.length > 0) { + finalResponse.push(updateResponse); + } + if (errorResponseList.length > 0) { + finalResponse.push(...errorResponseList); + } + return finalResponse; +} + +module.exports = { + getErrorResponse, + createFinalResponse, +}; 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 a846e0370d..639e28403c 100644 --- a/test/integrations/destinations/google_adwords_remarketing_lists/processor/data.ts +++ b/test/integrations/destinations/google_adwords_remarketing_lists/processor/data.ts @@ -11,7 +11,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -70,7 +70,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -120,7 +120,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -143,7 +143,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -203,7 +203,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -239,7 +239,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -262,7 +262,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -323,7 +323,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -358,7 +358,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -381,7 +381,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -456,7 +456,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -489,7 +489,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -546,7 +546,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -579,7 +579,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -635,7 +635,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -667,7 +667,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -711,7 +711,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -743,7 +743,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -807,7 +807,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -839,7 +839,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -1425,7 +1425,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -2712,7 +2712,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -2735,7 +2735,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -2820,7 +2820,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -2887,7 +2887,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -2900,7 +2900,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -2967,7 +2967,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -2990,7 +2990,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -4113,7 +4113,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -5400,7 +5400,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -5413,7 +5413,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -6700,7 +6700,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -6723,7 +6723,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -6790,7 +6790,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -6857,7 +6857,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -6880,7 +6880,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -6944,7 +6944,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -6977,7 +6977,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -8100,7 +8100,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -9387,7 +9387,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -9400,7 +9400,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -10687,7 +10687,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -10710,7 +10710,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -10795,7 +10795,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -10862,7 +10862,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -10875,7 +10875,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -10942,7 +10942,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -10965,7 +10965,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11050,7 +11050,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11115,7 +11115,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11128,7 +11128,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11190,7 +11190,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11213,7 +11213,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11272,7 +11272,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11316,7 +11316,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11419,7 +11419,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11479,7 +11479,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11523,7 +11523,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11546,7 +11546,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11618,7 +11618,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11661,7 +11661,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11684,7 +11684,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11756,7 +11756,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11799,7 +11799,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11822,7 +11822,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11896,7 +11896,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11942,7 +11942,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11965,7 +11965,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -12039,7 +12039,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -12082,7 +12082,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/router/audience.ts b/test/integrations/destinations/google_adwords_remarketing_lists/router/audience.ts new file mode 100644 index 0000000000..233f160ad3 --- /dev/null +++ b/test/integrations/destinations/google_adwords_remarketing_lists/router/audience.ts @@ -0,0 +1,129 @@ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + Config: { + rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', + listId: '7090784486', + customerId: '7693729833', + loginCustomerId: '', + subAccount: false, + userSchema: ['email', 'phone', 'addressInfo'], + isHashRequired: true, + typeOfList: 'General', + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, +}; + +export const rETLAudienceRouterRequest: RouterTransformationRequest = { + input: [ + { + metadata: generateMetadata(1), + destination: destination, + message: { + userId: 'user 1', + anonymousId: 'anon-id-new', + event: 'event1', + type: 'audiencelist', + properties: { + listData: { + add: [ + { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + ], + }, + enablePartialFailure: true, + }, + context: { ip: '14.5.67.21', library: { name: 'http' } }, + timestamp: '2020-02-02T00:23:09.544Z', + }, + }, + { + metadata: generateMetadata(3), + destination: destination, + message: { + userId: 'user 1', + anonymousId: 'anon-id-new', + event: 'event1', + type: 'audiencelist', + properties: { + listData: { + remove: [ + { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + ], + }, + enablePartialFailure: true, + }, + context: { ip: '14.5.67.21', library: { name: 'http' } }, + timestamp: '2020-02-02T00:23:09.544Z', + }, + }, + { + metadata: generateMetadata(4), + destination: destination, + message: { + userId: 'user 1', + anonymousId: 'anon-id-new', + event: 'event1', + type: 'audiencelist', + properties: { + listData: { + remove: [ + { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + ], + add: [ + { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + ], + }, + enablePartialFailure: true, + }, + context: { ip: '14.5.67.21', library: { name: 'http' } }, + timestamp: '2020-02-02T00:23:09.544Z', + }, + }, + ], + destType: 'google_adwords_remarketing_lists', +}; + +module.exports = { + rETLAudienceRouterRequest, +}; 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 31d5c72694..35da4daff5 100644 --- a/test/integrations/destinations/google_adwords_remarketing_lists/router/data.ts +++ b/test/integrations/destinations/google_adwords_remarketing_lists/router/data.ts @@ -1,3 +1,6 @@ +import { rETLAudienceRouterRequest } from './audience'; +import { rETLRecordRouterRequest } from './record'; + export const data = [ { name: 'google_adwords_remarketing_lists', @@ -7,157 +10,85 @@ export const data = [ version: 'v0', input: { request: { + body: rETLAudienceRouterRequest, + method: 'POST', + }, + }, + output: { + response: { + status: 200, body: { - input: [ + output: [ { - metadata: { - secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', - }, - jobId: 1, - userId: 'u1', - }, - destination: { - Config: { - rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', - customerId: '7693729833', - loginCustomerId: '', - subAccount: false, - userSchema: ['email', 'phone', 'addressInfo'], - isHashRequired: true, - typeOfList: 'General', - }, - }, - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - add: [ - { - email: 'test@abc.com', - phone: '@09876543210', - firstName: 'test', - lastName: 'rudderlabs', - country: 'US', - postalCode: '1245', - }, - ], + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', + headers: { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', }, - enablePartialFailure: true, - }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', - }, - }, - { - metadata: { - secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', - }, - jobId: 2, - userId: 'u1', - }, - destination: { - Config: { - rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', - customerId: '7693729833', - loginCustomerId: '', - subAccount: false, - userSchema: ['email', 'phone', 'addressInfo'], - isHashRequired: true, - typeOfList: 'userID', - }, - }, - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - add: [ - { - email: 'test@abc.com', - phone: '@09876543210', - firstName: 'test', - lastName: 'rudderlabs', - country: 'US', - postalCode: '1245', - thirdPartyUserId: 'useri1234', - }, - ], + params: { + listId: '7090784486', + customerId: '7693729833', + consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, }, - enablePartialFailure: true, - }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', - }, - }, - { - metadata: { - secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', - }, - jobId: 3, - userId: 'u1', - }, - destination: { - Config: { - rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', - customerId: '7693729833', - loginCustomerId: '', - subAccount: false, - userSchema: ['email', 'phone', 'addressInfo'], - isHashRequired: true, - typeOfList: 'General', - }, - }, - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - remove: [ - { - email: 'test@abc.com', - phone: '@09876543210', - firstName: 'test', - lastName: 'rudderlabs', - country: 'US', - postalCode: '1245', - }, - ], + body: { + JSON: { + enablePartialFailure: true, + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: + 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: + '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: + 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, + ], + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, - enablePartialFailure: true, + files: {}, }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', - }, - }, - { - metadata: { - secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', + ], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 1, }, - jobId: 4, - userId: 'u1', - }, + ], + batched: false, + statusCode: 200, destination: { Config: { rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', @@ -169,52 +100,21 @@ export const data = [ isHashRequired: true, typeOfList: 'General', }, - }, - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - remove: [ - { - email: 'test@abc.com', - phone: '@09876543210', - firstName: 'test', - lastName: 'rudderlabs', - country: 'US', - postalCode: '1245', - }, - ], - add: [ - { - email: 'test@abc.com', - phone: '@09876543210', - firstName: 'test', - lastName: 'rudderlabs', - country: 'US', - postalCode: '1245', - }, - ], - }, - enablePartialFailure: true, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', }, }, - ], - destType: 'google_adwords_remarketing_lists', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: [ { batchedRequest: [ { @@ -222,11 +122,10 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { - Authorization: 'Bearer abcd1234', + Authorization: 'Bearer default-accessToken', 'Content-Type': 'application/json', - 'developer-token': 'ijkl9101', }, params: { listId: '7090784486', @@ -238,7 +137,7 @@ export const data = [ enablePartialFailure: true, operations: [ { - create: { + remove: { userIdentifiers: [ { hashedEmail: @@ -272,13 +171,16 @@ export const data = [ ], metadata: [ { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', + accessToken: 'default-accessToken', }, - jobId: 1, - userId: 'u1', + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 3, }, ], batched: false, @@ -294,6 +196,19 @@ export const data = [ isHashRequired: true, typeOfList: 'General', }, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + }, + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', }, }, { @@ -303,11 +218,10 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { - Authorization: 'Bearer abcd1234', + Authorization: 'Bearer default-accessToken', 'Content-Type': 'application/json', - 'developer-token': 'ijkl9101', }, params: { listId: '7090784486', @@ -318,7 +232,81 @@ export const data = [ JSON: { enablePartialFailure: true, operations: [ - { create: { userIdentifiers: [{ thirdPartyUserId: 'useri1234' }] } }, + { + remove: { + userIdentifiers: [ + { + hashedEmail: + 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: + '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: + 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, + ], + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', + headers: { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', + }, + params: { + listId: '7090784486', + customerId: '7693729833', + consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, + }, + body: { + JSON: { + enablePartialFailure: true, + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: + 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: + '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: + 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, + ], + }, + }, ], }, JSON_ARRAY: {}, @@ -330,13 +318,16 @@ export const data = [ ], metadata: [ { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', + accessToken: 'default-accessToken', }, - jobId: 2, - userId: 'u1', + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 4, }, ], batched: false, @@ -350,10 +341,45 @@ export const data = [ subAccount: false, userSchema: ['email', 'phone', 'addressInfo'], isHashRequired: true, - typeOfList: 'userID', + typeOfList: 'General', + }, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', }, + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', }, }, + ], + }, + }, + }, + }, + { + name: 'google_adwords_remarketing_lists record event tests', + description: 'Test 1', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: rETLRecordRouterRequest, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ { batchedRequest: [ { @@ -361,20 +387,21 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { - Authorization: 'Bearer abcd1234', + Authorization: 'Bearer default-accessToken', 'Content-Type': 'application/json', - 'developer-token': 'ijkl9101', }, params: { listId: '7090784486', customerId: '7693729833', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, }, body: { JSON: { - enablePartialFailure: true, operations: [ { remove: { @@ -411,16 +438,19 @@ export const data = [ ], metadata: [ { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', + accessToken: 'default-accessToken', }, - jobId: 3, - userId: 'u1', + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 1, }, ], - batched: false, + batched: true, statusCode: 200, destination: { Config: { @@ -433,6 +463,19 @@ export const data = [ isHashRequired: true, typeOfList: 'General', }, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + }, + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', }, }, { @@ -442,23 +485,24 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { - Authorization: 'Bearer abcd1234', + Authorization: 'Bearer default-accessToken', 'Content-Type': 'application/json', - 'developer-token': 'ijkl9101', }, params: { listId: '7090784486', customerId: '7693729833', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, }, body: { JSON: { - enablePartialFailure: true, operations: [ { - remove: { + create: { userIdentifiers: [ { hashedEmail: @@ -478,6 +522,24 @@ export const data = [ postalCode: '1245', }, }, + { + hashedEmail: + 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: + '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: + 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, ], }, }, @@ -489,25 +551,83 @@ export const data = [ }, files: {}, }, + ], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 2, + }, + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 3, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], + batched: true, + statusCode: 200, + destination: { + Config: { + rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', + listId: '7090784486', + customerId: '7693729833', + loginCustomerId: '', + subAccount: false, + userSchema: ['email', 'phone', 'addressInfo'], + isHashRequired: true, + typeOfList: 'General', + }, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + }, + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + }, + }, + { + batchedRequest: [ { version: '1', type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { - Authorization: 'Bearer abcd1234', + Authorization: 'Bearer default-accessToken', 'Content-Type': 'application/json', - 'developer-token': 'ijkl9101', }, params: { listId: '7090784486', customerId: '7693729833', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, }, body: { JSON: { - enablePartialFailure: true, operations: [ { create: { @@ -544,16 +664,19 @@ export const data = [ ], metadata: [ { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', + accessToken: 'default-accessToken', }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', jobId: 4, - userId: 'u1', }, ], - batched: false, + batched: true, statusCode: 200, destination: { Config: { @@ -566,6 +689,48 @@ export const data = [ isHashRequired: true, typeOfList: 'General', }, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + }, + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + }, + }, + { + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 5, + }, + ], + batched: false, + statusCode: 400, + error: 'Invalid action type in record event', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destinationId: 'default-destinationId', + destType: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + module: 'destination', + implementation: 'native', + feature: 'router', + workspaceId: 'default-workspaceId', }, }, ], diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/router/record.ts b/test/integrations/destinations/google_adwords_remarketing_lists/router/record.ts new file mode 100644 index 0000000000..743213bcc1 --- /dev/null +++ b/test/integrations/destinations/google_adwords_remarketing_lists/router/record.ts @@ -0,0 +1,158 @@ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + Config: { + rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', + listId: '7090784486', + customerId: '7693729833', + loginCustomerId: '', + subAccount: false, + userSchema: ['email', 'phone', 'addressInfo'], + isHashRequired: true, + typeOfList: 'General', + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, +}; + +export const rETLRecordRouterRequest: RouterTransformationRequest = { + input: [ + { + destination: destination, + message: { + action: 'insert', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + type: 'record', + }, + metadata: generateMetadata(2), + }, + { + destination: destination, + message: { + action: 'update', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + type: 'record', + }, + metadata: generateMetadata(4), + }, + { + destination: destination, + message: { + action: 'delete', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + type: 'record', + }, + metadata: generateMetadata(1), + }, + { + destination: destination, + message: { + action: 'lol', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + type: 'record', + }, + metadata: generateMetadata(5), + }, + { + destination: destination, + message: { + action: 'insert', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + type: 'record', + }, + metadata: generateMetadata(3), + }, + ], + destType: 'google_adwords_remarketing_lists', +}; + +module.exports = { + rETLRecordRouterRequest, +};