Skip to content

Commit

Permalink
Merge branch 'develop' into feat.v1-networkHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
aashishmalik authored Nov 30, 2023
2 parents e2a298a + 0e7adc6 commit 2e5a137
Show file tree
Hide file tree
Showing 24 changed files with 1,719 additions and 1,469 deletions.
3 changes: 3 additions & 0 deletions src/v0/destinations/facebook_pixel/config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const { getMappingConfig } = require('../../util');

const VERSION = 'v18.0';

const CONFIG_CATEGORIES = {
USERDATA: {
standard: false,
Expand Down Expand Up @@ -106,6 +108,7 @@ const STANDARD_ECOMM_EVENTS_TYPE = [
];

module.exports = {
VERSION,
CONFIG_CATEGORIES,
MAPPING_CONFIG,
ACTION_SOURCES_VALUES,
Expand Down
3 changes: 2 additions & 1 deletion src/v0/destinations/facebook_pixel/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const moment = require('moment');
const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib');
const stats = require('../../../util/stats');
const {
VERSION,
CONFIG_CATEGORIES,
MAPPING_CONFIG,
FB_PIXEL_DEFAULT_EXCLUSION,
Expand Down Expand Up @@ -65,7 +66,7 @@ const responseBuilderSimple = (message, category, destination) => {
} = Config;
const integrationsObj = getIntegrationsObj(message, 'fb_pixel');

const endpoint = `https://graph.facebook.com/v17.0/${pixelId}/events?access_token=${accessToken}`;
const endpoint = `https://graph.facebook.com/${VERSION}/${pixelId}/events?access_token=${accessToken}`;

const userData = fetchUserData(
message,
Expand Down
3 changes: 3 additions & 0 deletions src/v0/destinations/fb/config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const fs = require('fs');
const path = require('path');

const VERSION = 'v18.0';

const getPath = (file) => path.resolve(__dirname, file);

const baseMapping = JSON.parse(fs.readFileSync(getPath('./data/FbAppBasicMapping.json')));
Expand All @@ -20,6 +22,7 @@ const eventPropToTypeMapping = JSON.parse(
);

module.exports = {
VERSION,
baseMapping,
eventNameMapping,
eventPropsMapping,
Expand Down
3 changes: 2 additions & 1 deletion src/v0/destinations/fb/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const {
} = require('../../util');

const {
VERSION,
baseMapping,
eventNameMapping,
eventPropsMapping,
Expand Down Expand Up @@ -250,7 +251,7 @@ function responseBuilderSimple(message, payload, destination) {

// "https://graph.facebook.com/v13.0/644748472345539/activities"

const endpoint = `https://graph.facebook.com/v17.0/${appID}/activities`;
const endpoint = `https://graph.facebook.com/${VERSION}/${appID}/activities`;

const response = defaultRequestConfig();
response.endpoint = endpoint;
Expand Down
4 changes: 2 additions & 2 deletions src/v0/destinations/fb_custom_audience/config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const BASE_URL = 'https://graph.facebook.com/v17.0';
const BASE_URL = 'https://graph.facebook.com/v18.0';

function getEndPoint(audienceId) {
return `${BASE_URL}/${audienceId}/users`;
Expand Down Expand Up @@ -93,7 +93,7 @@ const subTypeFields = [
// const MAX_USER_COUNT = 500; (using from destination definition)
const USER_ADD = 'add';
const USER_DELETE = 'remove';
/* No official Documentation is available for this but using trial
/* No official Documentation is available for this but using trial
and error method we found that 65000 bytes is the maximum payload allowed size but we are 60000 just to be sure batching is done properly
*/
const maxPayloadSize = 60000; // bytes
Expand Down
2 changes: 1 addition & 1 deletion src/v0/destinations/moengage/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ const processEvent = (message, destination) => {
response = responseBuilderSimple(message, category, destination);
// only if device information is present device info will be added/updated
// with an identify call otherwise only user info will be added/updated
if (message.context.device && message.context.device.type && message.context.device.token) {
if (message?.context?.device?.type && message?.context?.device?.token) {
// build the response
response = [
// user api payload (output for identify)
Expand Down
23 changes: 18 additions & 5 deletions src/v0/destinations/sfmc/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ const {
NetworkError,
ConfigurationError,
InstrumentationError,
isDefinedAndNotNull,
isEmpty,
} = require('@rudderstack/integrations-lib');
const myAxios = require('../../../util/myAxios');
const { EventType } = require('../../../constants');
Expand All @@ -17,7 +19,6 @@ const {
flattenJson,
toTitleCase,
getHashFromArray,
isEmpty,
simpleProcessRouterDest,
} = require('../../util');
const {
Expand Down Expand Up @@ -221,10 +222,22 @@ const responseBuilderSimple = async (message, category, destination) => {
}

if (category.type === 'identify' && createOrUpdateContacts) {
throw new ConfigurationError('Creating or updating contacts is disabled');
throw new ConfigurationError(
'Creating or updating contacts is disabled. To enable this feature set "Do Not Create or Update Contacts" to false',
);
}

if (category.type === 'track' && hashMapExternalKey[message.event.toLowerCase()]) {
if (category.type === 'track') {
if (isEmpty(message.event)) {
throw new ConfigurationError('Event name is required for track events');
}
if (typeof message.event !== 'string') {
throw new ConfigurationError('Event name must be a string');
}
if (!isDefinedAndNotNull(hashMapExternalKey[message.event.toLowerCase()])) {
throw new ConfigurationError('Event not mapped for this track call');
}

return responseBuilderForInsertData(
message,
hashMapExternalKey[message.event.toLowerCase()],
Expand All @@ -237,7 +250,7 @@ const responseBuilderSimple = async (message, category, destination) => {
);
}

throw new ConfigurationError('Event not mapped for this track call');
throw new ConfigurationError(`Event type '${category.type}' not supported`);
};

const processEvent = async (message, destination) => {
Expand Down Expand Up @@ -274,4 +287,4 @@ const processRouterDest = async (inputs, reqMetadata) => {
return respList;
};

module.exports = { process, processRouterDest };
module.exports = { process, processRouterDest, responseBuilderSimple };
125 changes: 125 additions & 0 deletions src/v0/destinations/sfmc/transform.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
const { ConfigurationError } = require('@rudderstack/integrations-lib');
const axios = require('axios');
const MockAxiosAdapter = require('axios-mock-adapter');
const { responseBuilderSimple } = require('./transform');
beforeAll(() => {
const mock = new MockAxiosAdapter(axios);
mock
.onPost('https://yourSubDomain.auth.marketingcloudapis.com/v2/token')
.reply(200, '{"access_token":"yourAuthToken"}');
});

describe('responseBuilderSimple', () => {
const destination = {
Config: {
clientId: 'yourClientId',
clientSecret: 'yourClientSecret',
subDomain: 'yourSubDomain',
createOrUpdateContacts: false,
externalKey: 'yourExternalKey',
eventToExternalKey: [{ from: 'purchase', to: 'purchaseKey' }],
eventToPrimaryKey: [{ from: 'purchase', to: 'primaryKey' }],
eventToUUID: [{ event: 'purchase', uuid: true }],
},
};
it('should return an array of two payloads for identify calls when createOrUpdateContacts is false', async () => {
const message = {
type: 'identify',
userId: '12345',
};

const category = {
type: 'identify',
name: 'Identify',
};

const response = await responseBuilderSimple(message, category, destination);

expect(response).toHaveLength(2);
expect(response[0]).toHaveProperty('endpoint');
expect(response[0]).toHaveProperty('method');
expect(response[0]).toHaveProperty('body.JSON');
expect(response[0]).toHaveProperty('headers');
expect(response[1]).toHaveProperty('endpoint');
expect(response[1]).toHaveProperty('method');
expect(response[1]).toHaveProperty('body.JSON');
expect(response[1]).toHaveProperty('headers');
});

// Throws an error when event name is not provided for track calls
it('should throw an error when event name is not provided for track calls', async () => {
const message = {
type: 'track',
};

const category = {
type: 'track',
name: 'Track',
};

try {
await responseBuilderSimple(message, category, destination);
} catch (e) {
expect(e).toBeInstanceOf(ConfigurationError);
expect(e.message).toBe('Event name is required for track events');
}
});

// Throws an error when event is not mapped for track calls
it('should throw an error when event is not mapped for track calls', async () => {
const message = {
type: 'track',
event: 'unmappedEvent',
};

const category = {
type: 'track',
name: 'Track',
};
try {
await responseBuilderSimple(message, category, destination);
} catch (e) {
expect(e).toBeInstanceOf(ConfigurationError);
expect(e.message).toBe('Event not mapped for this track call');
}
});

// Throws an error when event type is not supported
it('should throw an error when event type is not supported', async () => {
const message = {
type: 'unsupported',
};

const category = {
type: 'unsupported',
name: 'Unsupported',
};

try {
await responseBuilderSimple(message, category, destination);
} catch (e) {
expect(e).toBeInstanceOf(ConfigurationError);
expect(e.message).toBe("Event type 'unsupported' not supported");
}
});

// Returns a payload for track calls when event is mapped and event name is a string
it('should return a payload for track calls when event is mapped and event name is a string', async () => {
const message = {
type: 'track',
event: 'purchase',
userId: '12345',
};

const category = {
type: 'track',
name: 'Track',
};

const response = await responseBuilderSimple(message, category, destination);
expect(response).toHaveProperty('endpoint');
expect(response).toHaveProperty('method');
expect(response).toHaveProperty('body.JSON');
expect(response).toHaveProperty('headers');
});
});
6 changes: 1 addition & 5 deletions src/v0/util/errorTypes/filteredEventsError.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
const tags = require('../tags');
const { BaseError } = require('./base');
const { HTTP_STATUS_CODES } = require('../constant');

class FilteredEventsError extends BaseError {
constructor(message, statusCode = HTTP_STATUS_CODES.FILTER_EVENTS) {
const finalStatTags = {
[tags.TAG_NAMES.ERROR_CATEGORY]: tags.ERROR_CATEGORIES.TRANSFORMATION,
};
super(message, statusCode, finalStatTags);
super(message, statusCode);
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/v0/util/facebookUtils/networkHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ const errorDetailsMap = {
.setMessage('There have been too many calls to this ad-account.')
.build(),
},
200: {
default: new ErrorDetailsExtractorBuilder().setStatus(403).setMessageField('message').build(),
},
};

const getErrorDetailsFromErrorMap = (error) => {
Expand Down
2 changes: 1 addition & 1 deletion test/__tests__/data/sfmc_output.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[
{
"error": "Creating or updating contacts is disabled"
"error": "Creating or updating contacts is disabled. To enable this feature set \"Do Not Create or Update Contacts\" to false"
},
[
{
Expand Down
2 changes: 1 addition & 1 deletion test/__tests__/data/sfmc_router_output.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
},
"batched": false,
"statusCode": 400,
"error": "Creating or updating contacts is disabled",
"error": "Creating or updating contacts is disabled. To enable this feature set \"Do Not Create or Update Contacts\" to false",
"statTags": {
"errorCategory": "dataValidation",
"errorType": "configuration"
Expand Down
Loading

0 comments on commit 2e5a137

Please sign in to comment.