diff --git a/packages/nodes-base/credentials/IntercomApi.credentials.ts b/packages/nodes-base/credentials/IntercomApi.credentials.ts new file mode 100644 index 000000000..64263e889 --- /dev/null +++ b/packages/nodes-base/credentials/IntercomApi.credentials.ts @@ -0,0 +1,18 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + + +export class IntercomApi implements ICredentialType { + name = 'intercomApi'; + displayName = 'Intercom API'; + properties = [ + { + displayName: 'API Key', + name: 'apiKey', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Intercom/CompanyDescription.ts b/packages/nodes-base/nodes/Intercom/CompanyDescription.ts new file mode 100644 index 000000000..df8239719 --- /dev/null +++ b/packages/nodes-base/nodes/Intercom/CompanyDescription.ts @@ -0,0 +1,411 @@ +import { INodeProperties } from "n8n-workflow"; + +export const companyOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'company', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new company', + }, + { + name: 'Get', + value: 'get', + description: 'Get data of a company', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get data of all companies', + }, + { + name: 'Update', + value: 'update', + description: 'Update a company', + }, + { + name: 'Users', + value: 'users', + description: `List company's users`, + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const companyFields = [ + +/* -------------------------------------------------------------------------- */ +/* company:users */ +/* -------------------------------------------------------------------------- */ + + { + displayName: 'List By', + name: 'listBy', + type: 'options', + displayOptions: { + show: { + resource: [ + 'company', + ], + operation: [ + 'users', + ], + }, + }, + options: [ + { + name: 'ID', + value: 'id', + description: 'The Intercom defined id representing the company', + }, + { + name: 'Company ID', + value: 'companyId', + description: 'The company_id you have given to the company', + }, + ], + default: '', + description: 'List by', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'company', + ], + operation: [ + 'users', + ], + }, + }, + description: 'View by value', + }, +/* -------------------------------------------------------------------------- */ +/* company:getAll */ +/* -------------------------------------------------------------------------- */ + + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'company', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'company', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 60, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + default: {}, + displayOptions: { + show: { + resource: [ + 'company', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Segment ID', + name: 'segment_id', + type: 'string', + default: '', + description: 'Segment representing the Lead', + }, + { + displayName: 'Tag ID', + name: 'tag_id', + type: 'string', + default: '', + description: 'Tag representing the Lead', + }, + ] + }, + +/* -------------------------------------------------------------------------- */ +/* company:get */ +/* -------------------------------------------------------------------------- */ + + { + displayName: 'Select By', + name: 'selectBy', + type: 'options', + displayOptions: { + show: { + resource: [ + 'company', + ], + operation: [ + 'get', + ], + }, + }, + options: [ + { + name: 'Company ID', + value: 'companyId', + description: 'The company_id you have given to the company', + }, + { + name: 'ID', + value: 'id', + description: 'The Intercom defined id representing the company', + }, + { + name: 'Name', + value: 'name', + description: 'The name of the company', + }, + ], + default: '', + description: 'What property to use to query the company.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'company', + ], + operation: [ + 'get', + ], + }, + }, + description: 'View by value', + }, + +/* -------------------------------------------------------------------------- */ +/* company:create/update */ +/* -------------------------------------------------------------------------- */ + + { + displayName: 'Company Id', + name: 'companyId', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'company', + ], + operation: [ + 'create', + 'update', + ], + }, + }, + description: 'The company id you have defined for the company', + }, + { + displayName: 'JSON Parameters', + name: 'jsonParameters', + type: 'boolean', + default: false, + description: '', + displayOptions: { + show: { + operation: [ + 'create', + 'update', + ], + resource: [ + 'company', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + 'update', + ], + resource: [ + 'company', + ], + }, + }, + options: [ + { + displayName: 'Industry', + name: 'industry', + type: 'string', + default: '', + description: 'The industry that this company operates in', + }, + { + displayName: 'Monthly Spend', + name: 'monthlySpend', + type: 'string', + default: '', + description: 'The phone number of the user', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + placeholder: '', + description: 'Name of the user', + }, + { + displayName: 'Plan', + name: 'plan', + type: 'string', + default: '', + placeholder: '', + description: 'The name of the plan you have associated with the company', + }, + { + displayName: 'Size', + name: 'size', + type: 'number', + default: '', + description: 'The number of employees in this company', + }, + { + displayName: 'Website', + name: 'website', + type: 'string', + default: '', + description: `The URL for this company's website. Please note that the value
specified here is not validated. Accepts any string.`, + }, + ] + }, + { + displayName: 'Custom Attributes', + name: 'customAttributesJson', + type: 'json', + required: false, + typeOptions: { + alwaysOpenEditWindow: true, + }, + displayOptions: { + show: { + resource: [ + 'company', + ], + operation: [ + 'create', + 'update', + ], + jsonParameters: [ + true, + ], + }, + }, + default: '', + description: 'A hash of key/value pairs to represent custom data you want to attribute to a user.', + }, + { + displayName: 'Custom Attributes', + name: 'customAttributesUi', + type: 'fixedCollection', + default: '', + placeholder: 'Add Attribute', + typeOptions: { + multipleValues: true, + }, + required: false, + displayOptions: { + show: { + resource: [ + 'company', + ], + operation: [ + 'create', + 'update', + ], + jsonParameters: [ + false, + ], + }, + }, + options: [ + { + name: 'customAttributesValues', + displayName: 'Attributes', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + }, + ], + } + ], + description: 'A hash of key/value pairs to represent custom data you want to attribute to a user.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Intercom/CompanyInteface.ts b/packages/nodes-base/nodes/Intercom/CompanyInteface.ts new file mode 100644 index 000000000..bffa55334 --- /dev/null +++ b/packages/nodes-base/nodes/Intercom/CompanyInteface.ts @@ -0,0 +1,13 @@ +import { IDataObject } from "n8n-workflow"; + +export interface ICompany { + remote_created_at?: string; + company_id?: string; + name?: string; + monthly_spend?: number; + plan?: string; + size?: number; + website?: string; + industry?: string; + custom_attributes?: IDataObject; +} diff --git a/packages/nodes-base/nodes/Intercom/GenericFunctions.ts b/packages/nodes-base/nodes/Intercom/GenericFunctions.ts new file mode 100644 index 000000000..be68ef0c3 --- /dev/null +++ b/packages/nodes-base/nodes/Intercom/GenericFunctions.ts @@ -0,0 +1,82 @@ +import { OptionsWithUri } from 'request'; + +import { + IExecuteFunctions, + IHookFunctions, + ILoadOptionsFunctions, + IExecuteSingleFunctions +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; + +export async function intercomApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any + const credentials = this.getCredentials('intercomApi'); + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + const headerWithAuthentication = Object.assign({}, + { Authorization: `Bearer ${credentials.apiKey}`, Accept: 'application/json' }); + + const options: OptionsWithUri = { + headers: headerWithAuthentication, + method, + qs: query, + uri: uri || `https://api.intercom.io${endpoint}`, + body, + json: true + }; + + try { + return await this.helpers.request!(options); + } catch (error) { + const errorMessage = error.response.body.message || error.response.body.Message; + + if (errorMessage !== undefined) { + throw errorMessage; + } + throw error.response.body; + } +} + + + +/** + * Make an API request to paginated intercom endpoint + * and return all results + */ +export async function intercomApiRequestAllItems(this: IHookFunctions | IExecuteFunctions, propertyName: string, endpoint: string, method: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + + query.per_page = 60; + + let uri: string | undefined; + + do { + responseData = await intercomApiRequest.call(this, endpoint, method, body, query, uri); + uri = responseData.pages.next; + returnData.push.apply(returnData, responseData[propertyName]); + } while ( + responseData.pages !== undefined && + responseData.pages.next !== undefined && + responseData.pages.next !== null + ); + + return returnData; +} + + +export function validateJSON(json: string | undefined): any { // tslint:disable-line:no-any + let result; + try { + result = JSON.parse(json!); + } catch (exception) { + result = ''; + } + return result; +} diff --git a/packages/nodes-base/nodes/Intercom/Intercom.node.ts b/packages/nodes-base/nodes/Intercom/Intercom.node.ts new file mode 100644 index 000000000..6486517c3 --- /dev/null +++ b/packages/nodes-base/nodes/Intercom/Intercom.node.ts @@ -0,0 +1,529 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; +import { + IDataObject, + INodeTypeDescription, + INodeExecutionData, + INodeType, + ILoadOptionsFunctions, + INodePropertyOptions, +} from 'n8n-workflow'; +import { + leadOpeations, + leadFields, +} from './LeadDescription'; +import { + intercomApiRequest, + intercomApiRequestAllItems, + validateJSON, +} from './GenericFunctions'; +import { + ILead, + ILeadCompany, + IAvatar, + } from './LeadInterface'; +import { userOpeations, userFields } from './UserDescription'; +import { IUser, IUserCompany } from './UserInterface'; +import { companyOperations, companyFields } from './CompanyDescription'; +import { ICompany } from './CompanyInteface'; + +export class Intercom implements INodeType { + description: INodeTypeDescription = { + displayName: 'Intercom', + name: 'intercom', + icon: 'file:intercom.png', + group: ['output'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume intercom API', + defaults: { + name: 'Intercom', + color: '#c02428', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'intercomApi', + required: true, + } + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Company', + value: 'company', + description: 'Companies allow you to represent commercial organizations using your product.', + }, + { + name: 'Lead', + value: 'lead', + description: 'Leads are useful for representing logged-out users of your application.', + }, + { + name: 'User', + value: 'user', + description: 'The Users resource is the primary way of interacting with Intercom', + }, + ], + default: 'user', + description: 'Resource to consume.', + }, + ...leadOpeations, + ...userOpeations, + ...companyOperations, + ...userFields, + ...leadFields, + ...companyFields, + ], + }; + + methods = { + loadOptions: { + // Get all the available companies to display them to user so that he can + // select them easily + async getCompanies(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + let companies, response; + try { + response = await intercomApiRequest.call(this, '/companies', 'GET'); + } catch (err) { + throw new Error(`Intercom Error: ${err}`); + } + companies = response.companies; + for (const company of companies) { + const companyName = company.name; + const companyId = company.company_id; + returnData.push({ + name: companyName, + value: companyId, + }); + } + return returnData; + }, + }, + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + const length = items.length as unknown as number; + let qs: IDataObject; + let responseData; + for (let i = 0; i < length; i++) { + qs = {}; + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + //https://developers.intercom.com/intercom-api-reference/reference#leads + if (resource === 'lead') { + if (operation === 'create' || operation === 'update') { + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const jsonActive = this.getNodeParameter('jsonParameters', i) as boolean; + const body: ILead = {}; + if (operation === 'create') { + body.email = this.getNodeParameter('email', i) as string; + } + if (additionalFields.email) { + body.email = additionalFields.email as string; + } + if (additionalFields.phone) { + body.phone = additionalFields.phone as string; + } + if (additionalFields.name) { + body.name = additionalFields.name as string; + } + if (additionalFields.unsubscribedFromEmails) { + body.unsubscribed_from_emails = additionalFields.unsubscribedFromEmails as boolean; + } + if (additionalFields.updateLastRequestAt) { + body.update_last_request_at = additionalFields.updateLastRequestAt as boolean; + } + if (additionalFields.utmSource) { + body.utm_source = additionalFields.utmSource as string; + } + if (additionalFields.utmMedium) { + body.utm_medium = additionalFields.utmMedium as string; + } + if (additionalFields.utmCampaign) { + body.utm_campaign = additionalFields.utmCampaign as string; + } + if (additionalFields.utmTerm) { + body.utm_term = additionalFields.utmTerm as string; + } + if (additionalFields.utmContent) { + body.utm_content = additionalFields.utmContent as string; + } + if (additionalFields.avatar) { + const avatar: IAvatar = { + type: 'avatar', + image_url: additionalFields.avatar as string, + }; + body.avatar = avatar; + } + if (additionalFields.companies) { + const companies: ILeadCompany[] = []; + // @ts-ignore + additionalFields.companies.forEach( o => { + const company: ILeadCompany = {}; + company.company_id = o; + companies.push(company); + }); + body.companies = companies; + } + if (!jsonActive) { + const customAttributesValues = (this.getNodeParameter('customAttributesUi', i) as IDataObject).customAttributesValues as IDataObject[]; + if (customAttributesValues) { + const customAttributes = {}; + for (let i = 0; i < customAttributesValues.length; i++) { + // @ts-ignore + customAttributes[customAttributesValues[i].name] = customAttributesValues[i].value; + } + body.custom_attributes = customAttributes; + } + } else { + const customAttributesJson = validateJSON(this.getNodeParameter('customAttributesJson', i) as string); + if (customAttributesJson) { + body.custom_attributes = customAttributesJson; + } + } + + if (operation === 'update') { + const updateBy = this.getNodeParameter('updateBy', 0) as string; + const value = this.getNodeParameter('value', i) as string; + if (updateBy === 'userId') { + body.user_id = value; + } + if (updateBy === 'id') { + body.id = value; + } + } + + try { + responseData = await intercomApiRequest.call(this, '/contacts', 'POST', body); + } catch (err) { + throw new Error(`Intercom Error: ${JSON.stringify(err)}`); + } + } + if (operation === 'get') { + const selectBy = this.getNodeParameter('selectBy', 0) as string; + const value = this.getNodeParameter('value', i) as string; + if (selectBy === 'email') { + qs.email = value; + } + if (selectBy === 'userId') { + qs.user_id = value; + } + if (selectBy === 'phone') { + qs.phone = value; + } + try { + if (selectBy === 'id') { + responseData = await intercomApiRequest.call(this, `/contacts/${value}`, 'GET'); + } else { + responseData = await intercomApiRequest.call(this, '/contacts', 'GET', {}, qs); + responseData = responseData.contacts; + } + } catch (err) { + throw new Error(`Intercom Error: ${JSON.stringify(err)}`); + } + } + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const filters = this.getNodeParameter('filters', i) as IDataObject; + Object.assign(qs, filters); + + try { + if (returnAll === true) { + responseData = await intercomApiRequestAllItems.call(this, 'contacts', '/contacts', 'GET', {}, qs); + } else { + qs.per_page = this.getNodeParameter('limit', i) as number; + responseData = await intercomApiRequest.call(this, '/contacts', 'GET', {}, qs); + responseData = responseData.contacts; + } + } catch (err) { + throw new Error(`Intercom Error: ${JSON.stringify(err)}`); + } + } + if (operation === 'delete') { + const deleteBy = this.getNodeParameter('deleteBy', 0) as string; + const value = this.getNodeParameter('value', i) as string; + try { + if (deleteBy === 'id') { + responseData = await intercomApiRequest.call(this, `/contacts/${value}`, 'DELETE'); + } else { + qs.user_id = value; + responseData = await intercomApiRequest.call(this, '/contacts', 'DELETE', {}, qs); + } + } catch (err) { + throw new Error(`Intercom Error: ${JSON.stringify(err)}`); + } + } + } + //https://developers.intercom.com/intercom-api-reference/reference#users + if (resource === 'user') { + if (operation === 'create' || operation === 'update') { + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const jsonActive = this.getNodeParameter('jsonParameters', i) as boolean; + const body: IUser = {}; + + if (operation === 'create') { + const identifierType = this.getNodeParameter('identifierType', i) as string; + if (identifierType === 'email') { + body.email = this.getNodeParameter('idValue', i) as string; + } else if (identifierType === 'userId') { + body.user_id = this.getNodeParameter('idValue', i) as string; + } + } + + if (additionalFields.email) { + body.email = additionalFields.email as string; + } + if (additionalFields.userId) { + body.user_id = additionalFields.userId as string; + } + if (additionalFields.phone) { + body.phone = additionalFields.phone as string; + } + if (additionalFields.name) { + body.name = additionalFields.name as string; + } + if (additionalFields.unsubscribedFromEmails) { + body.unsubscribed_from_emails = additionalFields.unsubscribedFromEmails as boolean; + } + if (additionalFields.updateLastRequestAt) { + body.update_last_request_at = additionalFields.updateLastRequestAt as boolean; + } + if (additionalFields.sessionCount) { + body.session_count = additionalFields.sessionCount as number; + } + if (additionalFields.avatar) { + const avatar: IAvatar = { + type: 'avatar', + image_url: additionalFields.avatar as string, + }; + body.avatar = avatar; + } + if (additionalFields.utmSource) { + body.utm_source = additionalFields.utmSource as string; + } + if (additionalFields.utmMedium) { + body.utm_medium = additionalFields.utmMedium as string; + } + if (additionalFields.utmCampaign) { + body.utm_campaign = additionalFields.utmCampaign as string; + } + if (additionalFields.utmTerm) { + body.utm_term = additionalFields.utmTerm as string; + } + if (additionalFields.utmContent) { + body.utm_content = additionalFields.utmContent as string; + } + if (additionalFields.companies) { + const companies: IUserCompany[] = []; + // @ts-ignore + additionalFields.companies.forEach( o => { + const company: IUserCompany = {}; + company.company_id = o; + companies.push(company); + }); + body.companies = companies; + } + if (additionalFields.sessionCount) { + body.session_count = additionalFields.sessionCount as number; + } + if (!jsonActive) { + const customAttributesValues = (this.getNodeParameter('customAttributesUi', i) as IDataObject).customAttributesValues as IDataObject[]; + if (customAttributesValues) { + const customAttributes = {}; + for (let i = 0; i < customAttributesValues.length; i++) { + // @ts-ignore + customAttributes[customAttributesValues[i].name] = customAttributesValues[i].value; + } + body.custom_attributes = customAttributes; + } + } else { + const customAttributesJson = validateJSON(this.getNodeParameter('customAttributesJson', i) as string); + if (customAttributesJson) { + body.custom_attributes = customAttributesJson; + } + } + + if (operation === 'update') { + const updateBy = this.getNodeParameter('updateBy', 0) as string; + const value = this.getNodeParameter('value', i) as string; + if (updateBy === 'userId') { + body.user_id = value; + } + if (updateBy === 'id') { + body.id = value; + } + if (updateBy === 'email') { + body.email = value; + } + } + + try { + responseData = await intercomApiRequest.call(this, '/users', 'POST', body, qs); + } catch (err) { + throw new Error(`Intercom Error: ${JSON.stringify(err)}`); + } + } + if (operation === 'get') { + const selectBy = this.getNodeParameter('selectBy', 0) as string; + const value = this.getNodeParameter('value', i) as string; + if (selectBy === 'userId') { + qs.user_id = value; + } + try { + if (selectBy === 'id') { + responseData = await intercomApiRequest.call(this, `/users/${value}`, 'GET', {}, qs); + } else { + responseData = await intercomApiRequest.call(this, '/users', 'GET', {}, qs); + } + } catch (err) { + throw new Error(`Intercom Error: ${JSON.stringify(err)}`); + } + } + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const filters = this.getNodeParameter('filters', i) as IDataObject; + Object.assign(qs, filters); + + try { + if (returnAll === true) { + responseData = await intercomApiRequestAllItems.call(this, 'users', '/users', 'GET', {}, qs); + } else { + qs.per_page = this.getNodeParameter('limit', i) as number; + responseData = await intercomApiRequest.call(this, '/users', 'GET', {}, qs); + responseData = responseData.users; + } + } catch (err) { + throw new Error(`Intercom Error: ${JSON.stringify(err)}`); + } + } + if (operation === 'delete') { + const id = this.getNodeParameter('id', i) as string; + try { + responseData = await intercomApiRequest.call(this, `/users/${id}`, 'DELETE'); + } catch (err) { + throw new Error(`Intercom Error: ${JSON.stringify(err)}`); + } + } + } + //https://developers.intercom.com/intercom-api-reference/reference#companies + if (resource === 'company') { + if (operation === 'create' || operation === 'update') { + const id = this.getNodeParameter('companyId', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const jsonActive = this.getNodeParameter('jsonParameters', i) as boolean; + const body: ICompany = { + company_id: id, + }; + if (additionalFields.monthlySpend) { + body.monthly_spend = additionalFields.monthlySpend as number; + } + if (additionalFields.name) { + body.name = additionalFields.name as string; + } + if (additionalFields.plan) { + body.plan = additionalFields.plan as string; + } + if (additionalFields.size) { + body.size = additionalFields.size as number; + } + if (additionalFields.website) { + body.website = additionalFields.website as string; + } + if (additionalFields.industry) { + body.industry = additionalFields.industry as string; + } + if (!jsonActive) { + const customAttributesValues = (this.getNodeParameter('customAttributesUi', i) as IDataObject).customAttributesValues as IDataObject[]; + if (customAttributesValues) { + const customAttributes = {}; + for (let i = 0; i < customAttributesValues.length; i++) { + // @ts-ignore + customAttributes[customAttributesValues[i].name] = customAttributesValues[i].value; + } + body.custom_attributes = customAttributes; + } + } else { + const customAttributesJson = validateJSON(this.getNodeParameter('customAttributesJson', i) as string); + if (customAttributesJson) { + body.custom_attributes = customAttributesJson; + } + } + try { + responseData = await intercomApiRequest.call(this, '/companies', 'POST', body, qs); + } catch (err) { + throw new Error(`Intercom Error: ${JSON.stringify(err)}`); + } + } + if (operation === 'get') { + const selectBy = this.getNodeParameter('selectBy', 0) as string; + const value = this.getNodeParameter('value', i) as string; + if (selectBy === 'companyId') { + qs.company_id = value; + } + if (selectBy === 'name') { + qs.name = value; + } + try { + if (selectBy === 'id') { + responseData = await intercomApiRequest.call(this, `/companies/${value}`, 'GET', {}, qs); + } else { + responseData = await intercomApiRequest.call(this, '/companies', 'GET', {}, qs); + } + } catch (err) { + throw new Error(`Intercom Error: ${JSON.stringify(err)}`); + } + } + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const filters = this.getNodeParameter('filters', i) as IDataObject; + Object.assign(qs, filters); + + try { + if (returnAll === true) { + responseData = await intercomApiRequestAllItems.call(this, 'companies', '/companies', 'GET', {}, qs); + } else { + qs.per_page = this.getNodeParameter('limit', i) as number; + responseData = await intercomApiRequest.call(this, '/companies', 'GET', {}, qs); + responseData = responseData.companies; + } + } catch (err) { + throw new Error(`Intercom Error: ${JSON.stringify(err)}`); + } + } + if (operation === 'users') { + const filterBy = this.getNodeParameter('filterBy', 0) as string; + const value = this.getNodeParameter('value', i) as string; + if (filterBy === 'companyId') { + qs.company_id = value; + } + try { + if (filterBy === 'id') { + responseData = await intercomApiRequest.call(this, `/companies/${value}/users`, 'GET', {}, qs); + } else { + qs.type = 'users'; + responseData = await intercomApiRequest.call(this, '/companies', 'GET', {}, qs); + } + } catch (err) { + throw new Error(`Intercom Error: ${JSON.stringify(err)}`); + } + } + } + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else { + returnData.push(responseData as IDataObject); + } + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/Intercom/LeadDescription.ts b/packages/nodes-base/nodes/Intercom/LeadDescription.ts new file mode 100644 index 000000000..e7414ac8b --- /dev/null +++ b/packages/nodes-base/nodes/Intercom/LeadDescription.ts @@ -0,0 +1,521 @@ +import { INodeProperties } from "n8n-workflow"; + +export const leadOpeations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'lead', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new lead', + }, + { + name: 'Get', + value: 'get', + description: 'Get data of a lead', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get data of all leads', + }, + { + name: 'Update', + value: 'update', + description: 'Update new lead', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a lead', + } + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const leadFields = [ + +/* -------------------------------------------------------------------------- */ +/* lead:delete */ +/* -------------------------------------------------------------------------- */ + + { + displayName: 'Delete By', + name: 'deleteBy', + type: 'options', + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'delete', + ], + }, + }, + options: [ + { + name: 'ID', + value: 'id', + description: 'The Intercom defined id representing the Lead', + }, + { + name: 'User ID', + value: 'userId', + description: 'Automatically generated identifier for the Lead', + }, + ], + default: '', + description: 'Delete by', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'delete', + ], + }, + }, + description: 'Delete by value', + }, + +/* -------------------------------------------------------------------------- */ +/* lead:get */ +/* -------------------------------------------------------------------------- */ + + { + displayName: 'Select By', + name: 'selectBy', + type: 'options', + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'get', + ], + }, + }, + options: [ + { + name: 'Email', + value: 'email', + description: 'Email representing the Lead', + }, + { + name: 'ID', + value: 'id', + description: 'The Intercom defined id representing the Lead', + }, + { + name: 'User ID', + value: 'userId', + description: 'Automatically generated identifier for the Lead', + }, + { + name: 'Phone', + value: 'phone', + description: 'Phone representing the Lead', + }, + ], + default: '', + description: 'The property to select the lead by.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'get', + ], + }, + }, + description: 'View by value', + }, + +/* -------------------------------------------------------------------------- */ +/* lead:getAll */ +/* -------------------------------------------------------------------------- */ + + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 60, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + default: {}, + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + description: 'The email address of the lead', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + description: 'The phone number of the lead', + }, + ] + }, + +/* -------------------------------------------------------------------------- */ +/* lead:update */ +/* -------------------------------------------------------------------------- */ + + { + displayName: 'Update By', + name: 'updateBy', + type: 'options', + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + name: 'User ID', + value: 'userId', + description: 'Automatically generated identifier for the Lead', + }, + { + name: 'ID', + value: 'id', + description: 'The Intercom defined id representing the Lead', + }, + ], + default: 'id', + description: 'The property via which to query the lead.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'update', + ], + }, + }, + description: 'Value of the property to identify the lead to update', + }, + +/* -------------------------------------------------------------------------- */ +/* lead:create */ +/* -------------------------------------------------------------------------- */ + + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The email of the user.', + }, + { + displayName: 'JSON Parameters', + name: 'jsonParameters', + type: 'boolean', + default: false, + description: '', + displayOptions: { + show: { + operation: [ + 'create', + 'update', + ], + resource: [ + 'lead', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + 'update', + ], + resource: [ + 'lead', + ], + }, + }, + options: [ + { + displayName: 'Avatar', + name: 'avatar', + type: 'string', + default: '', + description: 'An avatar image URL. note: the image url needs to be https.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'Name of the user', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + description: 'The phone number of the user', + }, + { + displayName: 'Unsubscribed From Emails', + name: 'unsubscribedFromEmails', + type: 'boolean', + default: false, + description: 'Whether the Lead is unsubscribed from emails', + }, + { + displayName: 'Update Last Request At', + name: 'updateLastRequestAt', + type: 'boolean', + default: false, + description: 'A boolean value, which if true, instructs Intercom to update the
users last_request_at value to the current API service time in
UTC. default value if not sent is false.', + }, + { + displayName: 'Companies', + name: 'companies', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getCompanies', + }, + default: [], + description: 'Identifies the companies this user belongs to.', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + displayOptions: { + show: { + '/resource': [ + 'lead', + ], + '/operation': [ + 'update', + ], + }, + }, + description: 'The email of the user.', + }, + { + displayName: 'UTM Source', + name: 'utmSource', + type: 'string', + default: '', + description: 'An avatar image URL. note: the image url needs to be https.', + }, + { + displayName: 'UTM Medium', + name: 'utmMedium', + type: 'string', + default: '', + description: 'Identifies what type of link was used', + }, + { + displayName: 'UTM Campaign', + name: 'utmCampaign', + type: 'string', + default: '', + description: 'Identifies a specific product promotion or strategic campaign', + }, + { + displayName: 'UTM Term', + name: 'utmTerm', + type: 'string', + default: '', + description: 'Identifies search terms', + }, + { + displayName: 'UTM Content', + name: 'utmContent', + type: 'string', + default: '', + description: 'Identifies what specifically was clicked to bring the user to the site', + }, + ] + }, + { + displayName: 'Custom Attributes', + name: 'customAttributesJson', + type: 'json', + required: false, + typeOptions: { + alwaysOpenEditWindow: true, + }, + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'create', + 'update', + ], + jsonParameters: [ + true, + ], + }, + }, + default: '', + description: 'A hash of key/value pairs to represent custom data you want to attribute to a user.', + }, + { + displayName: 'Custom Attributes', + name: 'customAttributesUi', + type: 'fixedCollection', + default: '', + placeholder: 'Add Attribute', + typeOptions: { + multipleValues: true, + }, + required: false, + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'create', + 'update', + ], + jsonParameters: [ + false, + ], + }, + }, + options: [ + { + name: 'customAttributesValues', + displayName: 'Attributes', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + }, + ], + } + ], + description: 'A hash of key/value pairs to represent custom data you want to attribute to a user.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Intercom/LeadInterface.ts b/packages/nodes-base/nodes/Intercom/LeadInterface.ts new file mode 100644 index 000000000..6a716577f --- /dev/null +++ b/packages/nodes-base/nodes/Intercom/LeadInterface.ts @@ -0,0 +1,29 @@ +import { IDataObject } from "n8n-workflow"; + +export interface ILeadCompany { + company_id?: string; +} + +export interface IAvatar { + type?: string; + image_url?: string; +} + +export interface ILead { + user_id?: string; + id?: string; + email?: string; + phone?: string; + name?: string; + custom_attributes?: IDataObject; + companies?: ILeadCompany[]; + last_request_at?: number; + unsubscribed_from_emails?: boolean; + update_last_request_at?: boolean; + avatar?: IAvatar; + utm_source?: string; + utm_medium?: string; + utm_campaign?: string; + utm_term?: string; + utm_content?: string; +} diff --git a/packages/nodes-base/nodes/Intercom/UserDescription.ts b/packages/nodes-base/nodes/Intercom/UserDescription.ts new file mode 100644 index 000000000..b54952b5f --- /dev/null +++ b/packages/nodes-base/nodes/Intercom/UserDescription.ts @@ -0,0 +1,574 @@ +import { INodeProperties } from "n8n-workflow"; + +export const userOpeations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'user', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new user', + }, + { + name: 'Get', + value: 'get', + description: 'Get data of a user', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get data of all users', + }, + { + name: 'Update', + value: 'update', + description: 'Update a user', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a user', + } + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const userFields = [ + +/* -------------------------------------------------------------------------- */ +/* user:delete */ +/* -------------------------------------------------------------------------- */ + + { + displayName: 'ID', + name: 'id', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'user', + ], + operation: [ + 'delete', + ], + }, + }, + default: '', + description: 'The Intercom defined id representing the Lead', + }, + +/* -------------------------------------------------------------------------- */ +/* user:getAll */ +/* -------------------------------------------------------------------------- */ + + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'user', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'user', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 60, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + default: {}, + displayOptions: { + show: { + resource: [ + 'user', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Company ID', + name: 'company_id', + type: 'string', + default: '', + description: 'Company ID representing the user', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + description: 'The email address of the user', + }, + { + displayName: 'Tag ID', + name: 'tag_id', + type: 'string', + default: '', + description: 'Tag representing the user', + }, + { + displayName: 'Segment ID', + name: 'segment_id', + type: 'string', + default: '', + description: 'Segment representing the user', + }, + ], + }, + + +/* -------------------------------------------------------------------------- */ +/* user:get */ +/* -------------------------------------------------------------------------- */ + + { + displayName: 'Select By', + name: 'selectBy', + type: 'options', + displayOptions: { + show: { + resource: [ + 'user', + ], + operation: [ + 'get', + ], + }, + }, + options: [ + { + name: 'ID', + value: 'id', + default: '', + description: 'The Intercom defined id representing the Lead', + }, + { + name: 'User ID', + value: 'userId', + default: '', + description: 'Automatically generated identifier for the Lead', + }, + ], + default: '', + description: 'The property to select the user by.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'user', + ], + operation: [ + 'get', + ], + }, + }, + description: 'View by value', + }, + +/* -------------------------------------------------------------------------- */ +/* user:update */ +/* -------------------------------------------------------------------------- */ + + { + displayName: 'Update By', + name: 'updateBy', + type: 'options', + displayOptions: { + show: { + resource: [ + 'user', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + name: 'ID', + value: 'id', + description: 'The Intercom defined id representing the user', + }, + { + name: 'Email', + value: 'email', + description: 'The email address of user', + }, + { + name: 'User ID', + value: 'userId', + description: 'Automatically generated identifier for the user', + }, + + ], + default: 'id', + description: 'The property via which to query the user.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'user', + ], + operation: [ + 'update', + ], + }, + }, + description: 'Value of the property to identify the user to update', + }, + +/* -------------------------------------------------------------------------- */ +/* user:create */ +/* -------------------------------------------------------------------------- */ + + { + displayName: 'Identifier Type', + name: 'identifierType', + type: 'options', + displayOptions: { + show: { + resource: [ + 'user', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + name: 'User ID', + value: 'userId', + description: 'A unique string identifier for the user. It is required on creation if an email is not supplied.', + }, + { + name: 'Email', + value: 'email', + description: `The user's email address. It is required on creation if a user_id is not supplied.`, + }, + ], + default: '', + description: 'Unique string identifier', + }, + { + displayName: 'Value', + name: 'idValue', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'user', + ], + operation: [ + 'create', + ], + }, + }, + description: 'Unique string identifier value', + }, + { + displayName: 'JSON Parameters', + name: 'jsonParameters', + type: 'boolean', + default: false, + description: '', + displayOptions: { + show: { + operation: [ + 'create', + 'update', + ], + resource: [ + 'user' + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + 'update', + ], + resource: [ + 'user' + ], + }, + }, + options: [ + { + displayName: 'Email', + name: 'email', + displayOptions: { + show: { + '/operation': [ + 'update', + ], + '/resource': [ + 'user' + ], + }, + hide: { + '/updateBy': [ + 'email', + ] + }, + }, + type: 'string', + default: '', + description: 'Email of the user', + }, + { + displayName: 'User ID', + name: 'userId', + displayOptions: { + show: { + '/operation': [ + 'update', + ], + '/resource': [ + 'user' + ], + }, + hide: { + '/updateBy': [ + 'email', + 'userId', + ] + }, + }, + type: 'string', + default: '', + description: 'Email of the user', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + description: 'The phone number of the user', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + placeholder: '', + description: 'Name of the user', + }, + { + displayName: 'Unsubscribed From Emails', + name: 'unsubscribedFromEmails', + type: 'boolean', + default: '', + placeholder: '', + description: 'Whether the user is unsubscribed from emails', + }, + { + displayName: 'Update Last Request At', + name: 'updateLastRequestAt', + type: 'boolean', + default: false, + options: [], + description: 'A boolean value, which if true, instructs Intercom to update the users
last_request_at value to the current API service time in UTC.', + }, + { + displayName: 'Session Count', + name: 'sessionCount', + type: 'number', + default: false, + options: [], + description: `How many sessions the user has recorded`, + }, + { + displayName: 'Companies', + name: 'companies', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getCompanies', + }, + default: [], + description: 'Identifies the companies this user belongs to.', + }, + { + displayName: 'Avatar', + name: 'avatar', + type: 'string', + default: '', + description: 'An avatar image URL. note: the image url needs to be https.', + }, + { + displayName: 'UTM Source', + name: 'utmSource', + type: 'string', + default: '', + description: 'An avatar image URL. note: the image url needs to be https.', + }, + { + displayName: 'UTM Medium', + name: 'utmMedium', + type: 'string', + default: '', + description: 'Identifies what type of link was used', + }, + { + displayName: 'UTM Campaign', + name: 'utmCampaign', + type: 'string', + default: '', + description: 'Identifies a specific product promotion or strategic campaign', + }, + { + displayName: 'UTM Term', + name: 'utmTerm', + type: 'string', + default: '', + description: 'Identifies search terms', + }, + { + displayName: 'UTM Content', + name: 'utmContent', + type: 'string', + default: '', + description: 'Identifies what specifically was clicked to bring the user to the site', + }, + ] + }, + { + displayName: 'Custom Attributes', + name: 'customAttributesJson', + type: 'json', + required: false, + typeOptions: { + alwaysOpenEditWindow: true, + }, + displayOptions: { + show: { + resource: [ + 'user', + ], + operation: [ + 'create', + 'update', + ], + jsonParameters: [ + true, + ], + }, + }, + default: '', + description: 'A hash of key/value pairs to represent custom data you want to attribute to a user.', + }, + { + displayName: 'Custom Attributes', + name: 'customAttributesUi', + type: 'fixedCollection', + default: '', + placeholder: 'Add Attribute', + typeOptions: { + multipleValues: true, + }, + required: false, + displayOptions: { + show: { + resource: [ + 'user', + ], + operation: [ + 'create', + 'update', + ], + jsonParameters: [ + false, + ], + }, + }, + options: [ + { + name: 'customAttributesValues', + displayName: 'Attributes', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + }, + ], + } + ], + description: 'A hash of key/value pairs to represent custom data you want to attribute to a user.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Intercom/UserInterface.ts b/packages/nodes-base/nodes/Intercom/UserInterface.ts new file mode 100644 index 000000000..802941af9 --- /dev/null +++ b/packages/nodes-base/nodes/Intercom/UserInterface.ts @@ -0,0 +1,32 @@ +import { IDataObject } from "n8n-workflow"; + +export interface IUserCompany { + company_id?: string; +} + +export interface IAvatar { + type?: string; + image_url?: string; +} + +export interface IUser { + user_id?: string; + id?: string; + email?: string; + phone?: string; + name?: string; + custom_attributes?: IDataObject; + companies?: IUserCompany[]; + last_request_at?: number; + signed_up_at?: string; + unsubscribed_from_emails?: boolean; + update_last_request_at?: boolean; + last_seen_user_agent?: boolean; + session_count?: number; + avatar?: IAvatar; + utm_source?: string; + utm_medium?: string; + utm_campaign?: string; + utm_term?: string; + utm_content?: string; +} diff --git a/packages/nodes-base/nodes/Intercom/intercom.png b/packages/nodes-base/nodes/Intercom/intercom.png new file mode 100644 index 000000000..5fb79c11e Binary files /dev/null and b/packages/nodes-base/nodes/Intercom/intercom.png differ diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 68d5278e2..1456bf653 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -40,7 +40,8 @@ "dist/credentials/GoogleApi.credentials.js", "dist/credentials/HttpBasicAuth.credentials.js", "dist/credentials/HttpDigestAuth.credentials.js", - "dist/credentials/HttpHeaderAuth.credentials.js", + "dist/credentials/HttpHeaderAuth.credentials.js", + "dist/credentials/IntercomApi.credentials.js", "dist/credentials/Imap.credentials.js", "dist/credentials/LinkFishApi.credentials.js", "dist/credentials/MailchimpApi.credentials.js", @@ -98,7 +99,8 @@ "dist/nodes/GraphQL/GraphQL.node.js", "dist/nodes/HttpRequest.node.js", "dist/nodes/If.node.js", - "dist/nodes/Interval.node.js", + "dist/nodes/Interval.node.js", + "dist/nodes/Intercom/Intercom.node.js", "dist/nodes/LinkFish/LinkFish.node.js", "dist/nodes/Mailchimp/Mailchimp.node.js", "dist/nodes/Mailgun/Mailgun.node.js",