Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: garl record event support #3403

Merged
merged 12 commits into from
Jun 18, 2024
158 changes: 44 additions & 114 deletions src/v0/destinations/google_adwords_remarketing_lists/transform.js
Original file line number Diff line number Diff line change
@@ -1,87 +1,32 @@
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, populateIdentifiers, responseBuilder } = require('./transformV2');
Vikas26021999 marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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');
function extraKeysPresent(dictionary, keyList) {
// eslint-disable-next-line no-restricted-syntax
for (const key in dictionary) {
if (!keyList.includes(key)) {
return true;

Check warning on line 24 in src/v0/destinations/google_adwords_remarketing_lists/transform.js

View check run for this annotation

Codecov / codecov/patch

src/v0/destinations/google_adwords_remarketing_lists/transform.js#L24

Added line #L24 was not covered by tests
}
}
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;
};
return false;
}

/**
* 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
Expand All @@ -91,48 +36,6 @@
* @returns
Vikas26021999 marked this conversation as resolved.
Show resolved Hide resolved
*/

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.
Expand Down Expand Up @@ -218,9 +121,14 @@
);
}

const accessToken = getAccessToken(metadata, 'access_token');
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;
}
Expand All @@ -231,7 +139,29 @@
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');

Check warning on line 149 in src/v0/destinations/google_adwords_remarketing_lists/transform.js

View check run for this annotation

Codecov / codecov/patch

src/v0/destinations/google_adwords_remarketing_lists/transform.js#L149

Added line #L149 was not covered by tests
}

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;
};

krishna2020 marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
Loading
Loading