Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:rudderlabs/rudder-transformer in…
Browse files Browse the repository at this point in the history
…to fix.version-dep-false-positives
  • Loading branch information
Sai Sankeerth committed Feb 28, 2024
2 parents 380a2e2 + 0486049 commit 8293e2b
Show file tree
Hide file tree
Showing 14 changed files with 954 additions and 210 deletions.
1 change: 0 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ See the project's [README](README.md) for further information about working in t
- Include instructions on how to test your changes.
3. Your branch may be merged once all configured checks pass, including:
- A review from appropriate maintainers
4. Along with the PR in transformer raise a PR against [config-generator][config-generator] with the configurations.

## Committing

Expand Down
2 changes: 1 addition & 1 deletion src/util/prometheus.js
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ class Prometheus {
name: 'shopify_anon_id_resolve',
help: 'shopify_anon_id_resolve',
type: 'counter',
labelNames: ['method', 'writeKey', 'shopifyTopic'],
labelNames: ['method', 'writeKey', 'shopifyTopic', 'source'],
},
{
name: 'shopify_redis_calls',
Expand Down
5 changes: 4 additions & 1 deletion src/v0/destinations/hs/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const {
getHashFromArray,
getDestinationExternalIDInfoForRetl,
getValueFromMessage,
isNull,
} = require('../../util');
const {
CONTACT_PROPERTY_MAP_ENDPOINT,
Expand Down Expand Up @@ -223,7 +224,9 @@ const getTransformedJSON = async (message, destination, propertyMap) => {
// lowercase and replace ' ' & '.' with '_'
const hsSupportedKey = formatKey(traitsKey);
if (!rawPayload[traitsKey] && propertyMap[hsSupportedKey]) {
let propValue = traits[traitsKey];
// HS accepts empty string to remove the property from contact
// https://community.hubspot.com/t5/APIs-Integrations/Clearing-values-of-custom-properties-in-Hubspot-contact-using/m-p/409156
let propValue = isNull(traits[traitsKey]) ? '' : traits[traitsKey];
if (propertyMap[hsSupportedKey] === 'date') {
propValue = getUTCMidnightTimeStampValue(propValue);
}
Expand Down
1 change: 1 addition & 0 deletions src/v0/destinations/sfmc/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const ENDPOINTS = {
GET_TOKEN: `auth.marketingcloudapis.com/v2/token`,
CONTACTS: `rest.marketingcloudapis.com/contacts/v1/contacts`,
INSERT_CONTACTS: `rest.marketingcloudapis.com/hub/v1/dataevents/key:`,
EVENT: "rest.marketingcloudapis.com/interaction/v1/events",
};

const CONFIG_CATEGORIES = {
Expand Down
35 changes: 33 additions & 2 deletions src/v0/destinations/sfmc/transform.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-param-reassign */
/* eslint-disable no-nested-ternary */
const {
NetworkError,
Expand Down Expand Up @@ -188,6 +189,26 @@ const responseBuilderForInsertData = (
return response;
};

// DOC : https://developer.salesforce.com/docs/marketing/marketing-cloud/references/mc_rest_interaction/postEvent.html

const responseBuilderForMessageEvent = (message, subDomain, authToken, hashMapEventDefinition) => {
const contactKey = message.properties.contactId;
delete message.properties.contactId;
const response = defaultRequestConfig();
response.method = defaultPostRequestConfig.requestMethod;
response.endpoint = `https://${subDomain}.${ENDPOINTS.EVENT}`;
response.headers = {
'Content-Type': JSON_MIME_TYPE,
Authorization: `Bearer ${authToken}`,
};
response.body.JSON = {
ContactKey: contactKey,
EventDefinitionKey: hashMapEventDefinition[message.event.toLowerCase()],
Data: { ...message.properties },
};
return response;
};

const responseBuilderSimple = async (message, category, destination) => {
const {
clientId,
Expand All @@ -198,6 +219,7 @@ const responseBuilderSimple = async (message, category, destination) => {
eventToExternalKey,
eventToPrimaryKey,
eventToUUID,
eventToDefinitionMapping,
} = destination.Config;
// map from an event name to an external key of a data extension.
const hashMapExternalKey = getHashFromArray(eventToExternalKey, 'from', 'to');
Expand All @@ -207,6 +229,8 @@ const responseBuilderSimple = async (message, category, destination) => {
const hashMapUUID = getHashFromArray(eventToUUID, 'event', 'uuid');
// token needed for authorization for subsequent calls
const authToken = await getToken(clientId, clientSecret, subDomain);
// map from an event name to an event definition key.
const hashMapEventDefinition = getHashFromArray(eventToDefinitionMapping, 'from', 'to');
// if createOrUpdateContacts is true identify calls for create and update of contacts will not occur.
if (category.type === 'identify' && !createOrUpdateContacts) {
// first call to identify the contact
Expand Down Expand Up @@ -240,10 +264,12 @@ const responseBuilderSimple = async (message, category, destination) => {
if (typeof message.event !== 'string') {
throw new ConfigurationError('Event name must be a string');
}
if (hashMapEventDefinition[message.event.toLowerCase()]) {
return responseBuilderForMessageEvent(message, subDomain, authToken, hashMapEventDefinition);
}
if (!isDefinedAndNotNull(hashMapExternalKey[message.event.toLowerCase()])) {
throw new ConfigurationError('Event not mapped for this track call');
}

return responseBuilderForInsertData(
message,
hashMapExternalKey[message.event.toLowerCase()],
Expand Down Expand Up @@ -293,4 +319,9 @@ const processRouterDest = async (inputs, reqMetadata) => {
return respList;
};

module.exports = { process, processRouterDest, responseBuilderSimple };
module.exports = {
process,
processRouterDest,
responseBuilderSimple,
responseBuilderForMessageEvent,
};
42 changes: 41 additions & 1 deletion src/v0/destinations/sfmc/transform.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const { ConfigurationError } = require('@rudderstack/integrations-lib');
const axios = require('axios');
const MockAxiosAdapter = require('axios-mock-adapter');
const { responseBuilderSimple } = require('./transform');
const { responseBuilderSimple, responseBuilderForMessageEvent } = require('./transform');
beforeAll(() => {
const mock = new MockAxiosAdapter(axios);
mock
Expand Down Expand Up @@ -122,4 +122,44 @@ describe('responseBuilderSimple', () => {
expect(response).toHaveProperty('body.JSON');
expect(response).toHaveProperty('headers');
});

it('should build response object with correct details for message event', () => {
const message = {
userId: 'u123',
event: 'testEvent',
properties: {
contactId: '12345',
prop1: 'value1',
prop2: 'value2',
},
};
const subDomain = 'subdomain';
const authToken = 'token';
const hashMapEventDefinition = {
testevent: 'eventDefinitionKey',
};

const response = responseBuilderForMessageEvent(
message,
subDomain,
authToken,
hashMapEventDefinition,
);
expect(response.method).toBe('POST');
expect(response.endpoint).toBe(
'https://subdomain.rest.marketingcloudapis.com/interaction/v1/events',
);
expect(response.headers).toEqual({
'Content-Type': 'application/json',
Authorization: 'Bearer token',
});
expect(response.body.JSON).toEqual({
ContactKey: '12345',
EventDefinitionKey: 'eventDefinitionKey',
Data: {
prop1: 'value1',
prop2: 'value2',
},
});
});
});
2 changes: 2 additions & 0 deletions src/v0/util/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const removeUndefinedAndNullAndEmptyValues = (obj) =>
lodash.pickBy(obj, isDefinedAndNotNullAndNotEmpty);
const isBlank = (value) => lodash.isEmpty(lodash.toString(value));
const flattenMap = (collection) => lodash.flatMap(collection, (x) => x);
const isNull = (x) => lodash.isNull(x);
// ========================================================================
// GENERIC UTLITY
// ========================================================================
Expand Down Expand Up @@ -2266,6 +2267,7 @@ module.exports = {
isDefinedAndNotNullAndNotEmpty,
isEmpty,
isNotEmpty,
isNull,
isEmptyObject,
isHttpStatusRetryable,
isHttpStatusSuccess,
Expand Down
104 changes: 104 additions & 0 deletions test/integrations/destinations/hs/processor/data.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,45 @@
import { Destination } from '../../../../../src/types';
import { generateMetadata, generateSimplifiedIdentifyPayload } from '../../../testUtils';

const commonOutputHeaders = {
'Content-Type': 'application/json',
Authorization: 'Bearer dummy-access-token',
};

const destination: Destination = {
Config: {
authorizationType: 'newPrivateAppApi',
accessToken: 'dummy-access-token',
hubID: 'dummy-hubId',
apiKey: 'dummy-apikey',
apiVersion: 'newApi',
lookupField: 'email',
hubspotEvents: [],
eventFilteringOption: 'disable',
blacklistedEvents: [
{
eventName: '',
},
],
whitelistedEvents: [
{
eventName: '',
},
],
},
Enabled: true,
ID: '123',
Name: 'hs',
DestinationDefinition: {
ID: '123',
Name: 'hs',
DisplayName: 'Hubspot',
Config: {},
},
WorkspaceID: '123',
Transformations: [],
};

export const data = [
{
name: 'hs',
Expand Down Expand Up @@ -5269,4 +5311,66 @@ export const data = [
},
},
},
{
name: 'hs',
description: 'Test coversion of null to string values',
feature: 'processor',
module: 'destination',
version: 'v0',
input: {
request: {
body: [
{
destination,
message: generateSimplifiedIdentifyPayload({
userId: '12345',
context: {
traits: {
email: '[email protected]',
firstname: null,
gender: '',
lookupField: 'email',
},
},
}),
metadata: generateMetadata(1),
},
],
},
},
output: {
response: {
status: 200,
body: [
{
output: {
version: '1',
type: 'REST',
userId: '',
method: 'POST',
endpoint: 'https://api.hubapi.com/crm/v3/objects/contacts',
files: {},
headers: commonOutputHeaders,
operation: 'createContacts',
params: {},
body: {
FORM: {},
JSON: {
properties: {
email: '[email protected]',
firstname: '',
gender: '',
},
},
JSON_ARRAY: {},
XML: {},
},
},
statusCode: 200,
metadata: generateMetadata(1),
},
],
},
},
},
];
Loading

0 comments on commit 8293e2b

Please sign in to comment.