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: add tags to a company in intercom #3434

Merged
merged 10 commits into from
Jun 19, 2024
2 changes: 2 additions & 0 deletions src/cdk/v2/destinations/intercom/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const BASE_AU_ENDPOINT = 'https://api.au.intercom.io';

const SEARCH_CONTACT_ENDPOINT = 'contacts/search';
const CREATE_OR_UPDATE_COMPANY_ENDPOINT = 'companies';
const TAGS_ENDPOINT = 'tags';

const ReservedAttributes = {
v1UserAttributes: [
Expand Down Expand Up @@ -78,4 +79,5 @@ module.exports = {
SEARCH_CONTACT_ENDPOINT,
ReservedCompanyProperties,
CREATE_OR_UPDATE_COMPANY_ENDPOINT,
TAGS_ENDPOINT,
};
25 changes: 25 additions & 0 deletions src/cdk/v2/destinations/intercom/procWorkflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ bindings:
- name: addExternalIdToTraits
path: ../../../../v0/util
- path: ../../bindings/jsontemplate
- name: HTTP_STATUS_CODES
path: ../../../../v0/util/constant

steps:
- name: checkIfProcessed
Expand Down Expand Up @@ -182,10 +184,17 @@ steps:
id: companyId,
};
$.context.endpoint = $.getBaseEndpoint(.destination) + "/" + "contacts" + "/" + contactId + "/" + "companies";
const payload = $.context.payload;
const endpoint = $.context.endpoint;
await $.attachContactToCompany(payload, endpoint, .destination);
await $.addOrUpdateTagsToCompany(.message, .destination, companyId);
else:
name: whenSearchContactNotFound
template: |
const companyId = await $.createOrUpdateCompany($.context.payload, .destination);
$.assert(companyId, "Unable to create or update company");
$.context.endpoint = $.getBaseEndpoint(.destination) + "/" + "companies";
await $.addOrUpdateTagsToCompany(.message, .destination, companyId);
- name: prepareFinalPayload
template: |
$.context.requestMethod = 'POST';
Expand All @@ -208,9 +217,25 @@ steps:
response.method = "POST";
response.userId = .message.anonymousId;
$.context.response.push(response);
payload = response.body.JSON;
const companyId = await $.createOrUpdateCompany(payload, .destination);
$.assert(companyId, "Unable to create or update company");
manish339k marked this conversation as resolved.
Show resolved Hide resolved
const attachUserAndCompanyResponse = $.attachUserAndCompany(.message, .destination.Config);
attachUserAndCompanyResponse ? attachUserAndCompanyResponse.userId = .message.anonymousId;
attachUserAndCompanyResponse ? $.context.response.push(attachUserAndCompanyResponse);
payload = attachUserAndCompanyResponse.body.JSON;
let endpoint = attachUserAndCompanyResponse.endpoint;
attachUserAndCompanyResponse ? await $.attachContactToCompany(payload, endpoint, .destination);
await $.addOrUpdateTagsToCompany(.message, .destination, companyId);
manish339k marked this conversation as resolved.
Show resolved Hide resolved

- name: statusCode
condition: $.outputs.messageType === {{$.EventType.GROUP}}
template: |
$.HTTP_STATUS_CODES.SUPPRESS_EVENTS
else:
name: successStatusCode
template: |
$.HTTP_STATUS_CODES.OK

- name: buildResponseForProcessTransformation
description: Build response for multiple transformed event
Expand Down
2 changes: 1 addition & 1 deletion src/cdk/v2/destinations/intercom/rtWorkflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ steps:
"batched": false,
"destination": ^[idx].destination,
"metadata": ^[idx].metadata[],
"statusCode": 200
"statusCode": .outputs.statusCode
})[]
- name: failedEvents
template: |
Expand Down
138 changes: 136 additions & 2 deletions src/cdk/v2/destinations/intercom/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
const md5 = require('md5');
const get = require('get-value');
const { NetworkError } = require('@rudderstack/integrations-lib');
const {
NetworkError,
ConfigurationError,
InstrumentationError,
} = require('@rudderstack/integrations-lib');
const tags = require('../../../../v0/util/tags');
const { httpPOST } = require('../../../../adapters/network');
const {
Expand All @@ -27,8 +31,42 @@ const {
SEARCH_CONTACT_ENDPOINT,
ReservedCompanyProperties,
CREATE_OR_UPDATE_COMPANY_ENDPOINT,
TAGS_ENDPOINT,
} = require('./config');

/**
* method to handle error during api call
* ref docs: https://developers.intercom.com/docs/references/rest-api/errors/error-codes/
* https://developers.intercom.com/docs/references/rest-api/errors/error-objects/
* https://developers.intercom.com/docs/references/rest-api/errors/http-responses/
* e.g.
* 400 - code: parameter_not_found (or parameter_invalid), message: company not specified
* 401 - code: unauthorized, message: Access Token Invalid
* 404 - code: company_not_found, message: Company Not Found
* @param {*} message
* @param {*} processedResponse
*/
const intercomErrorHandler = (message, processedResponse) => {
const errorMessages = JSON.stringify(processedResponse.response);
if (processedResponse.status === 400) {
throw new InstrumentationError(`${message} : ${errorMessages}`);
manish339k marked this conversation as resolved.
Show resolved Hide resolved
}
if (processedResponse.status === 401) {
throw new ConfigurationError(`${message} : ${errorMessages}`);
}
if (processedResponse.status === 404) {
throw new InstrumentationError(`${message} : ${errorMessages}`);
}
throw new NetworkError(
`${message} : ${errorMessages}`,
processedResponse.status,
{
[tags]: getDynamicErrorType(processedResponse.status),
},
processedResponse,
);
};

/**
* Returns destination request headers
* @param {*} destination
Expand Down Expand Up @@ -285,7 +323,8 @@ const searchContact = async (message, destination) => {
* @returns
*/
const createOrUpdateCompany = async (payload, destination) => {
const headers = getHeaders(destination);
const { apiVersion } = destination.Config;
const headers = getHeaders(destination, apiVersion);
const finalPayload = JSON.stringify(removeUndefinedAndNullValues(payload));
const baseEndPoint = getBaseEndpoint(destination);
const endpoint = `${baseEndPoint}/${CREATE_OR_UPDATE_COMPANY_ENDPOINT}`;
Expand Down Expand Up @@ -369,6 +408,99 @@ const addMetadataToPayload = (payload) => {
return finalPayload;
};

/**
* Api call to attach user to the company
* Ref doc v1: https://developers.intercom.com/docs/references/1.4/rest-api/users/companies-and-users/
* Ref doc v2: https://developers.intercom.com/docs/references/2.10/rest-api/api.intercom.io/Contacts/attachContactToACompany/
* @param {*} payload
* @param {*} endpoint
* @param {*} destination
*/
const attachContactToCompany = async (payload, endpoint, destination) => {
let { apiVersion } = destination.Config;
apiVersion = isDefinedAndNotNull(apiVersion) ? apiVersion : 'v2';
let endpointPath = '/contact/{id}/companies';
if (apiVersion === 'v1') {
endpointPath = '/users';
}
const commonStatTags = {
destType: 'intercom',
feature: 'transformation',
requestMethod: 'POST',
module: 'router',
};
const headers = getHeaders(destination, apiVersion);
const finalPayload = JSON.stringify(removeUndefinedAndNullValues(payload));
const response = await httpPOST(
endpoint,
finalPayload,
{
headers,
},
{
...commonStatTags,
endpointPath,
},
);

const processedResponse = processAxiosResponse(response);
if (!isHttpStatusSuccess(processedResponse.status)) {
intercomErrorHandler('Unable to attach Contact or User to Company due to', processedResponse);
}
};

/**
* Api calls to add or update tags to the company
* Ref doc v1: https://developers.intercom.com/docs/references/1.4/rest-api/tags/tag-or-untag-users-companies-leads-contacts/
* Ref doc v2: https://developers.intercom.com/docs/references/2.10/rest-api/api.intercom.io/Tags/createTag/
* @param message
* @param destination
* @param id
* @returns
*/
const addOrUpdateTagsToCompany = async (message, destination, id) => {
const companyTags = message?.context?.traits?.tags;
if (!companyTags) return;
const { apiVersion } = destination.Config;
const headers = getHeaders(destination, apiVersion);
const baseEndPoint = getBaseEndpoint(destination);
const endpoint = `${baseEndPoint}/${TAGS_ENDPOINT}`;
const statTags = {
destType: 'intercom',
feature: 'transformation',
endpointPath: '/tags',
requestMethod: 'POST',
module: 'router',
};
await Promise.all(
companyTags.map(async (tag) => {
const finalPayload = {
name: tag,
companies: [
{
id,
},
],
};
const response = await httpPOST(
endpoint,
finalPayload,
{
headers,
},
statTags,
);
const processedResponse = processAxiosResponse(response);
if (!isHttpStatusSuccess(processedResponse.status)) {
intercomErrorHandler(
'Unable to Add or Update the Tag to Company due to',
processedResponse,
);
}
}),
);
};

module.exports = {
getName,
getHeaders,
Expand All @@ -382,4 +514,6 @@ module.exports = {
filterCustomAttributes,
checkIfEmailOrUserIdPresent,
separateReservedAndRestMetadata,
attachContactToCompany,
addOrUpdateTagsToCompany,
};
Loading
Loading