diff --git a/src/v0/destinations/intercom/config.js b/src/v0/destinations/intercom/config.js new file mode 100644 index 0000000000..ae29eebc1e --- /dev/null +++ b/src/v0/destinations/intercom/config.js @@ -0,0 +1,53 @@ +const { getMappingConfig } = require('../../util'); + +const BASE_ENDPOINT = 'https://api.intercom.io'; + +// track events | Track +const TRACK_ENDPOINT = `${BASE_ENDPOINT}/events`; +// Create, Update a user with a company | Identify +const IDENTIFY_ENDPOINT = `${BASE_ENDPOINT}/users`; +// create, update, delete a company | Group +const GROUP_ENDPOINT = `${BASE_ENDPOINT}/companies`; + +const ConfigCategory = { + TRACK: { + endpoint: TRACK_ENDPOINT, + name: 'INTERCOMTrackConfig', + }, + IDENTIFY: { + endpoint: IDENTIFY_ENDPOINT, + name: 'INTERCOMIdentifyConfig', + }, + GROUP: { + endpoint: GROUP_ENDPOINT, + name: 'INTERCOMGroupConfig', + }, +}; + +const MappingConfig = getMappingConfig(ConfigCategory, __dirname); + +const ReservedTraitsProperties = [ + 'userId', + 'email', + 'phone', + 'name', + 'createdAt', + 'firstName', + 'lastName', + 'firstname', + 'lastname', + 'company', +]; + +const ReservedCompanyProperties = ['id', 'name', 'industry']; + +// ref:- https://developers.intercom.com/intercom-api-reference/v1.4/reference/event-metadata-types +const MetadataTypes = { richLink: ['url', 'value'], monetaryAmount: ['amount', 'currency'] }; + +module.exports = { + ConfigCategory, + MappingConfig, + ReservedCompanyProperties, + ReservedTraitsProperties, + MetadataTypes, +}; diff --git a/src/v0/destinations/intercom/data/INTERCOMGroupConfig.json b/src/v0/destinations/intercom/data/INTERCOMGroupConfig.json new file mode 100644 index 0000000000..6857c4e104 --- /dev/null +++ b/src/v0/destinations/intercom/data/INTERCOMGroupConfig.json @@ -0,0 +1,53 @@ +[ + { + "destKey": "company_id", + "sourceKeys": "groupId", + "required": true + }, + { + "destKey": "name", + "sourceKeys": "name", + "sourceFromGenericMap": true, + "required": false + }, + { + "destKey": "plan", + "sourceKeys": ["traits.plan", "context.traits.plan"], + "required": false + }, + { + "destKey": "size", + "sourceKeys": ["traits.size", "context.traits.size"], + "metadata": { + "type": "toNumber" + }, + "required": false + }, + { + "destKey": "website", + "sourceKeys": "website", + "sourceFromGenericMap": true, + "required": false + }, + { + "destKey": "industry", + "sourceKeys": ["traits.industry", "context.traits.industry"], + "required": false + }, + { + "destKey": "monthly_spend", + "sourceKeys": ["traits.monthlySpend", "context.traits.monthlySpend"], + "metadata": { + "type": "toNumber" + }, + "required": false + }, + { + "destKey": "remote_created_at", + "sourceKeys": ["traits.remoteCreatedAt", "context.traits.remoteCreatedAt"], + "metadata": { + "type": "toNumber" + }, + "required": false + } +] diff --git a/src/v0/destinations/intercom/data/INTERCOMIdentifyConfig.json b/src/v0/destinations/intercom/data/INTERCOMIdentifyConfig.json new file mode 100644 index 0000000000..726a741161 --- /dev/null +++ b/src/v0/destinations/intercom/data/INTERCOMIdentifyConfig.json @@ -0,0 +1,46 @@ +[ + { + "destKey": "user_id", + "sourceKeys": [ + "userId", + "traits.userId", + "traits.id", + "context.traits.userId", + "context.traits.id" + ], + "required": false + }, + { + "destKey": "email", + "sourceKeys": ["traits.email", "context.traits.email"], + "required": false + }, + { + "destKey": "phone", + "sourceKeys": ["traits.phone", "context.traits.phone"], + "required": false + }, + { + "destKey": "name", + "sourceKeys": ["traits.name", "context.traits.name"], + "required": false + }, + { + "destKey": "signed_up_at", + "sourceKeys": ["traits.createdAt", "context.traits.createdAt"], + "required": false, + "metadata": { + "type": "secondTimestamp" + } + }, + { + "destKey": "last_seen_user_agent", + "sourceKeys": "context.userAgent", + "required": false + }, + { + "destKey": "custom_attributes", + "sourceKeys": ["traits", "context.traits"], + "required": false + } +] diff --git a/src/v0/destinations/intercom/data/INTERCOMTrackConfig.json b/src/v0/destinations/intercom/data/INTERCOMTrackConfig.json new file mode 100644 index 0000000000..f33c9a8a98 --- /dev/null +++ b/src/v0/destinations/intercom/data/INTERCOMTrackConfig.json @@ -0,0 +1,36 @@ +[ + { + "destKey": "user_id", + "sourceKeys": [ + "userId", + "traits.userId", + "traits.id", + "context.traits.userId", + "context.traits.id" + ], + "required": false + }, + { + "destKey": "email", + "sourceKeys": ["traits.email", "context.traits.email"], + "required": false + }, + { + "destKey": "event_name", + "sourceKeys": "event", + "required": true + }, + { + "destKey": "created", + "sourceKeys": "timestamp", + "sourceFromGenericMap": true, + "required": true, + "metadata": { + "type": "secondTimestamp" + } + }, + { + "destKey": "metadata", + "sourceKeys": "properties" + } +] diff --git a/src/v0/destinations/intercom/transform.js b/src/v0/destinations/intercom/transform.js new file mode 100644 index 0000000000..212eaba13b --- /dev/null +++ b/src/v0/destinations/intercom/transform.js @@ -0,0 +1,252 @@ +const md5 = require('md5'); +const get = require('get-value'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { EventType, MappedToDestinationKey } = require('../../../constants'); +const { + ConfigCategory, + MappingConfig, + ReservedTraitsProperties, + ReservedCompanyProperties, +} = require('./config'); +const { + constructPayload, + removeUndefinedAndNullValues, + defaultRequestConfig, + defaultPostRequestConfig, + getFieldValueFromMessage, + addExternalIdToTraits, + simpleProcessRouterDest, + flattenJson, +} = require('../../util'); +const { separateReservedAndRestMetadata } = require('./util'); +const { JSON_MIME_TYPE } = require('../../util/constant'); + +function getCompanyAttribute(company) { + const companiesList = []; + if (company.name || company.id) { + const customAttributes = {}; + Object.keys(company).forEach((key) => { + // the key is not in ReservedCompanyProperties + if (!ReservedCompanyProperties.includes(key)) { + const val = company[key]; + if (val !== Object(val)) { + customAttributes[key] = val; + } else { + customAttributes[key] = JSON.stringify(val); + } + } + }); + + companiesList.push({ + company_id: company.id || md5(company.name), + custom_attributes: removeUndefinedAndNullValues(customAttributes), + name: company.name, + industry: company.industry, + }); + } + return companiesList; +} + +function validateIdentify(message, payload, config) { + const finalPayload = payload; + + finalPayload.update_last_request_at = + config.updateLastRequestAt !== undefined ? config.updateLastRequestAt : true; + if (payload.user_id || payload.email) { + if (payload.name === undefined || payload.name === '') { + const firstName = getFieldValueFromMessage(message, 'firstName'); + const lastName = getFieldValueFromMessage(message, 'lastName'); + if (firstName && lastName) { + finalPayload.name = `${firstName} ${lastName}`; + } else { + finalPayload.name = firstName || lastName; + } + } + + if (get(finalPayload, 'custom_attributes.company')) { + finalPayload.companies = getCompanyAttribute(finalPayload.custom_attributes.company); + } + + if (finalPayload.custom_attributes) { + ReservedTraitsProperties.forEach((trait) => { + delete finalPayload.custom_attributes[trait]; + }); + finalPayload.custom_attributes = flattenJson(finalPayload.custom_attributes); + } + + return finalPayload; + } + throw new InstrumentationError('Either of `email` or `userId` is required for Identify call'); +} + +function validateTrack(payload) { + if (!payload.user_id && !payload.email) { + throw new InstrumentationError('Either of `email` or `userId` is required for Track call'); + } + // pass only string, number, boolean properties + if (payload.metadata) { + // reserved metadata contains JSON objects that does not requires flattening + const { reservedMetadata, restMetadata } = separateReservedAndRestMetadata(payload.metadata); + return { ...payload, metadata: { ...reservedMetadata, ...flattenJson(restMetadata) } }; + } + + return payload; +} + +const checkIfEmailOrUserIdPresent = (message, Config) => { + const { context, anonymousId } = message; + let { userId } = message; + if (Config.sendAnonymousId && !userId) { + userId = anonymousId; + } + return !!(userId || context.traits?.email); +}; + +function attachUserAndCompany(message, Config) { + const email = message.context?.traits?.email; + const { userId, anonymousId, traits, groupId } = message; + const requestBody = {}; + if (userId) { + requestBody.user_id = userId; + } + if (Config.sendAnonymousId && !userId) { + requestBody.user_id = anonymousId; + } + if (email) { + requestBody.email = email; + } + const companyObj = { + company_id: groupId, + }; + if (traits?.name) { + companyObj.name = traits.name; + } + requestBody.companies = [companyObj]; + const response = defaultRequestConfig(); + response.method = defaultPostRequestConfig.requestMethod; + response.endpoint = ConfigCategory.IDENTIFY.endpoint; + response.headers = { + 'Content-Type': JSON_MIME_TYPE, + Authorization: `Bearer ${Config.apiKey}`, + Accept: JSON_MIME_TYPE, + 'Intercom-Version': '1.4', + }; + response.body.JSON = requestBody; + return response; +} + +function buildCustomAttributes(message, payload) { + const finalPayload = payload; + const { traits } = message; + const customAttributes = {}; + const companyReservedKeys = [ + 'remoteCreatedAt', + 'monthlySpend', + 'industry', + 'website', + 'size', + 'plan', + 'name', + ]; + + if (traits) { + Object.keys(traits).forEach((key) => { + if (!companyReservedKeys.includes(key) && key !== 'userId') { + customAttributes[key] = traits[key]; + } + }); + } + + if (Object.keys(customAttributes).length > 0) { + finalPayload.custom_attributes = flattenJson(customAttributes); + } + + return finalPayload; +} + +function validateAndBuildResponse(message, payload, category, destination) { + const respList = []; + const response = defaultRequestConfig(); + response.method = defaultPostRequestConfig.requestMethod; + response.endpoint = category.endpoint; + response.headers = { + 'Content-Type': JSON_MIME_TYPE, + Authorization: `Bearer ${destination.Config.apiKey}`, + Accept: JSON_MIME_TYPE, + 'Intercom-Version': '1.4', + }; + response.userId = message.anonymousId; + const messageType = message.type.toLowerCase(); + switch (messageType) { + case EventType.IDENTIFY: + response.body.JSON = removeUndefinedAndNullValues( + validateIdentify(message, payload, destination.Config), + ); + break; + case EventType.TRACK: + response.body.JSON = removeUndefinedAndNullValues(validateTrack(payload)); + break; + case EventType.GROUP: { + response.body.JSON = removeUndefinedAndNullValues(buildCustomAttributes(message, payload)); + respList.push(response); + if (checkIfEmailOrUserIdPresent(message, destination.Config)) { + const attachUserAndCompanyResponse = attachUserAndCompany(message, destination.Config); + attachUserAndCompanyResponse.userId = message.anonymousId; + respList.push(attachUserAndCompanyResponse); + } + break; + } + default: + throw new InstrumentationError(`Message type ${messageType} not supported`); + } + + return messageType === EventType.GROUP ? respList : response; +} + +function processSingleMessage(message, destination) { + if (!message.type) { + throw new InstrumentationError('Message Type is not present. Aborting message.'); + } + const { sendAnonymousId } = destination.Config; + const messageType = message.type.toLowerCase(); + let category; + + switch (messageType) { + case EventType.IDENTIFY: + category = ConfigCategory.IDENTIFY; + break; + case EventType.TRACK: + category = ConfigCategory.TRACK; + break; + case EventType.GROUP: + category = ConfigCategory.GROUP; + break; + default: + throw new InstrumentationError(`Message type ${messageType} not supported`); + } + + // build the response and return + let payload; + if (get(message, MappedToDestinationKey)) { + addExternalIdToTraits(message); + payload = getFieldValueFromMessage(message, 'traits'); + } else { + payload = constructPayload(message, MappingConfig[category.name]); + } + if (category !== ConfigCategory.GROUP && sendAnonymousId && !payload.user_id) { + payload.user_id = message.anonymousId; + } + return validateAndBuildResponse(message, payload, category, destination); +} + +function process(event) { + const response = processSingleMessage(event.message, event.destination); + return response; +} + +const processRouterDest = async (inputs, reqMetadata) => { + const respList = await simpleProcessRouterDest(inputs, process, reqMetadata); + return respList; +}; + +module.exports = { process, processRouterDest }; diff --git a/src/v0/destinations/intercom/util.js b/src/v0/destinations/intercom/util.js new file mode 100644 index 0000000000..24a2934f7e --- /dev/null +++ b/src/v0/destinations/intercom/util.js @@ -0,0 +1,32 @@ +const { MetadataTypes } = require('./config'); + +/** + * Separates reserved metadata from rest of the metadata based on the metadata types + * ref:- https://developers.intercom.com/intercom-api-reference/v1.4/reference/event-metadata-types + * @param {*} metadata + * @returns + */ +function separateReservedAndRestMetadata(metadata) { + const reservedMetadata = {}; + const restMetadata = {}; + if (metadata) { + Object.entries(metadata).forEach(([key, value]) => { + if (value && typeof value === 'object') { + const hasMonetaryAmountKeys = MetadataTypes.monetaryAmount.every((type) => type in value); + const hasRichLinkKeys = MetadataTypes.richLink.every((type) => type in value); + if (hasMonetaryAmountKeys || hasRichLinkKeys) { + reservedMetadata[key] = value; + } else { + restMetadata[key] = value; + } + } else { + restMetadata[key] = value; + } + }); + } + + // Return the separated metadata objects + return { reservedMetadata, restMetadata }; +} + +module.exports = { separateReservedAndRestMetadata }; diff --git a/src/v0/destinations/intercom/util.test.js b/src/v0/destinations/intercom/util.test.js new file mode 100644 index 0000000000..99dbdd1f7e --- /dev/null +++ b/src/v0/destinations/intercom/util.test.js @@ -0,0 +1,176 @@ +const { separateReservedAndRestMetadata } = require('./util'); + +describe('separateReservedAndRestMetadata utility test', () => { + it('separate reserved and rest metadata', () => { + const metadata = { + property1: 1, + property2: 'test', + property3: true, + property4: { + property1: 1, + property2: 'test', + property3: { + subProp1: { + a: 'a', + b: 'b', + }, + subProp2: ['a', 'b'], + }, + }, + property5: {}, + property6: [], + property7: null, + property8: undefined, + revenue: { + amount: 1232, + currency: 'inr', + test: 123, + }, + price: { + amount: 3000, + currency: 'USD', + }, + article: { + url: 'https://example.org/ab1de.html', + value: 'the dude abides', + }, + }; + const expectedReservedMetadata = { + revenue: { + amount: 1232, + currency: 'inr', + test: 123, + }, + price: { + amount: 3000, + currency: 'USD', + }, + article: { + url: 'https://example.org/ab1de.html', + value: 'the dude abides', + }, + }; + const expectedRestMetadata = { + property1: 1, + property2: 'test', + property3: true, + property4: { + property1: 1, + property2: 'test', + property3: { + subProp1: { + a: 'a', + b: 'b', + }, + subProp2: ['a', 'b'], + }, + }, + property5: {}, + property6: [], + property7: null, + property8: undefined, + }; + const { reservedMetadata, restMetadata } = separateReservedAndRestMetadata(metadata); + + expect(expectedReservedMetadata).toEqual(reservedMetadata); + expect(expectedRestMetadata).toEqual(restMetadata); + }); + + it('reserved metadata types not present in input metadata', () => { + const metadata = { + property1: 1, + property2: 'test', + property3: true, + property4: { + property1: 1, + property2: 'test', + property3: { + subProp1: { + a: 'a', + b: 'b', + }, + subProp2: ['a', 'b'], + }, + }, + property5: {}, + property6: [], + property7: null, + property8: undefined, + }; + const expectedRestMetadata = { + property1: 1, + property2: 'test', + property3: true, + property4: { + property1: 1, + property2: 'test', + property3: { + subProp1: { + a: 'a', + b: 'b', + }, + subProp2: ['a', 'b'], + }, + }, + property5: {}, + property6: [], + property7: null, + property8: undefined, + }; + const { reservedMetadata, restMetadata } = separateReservedAndRestMetadata(metadata); + + expect({}).toEqual(reservedMetadata); + expect(expectedRestMetadata).toEqual(restMetadata); + }); + + it('metadata input contains only reserved metadata types', () => { + const metadata = { + revenue: { + amount: 1232, + currency: 'inr', + test: 123, + }, + price: { + amount: 3000, + currency: 'USD', + }, + article: { + url: 'https://example.org/ab1de.html', + value: 'the dude abides', + }, + }; + const expectedReservedMetadata = { + revenue: { + amount: 1232, + currency: 'inr', + test: 123, + }, + price: { + amount: 3000, + currency: 'USD', + }, + article: { + url: 'https://example.org/ab1de.html', + value: 'the dude abides', + }, + }; + const { reservedMetadata, restMetadata } = separateReservedAndRestMetadata(metadata); + + expect(expectedReservedMetadata).toEqual(reservedMetadata); + expect({}).toEqual(restMetadata); + }); + + it('empty metadata object', () => { + const metadata = {}; + const { reservedMetadata, restMetadata } = separateReservedAndRestMetadata(metadata); + expect({}).toEqual(reservedMetadata); + expect({}).toEqual(restMetadata); + }); + + it('null/undefined metadata', () => { + const metadata = null; + const { reservedMetadata, restMetadata } = separateReservedAndRestMetadata(metadata); + expect({}).toEqual(reservedMetadata); + expect({}).toEqual(restMetadata); + }); +}); diff --git a/test/integrations/destinations/intercom/processor/data.ts b/test/integrations/destinations/intercom/processor/data.ts index bd1b65b43e..2c562ed4e9 100644 --- a/test/integrations/destinations/intercom/processor/data.ts +++ b/test/integrations/destinations/intercom/processor/data.ts @@ -3743,4 +3743,407 @@ export const data = [ }, }, }, + { + name: 'intercom', + description: 'Test 0', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + channel: 'mobile', + context: { + app: { + build: '1.0', + name: 'Test_Example', + namespace: 'com.example.testapp', + version: '1.0', + }, + device: { + id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + manufacturer: 'Apple', + model: 'iPhone', + name: 'iPod touch (7th generation)', + type: 'iOS', + }, + library: { + name: 'test-ios-library', + version: '1.0.7', + }, + locale: 'en-US', + network: { + bluetooth: false, + carrier: 'unavailable', + cellular: false, + wifi: true, + }, + os: { + name: 'iOS', + version: '14.0', + }, + screen: { + density: 2, + height: 320, + width: 568, + }, + timezone: 'Asia/Kolkata', + traits: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + name: 'Test Name', + firstName: 'Test', + lastName: 'Name', + createdAt: '2020-09-30T19:11:00.337Z', + userId: 'test_user_id_1', + email: 'test_1@test.com', + phone: '9876543210', + key1: 'value1', + address: { + city: 'Kolkata', + state: 'West Bengal', + }, + originalArray: [ + { + nested_field: 'nested value', + tags: ['tag_1', 'tag_2', 'tag_3'], + }, + { + nested_field: 'nested value', + tags: ['tag_1'], + }, + { + nested_field: 'nested value', + }, + ], + }, + userAgent: 'unknown', + }, + event: 'Test Event 2', + integrations: { + All: true, + }, + messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', + originalTimestamp: '2020-09-30T19:11:00.337Z', + receivedAt: '2020-10-01T00:41:11.369+05:30', + request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', + sentAt: '2020-09-30T19:11:10.382Z', + timestamp: '2020-10-01T00:41:01.324+05:30', + type: 'identify', + }, + destination: { + Config: { + apiKey: 'intercomApiKey', + appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', + collectContext: false, + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.intercom.io/users', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer intercomApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', + }, + params: {}, + body: { + JSON: { + user_id: 'test_user_id_1', + email: 'test_1@test.com', + phone: '9876543210', + name: 'Test Name', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + custom_attributes: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + key1: 'value1', + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + 'originalArray[0].nested_field': 'nested value', + 'originalArray[0].tags[0]': 'tag_1', + 'originalArray[0].tags[1]': 'tag_2', + 'originalArray[0].tags[2]': 'tag_3', + 'originalArray[1].nested_field': 'nested value', + 'originalArray[1].tags[0]': 'tag_1', + 'originalArray[2].nested_field': 'nested value', + }, + update_last_request_at: true, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'intercom', + description: 'Test 1', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + channel: 'mobile', + context: { + app: { + build: '1.0', + name: 'Test_Example', + namespace: 'com.example.testapp', + version: '1.0', + }, + device: { + id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + manufacturer: 'Apple', + model: 'iPhone', + name: 'iPod touch (7th generation)', + type: 'iOS', + }, + library: { + name: 'test-ios-library', + version: '1.0.7', + }, + locale: 'en-US', + network: { + bluetooth: false, + carrier: 'unavailable', + cellular: false, + wifi: true, + }, + os: { + name: 'iOS', + version: '14.0', + }, + screen: { + density: 2, + height: 320, + width: 568, + }, + timezone: 'Asia/Kolkata', + traits: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + firstName: 'Test', + lastName: 'Name', + createdAt: '2020-09-30T19:11:00.337Z', + email: 'test_1@test.com', + phone: '9876543210', + key1: 'value1', + }, + userAgent: 'unknown', + }, + event: 'Test Event 2', + integrations: { + All: true, + }, + messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', + originalTimestamp: '2020-09-30T19:11:00.337Z', + receivedAt: '2020-10-01T00:41:11.369+05:30', + request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', + sentAt: '2020-09-30T19:11:10.382Z', + timestamp: '2020-10-01T00:41:01.324+05:30', + type: 'identify', + }, + destination: { + Config: { + apiKey: 'intercomApiKey', + appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', + collectContext: false, + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.intercom.io/users', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer intercomApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', + }, + params: {}, + body: { + JSON: { + email: 'test_1@test.com', + phone: '9876543210', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + custom_attributes: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + key1: 'value1', + }, + update_last_request_at: true, + name: 'Test Name', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'intercom', + description: 'Test 2', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + channel: 'mobile', + context: { + app: { + build: '1.0', + name: 'Test_Example', + namespace: 'com.example.testapp', + version: '1.0', + }, + device: { + id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + manufacturer: 'Apple', + model: 'iPhone', + name: 'iPod touch (7th generation)', + type: 'iOS', + }, + library: { + name: 'test-ios-library', + version: '1.0.7', + }, + locale: 'en-US', + network: { + bluetooth: false, + carrier: 'unavailable', + cellular: false, + wifi: true, + }, + os: { + name: 'iOS', + version: '14.0', + }, + screen: { + density: 2, + height: 320, + width: 568, + }, + timezone: 'Asia/Kolkata', + traits: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + lastName: 'Name', + createdAt: '2020-09-30T19:11:00.337Z', + email: 'test_1@test.com', + phone: '9876543210', + key1: 'value1', + }, + userAgent: 'unknown', + }, + event: 'Test Event 2', + integrations: { + All: true, + }, + messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', + originalTimestamp: '2020-09-30T19:11:00.337Z', + receivedAt: '2020-10-01T00:41:11.369+05:30', + request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', + sentAt: '2020-09-30T19:11:10.382Z', + timestamp: '2020-10-01T00:41:01.324+05:30', + type: 'identify', + }, + destination: { + Config: { + apiKey: 'intercomApiKey', + appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', + collectContext: false, + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.intercom.io/users', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer intercomApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', + }, + params: {}, + body: { + JSON: { + email: 'test_1@test.com', + phone: '9876543210', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + custom_attributes: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + key1: 'value1', + }, + update_last_request_at: true, + name: 'Name', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + }, + statusCode: 200, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/intercom/router/data.ts b/test/integrations/destinations/intercom/router/data.ts index 7ce3c7351a..2ce8621ca1 100644 --- a/test/integrations/destinations/intercom/router/data.ts +++ b/test/integrations/destinations/intercom/router/data.ts @@ -794,4 +794,394 @@ export const data = [ }, }, }, + { + name: 'intercom', + description: 'Test 0', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + channel: 'mobile', + context: { + app: { + build: '1.0', + name: 'Test_Example', + namespace: 'com.example.testapp', + version: '1.0', + }, + device: { + id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + manufacturer: 'Apple', + model: 'iPhone', + name: 'iPod touch (7th generation)', + type: 'iOS', + }, + library: { + name: 'test-ios-library', + version: '1.0.7', + }, + locale: 'en-US', + network: { + bluetooth: false, + carrier: 'unavailable', + cellular: false, + wifi: true, + }, + os: { + name: 'iOS', + version: '14.0', + }, + screen: { + density: 2, + height: 320, + width: 568, + }, + timezone: 'Asia/Kolkata', + traits: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + name: 'Test Name', + firstName: 'Test', + lastName: 'Name', + createdAt: '2020-09-30T19:11:00.337Z', + userId: 'test_user_id_1', + email: 'test_1@test.com', + phone: '9876543210', + key1: 'value1', + }, + userAgent: 'unknown', + }, + event: 'Test Event 2', + integrations: { + All: true, + }, + messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', + originalTimestamp: '2020-09-30T19:11:00.337Z', + receivedAt: '2020-10-01T00:41:11.369+05:30', + request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', + sentAt: '2020-09-30T19:11:10.382Z', + timestamp: '2020-10-01T00:41:01.324+05:30', + type: 'identify', + }, + metadata: { + jobId: 1, + }, + destination: { + Config: { + apiKey: 'testApiKey', + apiVersion: 'v1', + sendAnonymousId: false, + updateLastRequestAt: false, + collectContext: false, + }, + }, + }, + { + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + channel: 'mobile', + context: { + app: { + build: '1.0', + name: 'Test_Example', + namespace: 'com.example.testapp', + version: '1.0', + }, + device: { + id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + manufacturer: 'Apple', + model: 'iPhone', + name: 'iPod touch (7th generation)', + type: 'iOS', + }, + library: { + name: 'test-ios-library', + version: '1.0.7', + }, + locale: 'en-US', + network: { + bluetooth: false, + carrier: 'unavailable', + cellular: false, + wifi: true, + }, + os: { + name: 'iOS', + version: '14.0', + }, + screen: { + density: 2, + height: 320, + width: 568, + }, + timezone: 'Asia/Kolkata', + traits: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + firstName: 'Test', + lastName: 'Name', + createdAt: '2020-09-30T19:11:00.337Z', + email: 'test_1@test.com', + phone: '9876543210', + key1: 'value1', + }, + userAgent: 'unknown', + }, + event: 'Test Event 2', + integrations: { + All: true, + }, + messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', + originalTimestamp: '2020-09-30T19:11:00.337Z', + receivedAt: '2020-10-01T00:41:11.369+05:30', + request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', + sentAt: '2020-09-30T19:11:10.382Z', + timestamp: '2020-10-01T00:41:01.324+05:30', + type: 'identify', + }, + metadata: { + jobId: 2, + }, + destination: { + Config: { + apiKey: 'testApiKey', + apiVersion: 'v1', + sendAnonymousId: false, + updateLastRequestAt: false, + collectContext: false, + }, + }, + }, + { + message: { + userId: 'user@5', + groupId: 'rudderlabs', + channel: 'web', + context: { + traits: { + email: 'test+5@rudderlabs.com', + phone: '+91 9599999999', + firstName: 'John', + lastName: 'Snow', + ownerId: '17', + }, + }, + traits: { + name: 'RudderStack', + size: 500, + website: 'www.rudderstack.com', + industry: 'CDP', + plan: 'enterprise', + }, + type: 'group', + originalTimestamp: '2023-11-10T14:42:44.724Z', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + destination: { + Config: { + apiKey: 'testApiKey', + apiVersion: 'v1', + sendAnonymousId: false, + collectContext: false, + }, + }, + metadata: { + jobId: 3, + }, + }, + ], + destType: 'intercom', + }, + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.intercom.io/users', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', + }, + params: {}, + body: { + JSON: { + email: 'test_1@test.com', + phone: '9876543210', + name: 'Test Name', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + update_last_request_at: false, + user_id: 'test_user_id_1', + custom_attributes: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + key1: 'value1', + }, + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + }, + metadata: [ + { + jobId: 1, + }, + ], + batched: false, + statusCode: 200, + destination: { + Config: { + apiKey: 'testApiKey', + apiVersion: 'v1', + collectContext: false, + sendAnonymousId: false, + updateLastRequestAt: false, + }, + }, + }, + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.intercom.io/users', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', + }, + params: {}, + body: { + JSON: { + email: 'test_1@test.com', + phone: '9876543210', + signed_up_at: 1601493060, + name: 'Test Name', + last_seen_user_agent: 'unknown', + update_last_request_at: false, + custom_attributes: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + key1: 'value1', + }, + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + }, + metadata: [ + { + jobId: 2, + }, + ], + batched: false, + statusCode: 200, + destination: { + Config: { + apiKey: 'testApiKey', + apiVersion: 'v1', + collectContext: false, + sendAnonymousId: false, + updateLastRequestAt: false, + }, + }, + }, + { + batched: false, + batchedRequest: [ + { + body: { + FORM: {}, + JSON: { + company_id: 'rudderlabs', + industry: 'CDP', + name: 'RudderStack', + plan: 'enterprise', + size: 500, + website: 'www.rudderstack.com', + }, + JSON_ARRAY: {}, + XML: {}, + }, + endpoint: 'https://api.intercom.io/companies', + files: {}, + headers: { + Accept: 'application/json', + Authorization: 'Bearer testApiKey', + 'Content-Type': 'application/json', + 'Intercom-Version': '1.4', + }, + method: 'POST', + params: {}, + type: 'REST', + version: '1', + }, + { + body: { + FORM: {}, + JSON: { + companies: [ + { + company_id: 'rudderlabs', + name: 'RudderStack', + }, + ], + email: 'test+5@rudderlabs.com', + user_id: 'user@5', + }, + JSON_ARRAY: {}, + XML: {}, + }, + endpoint: 'https://api.intercom.io/users', + files: {}, + headers: { + Accept: 'application/json', + Authorization: 'Bearer testApiKey', + 'Content-Type': 'application/json', + 'Intercom-Version': '1.4', + }, + method: 'POST', + params: {}, + type: 'REST', + version: '1', + }, + ], + destination: { + Config: { + apiKey: 'testApiKey', + apiVersion: 'v1', + collectContext: false, + sendAnonymousId: false, + }, + }, + metadata: [ + { + jobId: 3, + }, + ], + statusCode: 200, + }, + ], + }, + }, + }, + }, ];