Skip to content

Commit

Permalink
Merge branch 'develop' into feat.hs-batching
Browse files Browse the repository at this point in the history
  • Loading branch information
mihir-4116 authored Feb 2, 2024
2 parents be93d67 + 717639b commit cd00df0
Show file tree
Hide file tree
Showing 29 changed files with 4,103 additions and 808 deletions.
46 changes: 41 additions & 5 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"@koa/router": "^12.0.0",
"@ndhoule/extend": "^2.0.0",
"@pyroscope/nodejs": "^0.2.6",
"@rudderstack/integrations-lib": "^0.1.8",
"@rudderstack/integrations-lib": "^0.2.2",
"@rudderstack/workflow-engine": "^0.6.9",
"ajv": "^8.12.0",
"ajv-draft-04": "^1.0.0",
Expand Down
81 changes: 81 additions & 0 deletions src/cdk/v2/destinations/intercom/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const BASE_ENDPOINT = 'https://api.intercom.io';
const BASE_EU_ENDPOINT = 'https://api.eu.intercom.io';
const BASE_AU_ENDPOINT = 'https://api.au.intercom.io';

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

const ReservedAttributes = {
v1UserAttributes: [
'userId',
'email',
'phone',
'name',
'createdAt',
'firstName',
'lastName',
'firstname',
'lastname',
'company',
],
v2UserAttributes: [
'userId',
'role',
'email',
'phone',
'name',
'avatar',
'company',
'ownerId',
'lastName',
'lastname',
'firstName',
'firstname',
'createdAt',
'timestamp',
'lastSeenAt',
'originalTimestamp',
'unsubscribedFromEmails',
],
v1CompanyAttributes: [
'remoteCreatedAt',
'monthlySpend',
'industry',
'website',
'size',
'plan',
'name',
'userId',
],
v2CompanyAttributes: [
'tags',
'size',
'plan',
'name',
'email',
'userId',
'website',
'industry',
'segments',
'userCount',
'createdAt',
'sessionCount',
'monthlySpend',
'remoteCreatedAt',
],
};

const ReservedCompanyProperties = ['id', 'name', 'industry'];

const MetadataTypes = { richLink: ['url', 'value'], monetaryAmount: ['amount', 'currency'] };

module.exports = {
BASE_ENDPOINT,
MetadataTypes,
BASE_EU_ENDPOINT,
BASE_AU_ENDPOINT,
ReservedAttributes,
SEARCH_CONTACT_ENDPOINT,
ReservedCompanyProperties,
CREATE_OR_UPDATE_COMPANY_ENDPOINT,
};
230 changes: 230 additions & 0 deletions src/cdk/v2/destinations/intercom/procWorkflow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
bindings:
- name: EventType
path: ../../../../constants
- path: ./utils
exportAll: true
- path: ../../bindings/jsontemplate
exportAll: true
- name: defaultRequestConfig
path: ../../../../v0/util
- name: removeUndefinedAndNullValues
path: ../../../../v0/util
- name: getFieldValueFromMessage
path: ../../../../v0/util
- name: isDefinedAndNotNull
path: ../../../../v0/util
- name: addExternalIdToTraits
path: ../../../../v0/util
- path: ../../bindings/jsontemplate

steps:
- name: checkIfProcessed
condition: .message.statusCode
template: |
$.batchMode ? .message.body.JSON : .message;
onComplete: return

- name: messageType
template: |
.message.type.toLowerCase();
- name: validateInput
template: |
let messageType = $.outputs.messageType;
$.assert(messageType, "message Type is not present. Aborting");
$.assert(messageType in {{$.EventType.([.IDENTIFY, .TRACK, .GROUP])}}, "message type " + messageType + " is not supported");
$.assertConfig(.destination.Config.apiKey, "Access Token is not present. Aborting");
- name: apiVersion
template: |
const version = $.isDefinedAndNotNull(.destination.Config.apiVersion) ? .destination.Config.apiVersion : "v2";
version;
- name: rEtlPayload
condition: .message.context.mappedToDestination === true
template: |
$.addExternalIdToTraits(.message);
const payload = $.getFieldValueFromMessage(.message, "traits");
payload;
- name: searchContact
condition: ($.outputs.messageType === {{$.EventType.IDENTIFY}} || $.outputs.messageType === {{$.EventType.GROUP}}) && $.outputs.apiVersion !== "v1"
template: |
const contactId = await $.searchContact(.message, .destination);
contactId;
- name: identifyTransformationForLatestVersion
condition: $.outputs.messageType === {{$.EventType.IDENTIFY}} && $.outputs.apiVersion !== "v1" && !.message.context.mappedToDestination
template: |
const payload = .message.({
external_id: {{{{$.getGenericPaths("userIdOnly")}}}},
email: {{{{$.getGenericPaths("email")}}}},
phone: {{{{$.getGenericPaths("phone")}}}},
avatar: {{{{$.getGenericPaths("avatar")}}}},
last_seen_at: $.toSeconds(.context.traits.lastSeenAt),
role: .traits.role || .context.traits.role,
signed_up_at: $.toSeconds(.traits.createdAt || .context.traits.createdAt),
owner_id: Number(.traits.ownerId || .context.traits.ownerId) || undefined,
unsubscribed_from_emails: .traits.unsubscribedFromEmails || .context.traits.unsubscribedFromEmails
});
!(payload.external_id) && .destination.Config.sendAnonymousId ? payload.external_id = .message.anonymousId;
payload;
- name: identifyPayloadForLatestVersion
condition: $.outputs.messageType === {{$.EventType.IDENTIFY}} && $.outputs.apiVersion !== "v1"
template: |
const payload = .message.context.mappedToDestination ? $.outputs.rEtlPayload : $.outputs.identifyTransformationForLatestVersion;
payload.name = $.getName(.message);
payload.custom_attributes = .message.context.traits || {};
payload.custom_attributes = $.filterCustomAttributes(payload, "user", .destination);
payload.external_id = !payload.external_id && .destination.Config.sendAnonymousId && .message.anonymousId ? .message.anonymousId : payload.external_id;
$.context.payload = payload;
$.assert($.context.payload.external_id || $.context.payload.email, "Either email or userId is required for Identify call");
const endpoint = $.getBaseEndpoint(.destination) + "/" + "contacts";
$.context.requestMethod = $.outputs.searchContact ? 'PUT' : 'POST';
$.context.endpoint = $.outputs.searchContact ? endpoint + "/" + $.outputs.searchContact : endpoint;
$.context.payload = $.removeUndefinedAndNullValues($.context.payload);
- name: identifyTransformationForOlderVersion
condition: $.outputs.messageType === {{$.EventType.IDENTIFY}} && $.outputs.apiVersion === "v1" && !.message.context.mappedToDestination
template: |
const payload = .message.({
user_id: {{{{$.getGenericPaths("userIdOnly")}}}},
email: {{{{$.getGenericPaths("email")}}}},
phone: {{{{$.getGenericPaths("phone")}}}},
signed_up_at: $.toSeconds(.traits.createdAt || .context.traits.createdAt),
last_seen_user_agent: .context.userAgent,
});
!(payload.user_id) && .destination.Config.sendAnonymousId ? payload.user_id = .message.anonymousId;
payload;
- name: identifyPayloadForOlderVersion
condition: $.outputs.messageType === {{$.EventType.IDENTIFY}} && $.outputs.apiVersion === "v1"
template: |
let payload = .message.context.mappedToDestination ? $.outputs.rEtlPayload : $.outputs.identifyTransformationForOlderVersion;
payload = {
...payload,
name : $.getName(.message),
custom_attributes : .message.traits || .message.context.traits || {},
update_last_request_at: typeof .destination.Config.updateLastRequestAt === 'boolean' ? .destination.Config.updateLastRequestAt : true
}
payload.companies = $.getCompaniesList(payload);
payload.custom_attributes = !.message.context.mappedToDestination ? $.filterCustomAttributes(payload, "user", .destination);
payload.user_id = !payload.user_id && .destination.Config.sendAnonymousId && .message.anonymousId ? .message.anonymousId : payload.user_id;
$.context.payload = payload;
$.assert($.context.payload.user_id || $.context.payload.email, "Either of `email` or `userId` is required for Identify call");
$.context.requestMethod = 'POST';
$.context.endpoint = $.getBaseEndpoint(.destination) + "/" + "users";
$.context.payload = $.removeUndefinedAndNullValues($.context.payload);
- name: trackTransformation
condition: $.outputs.messageType === {{$.EventType.TRACK}} && !.message.context.mappedToDestination
template: |
const timestamp = .message.().(
{{{{$.getGenericPaths("timestamp")}}}};
);
const payload = .message.({
event_name: .event,
user_id: {{{{$.getGenericPaths("userIdOnly")}}}},
email: {{{{$.getGenericPaths("email")}}}},
metadata: .properties
});
$.outputs.apiVersion !== "v1" ? payload.id = .message.properties.id || .message.traits.id;
$.outputs.apiVersion !== "v1" ? payload.created_at = $.toSeconds(timestamp);
$.outputs.apiVersion === "v1" ? payload.created = $.toSeconds(timestamp);
!(payload.user_id) && .destination.Config.sendAnonymousId ? payload.user_id = .message.anonymousId;
payload;
- name: trackPayload
condition: $.outputs.messageType === {{$.EventType.TRACK}}
template: |
let payload = .message.context.mappedToDestination ? $.outputs.rEtlPayload : $.outputs.trackTransformation;
payload = $.addMetadataToPayload(payload);
$.context.payload = payload;
$.assert($.context.payload.event_name, "Event name is required for track call");
$.assert($.context.payload.user_id || $.context.payload.email, "Either email or userId is required for Track call");
$.context.requestMethod = 'POST';
$.context.endpoint = $.getBaseEndpoint(.destination) + "/" + "events";
$.context.payload = $.removeUndefinedAndNullValues($.context.payload);
- name: groupTransformation
condition: $.outputs.messageType === {{$.EventType.GROUP}} && !.message.context.mappedToDestination
template: |
const payload = .message.({
company_id: {{{{$.getGenericPaths("groupId")}}}},
name: {{{{$.getGenericPaths("name")}}}},
website: {{{{$.getGenericPaths("website")}}}},
plan: .traits.plan || .context.traits.plan,
size: Number(.traits.size || .context.traits.size),
industry: .traits.industry || .context.traits.industry,
monthly_spend: .traits.monthlySpend || .context.traits.monthlySpend ? Number(.traits.monthlySpend || .context.traits.monthlySpend) : undefined,
remote_created_at: .traits.remoteCreatedAt || .context.traits.remoteCreatedAt ? Number(.traits.remoteCreatedAt || .context.traits.remoteCreatedAt) : undefined
});
payload;
- name: groupPayloadForLatestVersion
condition: $.outputs.messageType === {{$.EventType.GROUP}} && $.outputs.apiVersion !== "v1"
steps:
- name: validateMessageAndPreparePayload
template: |
$.assert(.message.groupId, "groupId is required for group call");
const payload = .message.context.mappedToDestination ? $.outputs.rEtlPayload : $.outputs.groupTransformation;
payload.custom_attributes = .message.traits || {};
payload.custom_attributes = $.filterCustomAttributes(payload, "company", .destination);
$.context.payload = payload;
- name: whenSearchContactFound
condition: $.isDefinedAndNotNull($.outputs.searchContact)
template: |
const contactId = $.outputs.searchContact;
const companyId = await $.createOrUpdateCompany($.context.payload, .destination);
$.assert(companyId, "Unable to create or update company");
$.context.payload = {
id: companyId,
};
$.context.endpoint = $.getBaseEndpoint(.destination) + "/" + "contacts" + "/" + contactId + "/" + "companies";
else:
name: whenSearchContactNotFound
template: |
$.context.endpoint = $.getBaseEndpoint(.destination) + "/" + "companies";
- name: prepareFinalPayload
template:
$.context.requestMethod = 'POST';
$.removeUndefinedAndNullValues($.context.payload);

- name: groupPayloadForOlderVersion
condition: $.outputs.messageType === {{$.EventType.GROUP}} && $.outputs.apiVersion === "v1"
template: |
$.context.response = [];
const response = $.defaultRequestConfig();
let payload = .message.context.mappedToDestination ? $.outputs.rEtlPayload : $.outputs.groupTransformation;
payload = {
...payload,
custom_attributes : $.getFieldValueFromMessage(.message, "traits") || {}
}
payload.custom_attributes = $.filterCustomAttributes(payload, "company", .destination);
response.body.JSON = $.removeUndefinedAndNullValues(payload);
response.endpoint = $.getBaseEndpoint(.destination) + "/" + "companies";
response.headers = $.getHeaders(.destination, $.outputs.apiVersion);
response.method = "POST";
response.userId = .message.anonymousId;
$.context.response.push(response);
const attachUserAndCompanyResponse = $.attachUserAndCompany(.message, .destination.Config);
attachUserAndCompanyResponse ? attachUserAndCompanyResponse.userId = .message.anonymousId;
attachUserAndCompanyResponse ? $.context.response.push(attachUserAndCompanyResponse);
- name: buildResponseForProcessTransformation
description: Build response for multiple transformed event
condition: $.context.response && $.context.response.length > 0
template: |
$.context.response;
else:
name: buildResponseForProcessTransformation
description: Build response for single transformed event
template: |
const response = $.defaultRequestConfig();
response.body.JSON = $.context.payload;
response.endpoint = $.context.endpoint;
response.method = $.context.requestMethod;
response.headers = $.getHeaders(.destination, $.outputs.apiVersion);
$.outputs.apiVersion === "v1" && $.outputs.messageType !== {{$.EventType.GROUP}} ? response.userId = .message.anonymousId;
response;
Loading

0 comments on commit cd00df0

Please sign in to comment.