Skip to content

Commit

Permalink
Merge branch 'develop' into feat.GARecordEvent
Browse files Browse the repository at this point in the history
  • Loading branch information
Vikas26021999 authored Jun 17, 2024
2 parents 44d6c05 + 04d0783 commit 14ebf8a
Show file tree
Hide file tree
Showing 12 changed files with 805 additions and 31 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/verify-server-start.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Verify Server start

on:
pull_request:
types: ['opened', 'reopened', 'synchronize']

jobs:
check-health:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/[email protected]
with:
fetch-depth: 1

- name: Setup Node
uses: actions/[email protected]
with:
node-version-file: '.nvmrc'
cache: 'npm'

- name: Install Dependencies
run: npm ci

- name: Start server
run: npm run build:start &

- name: Wait for server to start
run: sleep 10 # Adjust the time as necessary for your server to start

- name: Check server health
run: |
curl --fail http://localhost:9090/health || exit 1
38 changes: 22 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@
"@ndhoule/extend": "^2.0.0",
"@pyroscope/nodejs": "^0.2.9",
"@rudderstack/integrations-lib": "^0.2.8",
"@rudderstack/json-template-engine": "^0.11.0",
"@rudderstack/workflow-engine": "^0.8.0",
"@rudderstack/json-template-engine": "^0.13.2",
"@rudderstack/workflow-engine": "^0.8.2",
"@shopify/jest-koa-mocks": "^5.1.1",
"ajv": "^8.12.0",
"ajv-draft-04": "^1.0.0",
Expand All @@ -93,6 +93,7 @@
"koa": "^2.14.1",
"koa-bodyparser": "^4.4.0",
"koa2-swagger-ui": "^5.7.0",
"libphonenumber-js": "^1.11.1",
"lodash": "^4.17.21",
"match-json": "^1.3.5",
"md5": "^2.3.0",
Expand Down
4 changes: 4 additions & 0 deletions src/v0/destinations/campaign_manager/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ const ConfigCategories = {
type: 'track',
name: 'CampaignManagerTrackConfig',
},
ENHANCED_CONVERSION: {
type: 'track',
name: 'CampaignManagerEnhancedConversionConfig',
},
};

const MAX_BATCH_CONVERSATIONS_SIZE = 1000;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
[
{
"destKey": "hashedEmail",
"sourceKeys": "emailOnly",
"sourceFromGenericMap": true
},
{
"destKey": "hashedPhoneNumber",
"sourceKeys": "phone",
"sourceFromGenericMap": true
},
{
"destKey": "addressInfo.hashedFirstName",
"sourceKeys": "firstName",
"sourceFromGenericMap": true
},
{
"destKey": "addressInfo.hashedLastName",
"sourceKeys": "lastName",
"sourceFromGenericMap": true
},
{
"destKey": "addressInfo.hashedStreetAddress",
"sourceKeys": "street",
"sourceFromGenericMap": true
},
{
"destKey": "addressInfo.city",
"sourceKeys": [
"traits.city",
"traits.address.city",
"context.traits.city",
"context.traits.address.city"
]
},
{
"destKey": "addressInfo.state",
"sourceKeys": [
"traits.state",
"traits.address.state",
"context.traits.state",
"context.traits.address.state"
]
},
{
"destKey": "addressInfo.countryCode",
"sourceKeys": [
"traits.country",
"traits.address.country",
"context.traits.country",
"context.traits.address.country"
]
},
{
"destKey": "addressInfo.postalCode",
"sourceKeys": "zipcode",
"sourceFromGenericMap": true
}
]
15 changes: 14 additions & 1 deletion src/v0/destinations/campaign_manager/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const {
handleRtTfSingleEventError,
getAccessToken,
} = require('../../util');
const { CommonUtils } = require('../../../util/common');

const {
ConfigCategories,
Expand All @@ -22,7 +23,7 @@ const {
MAX_BATCH_CONVERSATIONS_SIZE,
} = require('./config');

const { convertToMicroseconds } = require('./util');
const { convertToMicroseconds, prepareUserIdentifiers } = require('./util');
const { JSON_MIME_TYPE } = require('../../util/constant');

function isEmptyObject(obj) {
Expand Down Expand Up @@ -105,6 +106,18 @@ function processTrack(message, metadata, destination) {
}
}

if (
destination.Config.enableEnhancedConversions &&
message.properties.requestType === 'batchupdate'
) {
const userIdentifiers = CommonUtils.toArray(
prepareUserIdentifiers(message, destination.Config.isHashingRequired ?? true),
);
if (userIdentifiers.length > 0) {
requestJson.userIdentifiers = userIdentifiers;
}
}

const endpointUrl = prepareUrl(message, destination);
return buildResponse(
requestJson,
Expand Down
106 changes: 106 additions & 0 deletions src/v0/destinations/campaign_manager/util.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
const { parsePhoneNumber } = require('libphonenumber-js');
const sha256 = require('sha256');
const { InstrumentationError } = require('@rudderstack/integrations-lib');
const {
constructPayload,
isDefinedAndNotNull,
removeUndefinedAndNullValues,
isEmptyObject,
} = require('../../util');
const { ConfigCategories, mappingConfig } = require('./config');

function convertToMicroseconds(input) {
const timestamp = Date.parse(input);

Expand Down Expand Up @@ -28,6 +39,101 @@ function convertToMicroseconds(input) {
return timestamp;
}

const normalizeEmail = (email) => {
const domains = ['@gmail.com', '@googlemail.com'];

const matchingDomain = domains.find((domain) => email.endsWith(domain));

if (matchingDomain) {
const localPart = email.split('@')[0].replace(/\./g, '');
return `${localPart}${matchingDomain}`;
}

return email;
};

const normalizePhone = (phone, countryCode) => {
const phoneNumberObject = parsePhoneNumber(phone, countryCode);
if (phoneNumberObject && phoneNumberObject.isValid()) {
return phoneNumberObject.format('E.164');
}
throw new InstrumentationError('Invalid phone number');
};

// ref:- https://developers.google.com/doubleclick-advertisers/guides/conversions_ec#hashing
const normalizeAndHash = (key, value, options) => {
if (!isDefinedAndNotNull(value)) return value;

let normalizedValue;
const trimmedValue = value.trim().toLowerCase();
switch (key) {
case 'hashedEmail':
normalizedValue = normalizeEmail(trimmedValue);
break;
case 'hashedPhoneNumber':
normalizedValue = normalizePhone(trimmedValue, options.countryCode);
break;
case 'hashedFirstName':
case 'hashedLastName':
case 'hashedStreetAddress':
normalizedValue = trimmedValue;
break;
default:
return value;
}
return sha256(normalizedValue);
};

const prepareUserIdentifiers = (message, isHashingRequired) => {
const payload = constructPayload(
message,
mappingConfig[ConfigCategories.ENHANCED_CONVERSION.name],
);

if (isHashingRequired) {
payload.hashedEmail = normalizeAndHash('hashedEmail', payload.hashedEmail);
payload.hashedPhoneNumber = normalizeAndHash('hashedPhoneNumber', payload.hashedPhoneNumber, {
options: payload.addressInfo?.countryCode,
});

if (!isEmptyObject(payload.addressInfo)) {
payload.addressInfo.hashedFirstName = normalizeAndHash(
'hashedFirstName',
payload.addressInfo.hashedFirstName,
);

payload.addressInfo.hashedLastName = normalizeAndHash(
'hashedLastName',
payload.addressInfo.hashedLastName,
);

payload.addressInfo.hashedStreetAddress = normalizeAndHash(
'hashedStreetAddress',
payload.addressInfo.hashedStreetAddress,
);
}
}

const userIdentifiers = [];
if (isDefinedAndNotNull(payload.hashedEmail)) {
userIdentifiers.push({ hashedEmail: payload.hashedEmail });
}
if (isDefinedAndNotNull(payload.hashedPhoneNumber)) {
userIdentifiers.push({ hashedPhoneNumber: payload.hashedPhoneNumber });
}

payload.addressInfo = removeUndefinedAndNullValues(payload.addressInfo);
if (!isEmptyObject(payload.addressInfo)) {
userIdentifiers.push({ addressInfo: payload.addressInfo });
}

return userIdentifiers;
};

module.exports = {
convertToMicroseconds,
normalizeEmail,
normalizePhone,
normalizeAndHash,
prepareUserIdentifiers,
};
Loading

0 comments on commit 14ebf8a

Please sign in to comment.