diff --git a/packages/nodes-base/credentials/DriftApi.credentials.ts b/packages/nodes-base/credentials/DriftApi.credentials.ts new file mode 100644 index 0000000000..83f6374c7a --- /dev/null +++ b/packages/nodes-base/credentials/DriftApi.credentials.ts @@ -0,0 +1,18 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class DriftApi implements ICredentialType { + name = 'driftApi'; + displayName = 'Drift API'; + properties = [ + { + displayName: 'Personal Access Token', + name: 'accessToken', + type: 'string' as NodePropertyTypes, + default: '', + description: 'Visit your account details page, and grab the Access Token. See Drift auth.' + }, + ]; +} diff --git a/packages/nodes-base/nodes/Drift/ContactDescription.ts b/packages/nodes-base/nodes/Drift/ContactDescription.ts new file mode 100644 index 0000000000..ad5bc14d5a --- /dev/null +++ b/packages/nodes-base/nodes/Drift/ContactDescription.ts @@ -0,0 +1,206 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const contactOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'contact', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a contact', + }, + { + name: 'Custom Attributes', + value: 'getCustomAttributes', + description: 'Get custom attributes', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a contact', + }, + { + name: 'Get', + value: 'get', + description: 'Get a contact', + }, + { + name: 'Update', + value: 'update', + description: 'Update a contact', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const contactFields = [ + +/* -------------------------------------------------------------------------- */ +/* contact:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Email', + name: 'email', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The email of the Contact', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'The name of the Contact', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + description: 'The phone number associated with the Contact', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* contact:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'update', + ] + }, + }, + description: 'Unique identifier for the contact.', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + description: 'The email of the Contact', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'The name of the Contact', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + description: 'The phone number associated with the Contact', + }, + ] + }, +/* -------------------------------------------------------------------------- */ +/* contact:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'get', + ] + }, + }, + description: 'Unique identifier for the contact.', + }, +/* -------------------------------------------------------------------------- */ +/* contact:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'delete', + ] + }, + }, + description: 'Unique identifier for the contact.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Drift/ContactInterface.ts b/packages/nodes-base/nodes/Drift/ContactInterface.ts new file mode 100644 index 0000000000..d228a1c880 --- /dev/null +++ b/packages/nodes-base/nodes/Drift/ContactInterface.ts @@ -0,0 +1,6 @@ +export interface IContact { + email?: string; + name?: string; + phone?: string; + tags?: string[]; +} diff --git a/packages/nodes-base/nodes/Drift/Drift.node.ts b/packages/nodes-base/nodes/Drift/Drift.node.ts new file mode 100644 index 0000000000..95566dfb60 --- /dev/null +++ b/packages/nodes-base/nodes/Drift/Drift.node.ts @@ -0,0 +1,130 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; +import { + INodeTypeDescription, + INodeType, + INodeExecutionData, + IDataObject, +} from 'n8n-workflow'; +import { + driftApiRequest, +} from './GenericFunctions'; +import { + contactFields, + contactOperations, +} from './ContactDescription'; +import { + IContact, +} from './ContactInterface'; + +export class Drift implements INodeType { + description: INodeTypeDescription = { + displayName: 'Drift', + name: 'drift', + icon: 'file:drift.png', + group: ['output'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume Drift API', + defaults: { + name: 'Drift ', + color: '#404040', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'driftApi', + required: true, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Contact', + value: 'contact', + }, + ], + default: 'contact', + description: 'Resource to consume.', + }, + ...contactOperations, + ...contactFields, + ], + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + const length = items.length as unknown as number; + let responseData; + const qs: IDataObject = {}; + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + for (let i = 0; i < length; i++) { + if (resource === 'contact') { + //https://devdocs.drift.com/docs/creating-a-contact + if (operation === 'create') { + const email = this.getNodeParameter('email', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IContact = { + email, + }; + if (additionalFields.name) { + body.name = additionalFields.name as string; + } + if (additionalFields.phone) { + body.phone = additionalFields.phone as string; + } + responseData = await driftApiRequest.call(this, 'POST', '/contacts', { attributes: body }); + responseData = responseData.data + } + //https://devdocs.drift.com/docs/updating-a-contact + if (operation === 'update') { + const contactId = this.getNodeParameter('contactId', i) as string; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + const body: IContact = {}; + if (updateFields.name) { + body.name = updateFields.name as string; + } + if (updateFields.phone) { + body.phone = updateFields.phone as string; + } + if (updateFields.email) { + body.email = updateFields.email as string; + } + responseData = await driftApiRequest.call(this, 'PATCH', `/contacts/${contactId}`, { attributes: body }); + responseData = responseData.data + } + //https://devdocs.drift.com/docs/retrieving-contact + if (operation === 'get') { + const contactId = this.getNodeParameter('contactId', i) as string; + responseData = await driftApiRequest.call(this, 'GET', `/contacts/${contactId}`); + responseData = responseData.data + } + //https://devdocs.drift.com/docs/listing-custom-attributes + if (operation === 'getCustomAttributes') { + responseData = await driftApiRequest.call(this, 'GET', '/contacts/attributes'); + responseData = responseData.data.properties; + } + //https://devdocs.drift.com/docs/removing-a-contact + if (operation === 'delete') { + const contactId = this.getNodeParameter('contactId', i) as string; + responseData = await driftApiRequest.call(this, 'DELETE', `/contacts/${contactId}`); + responseData = { success: true }; + } + } + 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/Drift/GenericFunctions.ts b/packages/nodes-base/nodes/Drift/GenericFunctions.ts new file mode 100644 index 0000000000..3f798398c4 --- /dev/null +++ b/packages/nodes-base/nodes/Drift/GenericFunctions.ts @@ -0,0 +1,50 @@ +import { OptionsWithUri } from 'request'; + +import { + IExecuteFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import { + IDataObject, + IHookFunctions, + IWebhookFunctions +} from 'n8n-workflow'; + +export async function driftApiRequest(this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, query: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const credentials = this.getCredentials('driftApi'); + + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + const endpoint = 'https://driftapi.com'; + + let options: OptionsWithUri = { + headers: { + Authorization: `Bearer ${credentials.accessToken}`, + }, + method, + body, + qs: query, + uri: uri || `${endpoint}${resource}`, + json: true + }; + if (!Object.keys(body).length) { + delete options.form; + } + if (!Object.keys(query).length) { + delete options.qs; + } + options = Object.assign({}, options, option); + try { + return await this.helpers.request!(options); + } catch (error) { + if (error.response) { + const errorMessage = error.message || (error.response.body && error.response.body.message ) + throw new Error(`Drift error response [${error.statusCode}]: ${errorMessage}`); + } + throw error; + } +} diff --git a/packages/nodes-base/nodes/Drift/drift.png b/packages/nodes-base/nodes/Drift/drift.png new file mode 100644 index 0000000000..82050d2ca8 Binary files /dev/null and b/packages/nodes-base/nodes/Drift/drift.png differ diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 876d16ff62..a99c9db2e3 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -42,6 +42,7 @@ "dist/credentials/CopperApi.credentials.js", "dist/credentials/DisqusApi.credentials.js", "dist/credentials/DropboxApi.credentials.js", + "dist/credentials/DriftApi.credentials.js", "dist/credentials/EventbriteApi.credentials.js", "dist/credentials/FreshdeskApi.credentials.js", "dist/credentials/FileMaker.credentials.js", @@ -129,6 +130,7 @@ "dist/nodes/Disqus/Disqus.node.js", "dist/nodes/Dropbox/Dropbox.node.js", "dist/nodes/DateTime.node.js", + "dist/nodes/Drift/Drift.node.js", "dist/nodes/EditImage.node.js", "dist/nodes/EmailReadImap.node.js", "dist/nodes/EmailSend.node.js",