From 5b00ef71587218a8e5871d29a63ac6e40a433979 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Tue, 25 Feb 2020 14:50:42 -0500 Subject: [PATCH 1/3] :sparkles: Affinity node and trigger --- .../credentials/AffinityApi.credentials.ts | 17 + .../nodes/Affinity/Affinity.node.ts | 267 +++++++++++++ .../nodes/Affinity/AffinityTrigger.node.ts | 268 +++++++++++++ .../nodes/Affinity/GenericFunctions.ts | 95 +++++ .../nodes/Affinity/OrganizationDescription.ts | 326 +++++++++++++++ .../nodes/Affinity/OrganizationInterface.ts | 6 + .../nodes/Affinity/PersonDescription.ts | 370 ++++++++++++++++++ .../nodes/Affinity/PersonInterface.ts | 7 + .../nodes-base/nodes/Affinity/affinity.png | Bin 0 -> 6429 bytes packages/nodes-base/package.json | 5 +- 10 files changed, 1360 insertions(+), 1 deletion(-) create mode 100644 packages/nodes-base/credentials/AffinityApi.credentials.ts create mode 100644 packages/nodes-base/nodes/Affinity/Affinity.node.ts create mode 100644 packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts create mode 100644 packages/nodes-base/nodes/Affinity/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Affinity/OrganizationDescription.ts create mode 100644 packages/nodes-base/nodes/Affinity/OrganizationInterface.ts create mode 100644 packages/nodes-base/nodes/Affinity/PersonDescription.ts create mode 100644 packages/nodes-base/nodes/Affinity/PersonInterface.ts create mode 100644 packages/nodes-base/nodes/Affinity/affinity.png diff --git a/packages/nodes-base/credentials/AffinityApi.credentials.ts b/packages/nodes-base/credentials/AffinityApi.credentials.ts new file mode 100644 index 0000000000..eb0ef3db0a --- /dev/null +++ b/packages/nodes-base/credentials/AffinityApi.credentials.ts @@ -0,0 +1,17 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class AffinityApi implements ICredentialType { + name = 'affinityApi'; + displayName = 'Affinity API'; + properties = [ + { + displayName: 'API Key', + name: 'apiKey', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Affinity/Affinity.node.ts b/packages/nodes-base/nodes/Affinity/Affinity.node.ts new file mode 100644 index 0000000000..2b024c7517 --- /dev/null +++ b/packages/nodes-base/nodes/Affinity/Affinity.node.ts @@ -0,0 +1,267 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; +import { + IDataObject, + ILoadOptionsFunctions, + INodeTypeDescription, + INodeExecutionData, + INodeType, + INodePropertyOptions, +} from 'n8n-workflow'; +import { + affinityApiRequest, + affinityApiRequestAllItems, +} from './GenericFunctions'; +import { + organizationFields, + organizationOperations, +} from './OrganizationDescription'; +import { + personFields, + personOperations, +} from './PersonDescription'; +import { + IOrganization, +} from './OrganizationInterface'; +import { + IPerson, +} from './PersonInterface'; + +import { snakeCase } from 'change-case'; + +export class Affinity implements INodeType { + description: INodeTypeDescription = { + displayName: 'Affinity', + name: 'affinity', + icon: 'file:affinity.png', + group: ['output'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume Affinity API', + defaults: { + name: 'Affinity', + color: '#3343df', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'affinityApi', + required: true, + } + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Organization', + value: 'organization', + }, + { + name: 'Person', + value: 'person', + }, + ], + default: 'organization', + description: 'Resource to consume.', + }, + ...organizationOperations, + ...organizationFields, + ...personOperations, + ...personFields, + ], + }; + + methods = { + loadOptions: { + // Get all the available organizations to display them to user so that he can + // select them easily + async getOrganizations(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const organizations = await affinityApiRequestAllItems.call(this, 'organizations', 'GET', '/organizations', {}); + for (const organization of organizations) { + const organizationName = organization.name; + const organizationId = organization.id; + returnData.push({ + name: organizationName, + value: organizationId, + }); + } + return returnData; + }, + // Get all the available persons to display them to user so that he can + // select them easily + async getPersons(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const persons = await affinityApiRequestAllItems.call(this, 'persons', 'GET', '/persons', {}); + for (const person of persons) { + let personName = `${person.first_name} ${person.last_name}`; + if (person.primary_email !== null) { + personName+= ` (${person.primary_email})` + } + const personId = person.id; + returnData.push({ + name: personName, + value: personId, + }); + } + return returnData; + }, + }, + }; + + 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 === 'person') { + //https://api-docs.affinity.co/#create-a-new-person + if (operation === 'create') { + const firstName = this.getNodeParameter('firstName', i) as string; + const lastName = this.getNodeParameter('lastName', i) as string; + const emails = this.getNodeParameter('emails', i) as string[]; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IPerson = { + first_name: firstName, + last_name: lastName, + emails, + }; + if (additionalFields.organizations) { + body.organization_ids = additionalFields.organizations as number[]; + } + responseData = await affinityApiRequest.call(this, 'POST', '/persons', body); + } + //https://api-docs.affinity.co/#update-a-person + if (operation === 'update') { + const personId = this.getNodeParameter('personId', i) as number; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + const emails = this.getNodeParameter('emails', i) as string[]; + const body: IPerson = { + emails, + }; + if (updateFields.firstName) { + body.first_name = updateFields.firstName as string; + } + if (updateFields.lastName) { + body.last_name = updateFields.lastName as string; + } + if (updateFields.organizations) { + body.organization_ids = updateFields.organizations as number[]; + } + responseData = await affinityApiRequest.call(this, 'PUT', `/persons/${personId}`, body); + } + //https://api-docs.affinity.co/#get-a-specific-person + if (operation === 'get') { + const personId = this.getNodeParameter('personId', i) as number; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.withInteractionDates) { + qs.with_interaction_dates = options.withInteractionDates as boolean; + } + responseData = await affinityApiRequest.call(this,'GET', `/persons/${personId}`, {}, qs); + } + //https://api-docs.affinity.co/#search-for-persons + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.term) { + qs.term = options.term as string; + } + if (options.withInteractionDates) { + qs.with_interaction_dates = options.withInteractionDates as boolean; + } + if (returnAll === true) { + responseData = await affinityApiRequestAllItems.call(this, 'persons', 'GET', '/persons', {}, qs); + } else { + qs.page_size = this.getNodeParameter('limit', i) as number; + responseData = await affinityApiRequest.call(this, 'GET', '/persons', {}, qs); + responseData = responseData.persons; + } + } + //https://api-docs.affinity.co/#delete-a-person + if (operation === 'delete') { + const personId = this.getNodeParameter('personId', i) as number; + responseData = await affinityApiRequest.call(this, 'DELETE', `/persons/${personId}`, {}, qs); + } + } + if (resource === 'organization') { + //https://api-docs.affinity.co/#create-a-new-organization + if (operation === 'create') { + const name = this.getNodeParameter('name', i) as string; + const domain = this.getNodeParameter('domain', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IOrganization = { + name, + domain, + }; + if (additionalFields.persons) { + body.person_ids = additionalFields.persons as number[]; + } + responseData = await affinityApiRequest.call(this, 'POST', '/organizations', body); + } + //https://api-docs.affinity.co/#update-an-organization + if (operation === 'update') { + const organizationId = this.getNodeParameter('organizationId', i) as number; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + const body: IOrganization = {}; + if (updateFields.name) { + body.name = updateFields.name as string; + } + if (updateFields.domain) { + body.domain = updateFields.domain as string; + } + if (updateFields.persons) { + body.person_ids = updateFields.persons as number[]; + } + responseData = await affinityApiRequest.call(this, 'PUT', `/organizations/${organizationId}`, body); + } + //https://api-docs.affinity.co/#get-a-specific-organization + if (operation === 'get') { + const organizationId = this.getNodeParameter('organizationId', i) as number; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.withInteractionDates) { + qs.with_interaction_dates = options.withInteractionDates as boolean; + } + responseData = await affinityApiRequest.call(this,'GET', `/organizations/${organizationId}`, {}, qs); + } + //https://api-docs.affinity.co/#search-for-organizations + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.term) { + qs.term = options.term as string; + } + if (options.withInteractionDates) { + qs.with_interaction_dates = options.withInteractionDates as boolean; + } + if (returnAll === true) { + responseData = await affinityApiRequestAllItems.call(this, 'organizations', 'GET', '/organizations', {}, qs); + } else { + qs.page_size = this.getNodeParameter('limit', i) as number; + responseData = await affinityApiRequest.call(this, 'GET', '/organizations', {}, qs); + responseData = responseData.organizations; + } + } + //https://api-docs.affinity.co/#delete-an-organization + if (operation === 'delete') { + const organizationId = this.getNodeParameter('organizationId', i) as number; + responseData = await affinityApiRequest.call(this, 'DELETE', `/organizations/${organizationId}`, {}, qs); + } + } + 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/Affinity/AffinityTrigger.node.ts b/packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts new file mode 100644 index 0000000000..1cce53de57 --- /dev/null +++ b/packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts @@ -0,0 +1,268 @@ +import { + IHookFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { + INodeTypeDescription, + INodeType, + IWebhookResponseData, + IDataObject, +} from 'n8n-workflow'; + +import { + affinityApiRequest, + eventsExist, + mapResource, +} from './GenericFunctions'; + +export class AffinityTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'Affinity Trigger', + name: 'affinityTrigger', + icon: 'file:affinity.png', + group: ['trigger'], + version: 1, + description: 'Handle Affinity events via webhooks', + defaults: { + name: 'Affinity Trigger', + color: '#3343df', + }, + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'affinityApi', + required: true, + }, + ], + webhooks: [ + { + name: 'default', + httpMethod: 'POST', + responseMode: 'onReceived', + path: 'webhook', + }, + ], + properties: [ + { + displayName: 'Events', + name: 'events', + type: 'multiOptions', + options: [ + { + name: 'file.created', + value: 'file.deleted', + }, + { + name: 'file.created', + value: 'file.deleted', + }, + { + name: 'field_value.created', + value: 'field_value.created', + }, + { + name: 'field_value.updated', + value: 'field_value.updated', + }, + { + name: 'field_value.deleted', + value: 'field_value.deleted', + }, + { + name: 'field.created', + value: 'field.created', + }, + { + name: 'field.updated', + value: 'field.updated', + }, + { + name: 'field.deleted', + value: 'field.deleted', + }, + { + name: 'list.created', + value: 'list.created', + }, + { + name: 'list.updated', + value: 'list.updated', + }, + { + name: 'list.deleted', + value: 'list.deleted', + }, + { + name: 'list_entry.created', + value: 'list_entry.created', + }, + { + name: 'list_entry.updated', + value: 'list_entry.updated', + }, + { + name: 'list_entry.deleted', + value: 'list_entry.deleted', + }, + { + name: 'note.created', + value: 'note.created', + }, + { + name: 'note.updated', + value: 'note.updated', + }, + { + name: 'note.deleted', + value: 'note.deleted', + }, + { + name: 'organization.created', + value: 'organization.created', + }, + { + name: 'organization.updated', + value: 'organization.updated', + }, + { + name: 'organization.deleted', + value: 'organization.deleted', + }, + { + name: 'opportunity.created', + value: 'opportunity.created', + }, + { + name: 'opportunity.updated', + value: 'opportunity.updated', + }, + { + name: 'opportunity.deleted', + value: 'organization.deleted', + }, + { + name: 'person.created', + value: 'person.created', + }, + { + name: 'person.updated', + value: 'person.updated', + }, + { + name: 'person.deleted', + value: 'person.deleted', + }, + ], + default: [], + required: true, + description: 'Webhook events that will be enabled for that endpoint.', + }, + { + displayName: 'Resolve Data', + name: 'resolveData', + type: 'boolean', + default: true, + description: 'By default does the webhook-data only contain the ID of the object.
If this option gets activated it will resolve the data automatically.', + }, + ], + + }; + + // @ts-ignore (because of request) + webhookMethods = { + default: { + async checkExists(this: IHookFunctions): Promise { + // Check all the webhooks which exist already if it is identical to the + // one that is supposed to get created. + const endpoint = '/webhook'; + + const responseData = await affinityApiRequest.call(this, 'GET', endpoint, {}); + + const webhookUrl = this.getNodeWebhookUrl('default'); + + const events = this.getNodeParameter('events') as string[]; + + for (const webhook of responseData) { + if (eventsExist(webhook.subscriptions, events) && webhook.webhook_url === webhookUrl) { + // Set webhook-id to be sure that it can be deleted + const webhookData = this.getWorkflowStaticData('node'); + webhookData.webhookId = webhook.id as string; + return true; + } + } + return false; + }, + async create(this: IHookFunctions): Promise { + const webhookUrl = this.getNodeWebhookUrl('default'); + + const events = this.getNodeParameter('events') as string[]; + + const endpoint = '/webhook/subscribe'; + + const body = { + webhook_url: webhookUrl, + subscriptions: events, + }; + + const responseData = await affinityApiRequest.call(this, 'POST', endpoint, body); + + if (responseData.id === undefined) { + // Required data is missing so was not successful + return false; + } + + const webhookData = this.getWorkflowStaticData('node'); + webhookData.webhookId = responseData.id as string; + return true; + }, + async delete(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + if (webhookData.webhookId !== undefined) { + + const endpoint = `/webhook/${webhookData.webhookId}`; + + const responseData = await affinityApiRequest.call(this, 'DELETE', endpoint); + + if (!responseData.success) { + return false; + } + // Remove from the static workflow data so that it is clear + // that no webhooks are registred anymore + delete webhookData.webhookId; + } + return true; + }, + }, + }; + + async webhook(this: IWebhookFunctions): Promise { + const bodyData = this.getBodyData(); + const resolveData = this.getNodeParameter('resolveData', false) as boolean; + if (resolveData === false) { + // Return the data as it got received + return { + workflowData: [ + this.helpers.returnJsonArray(bodyData), + ], + }; + } + + let responseData: IDataObject = {}; + + if (bodyData.type && bodyData.body) { + const resource = (bodyData.type as string).split('.')[0] + //@ts-ignore + const id = bodyData.body.id; + responseData = await affinityApiRequest.call(this, 'GET', `/${mapResource(resource)}/${id}`); + responseData.type = bodyData.type; + } + + return { + workflowData: [ + this.helpers.returnJsonArray(responseData), + ], + }; + } +} diff --git a/packages/nodes-base/nodes/Affinity/GenericFunctions.ts b/packages/nodes-base/nodes/Affinity/GenericFunctions.ts new file mode 100644 index 0000000000..76f73143ee --- /dev/null +++ b/packages/nodes-base/nodes/Affinity/GenericFunctions.ts @@ -0,0 +1,95 @@ +import { OptionsWithUri } from 'request'; + +import { + IExecuteFunctions, + ILoadOptionsFunctions, + BINARY_ENCODING +} from 'n8n-core'; + +import { IDataObject, IHookFunctions, IWebhookFunctions } from 'n8n-workflow'; + +export async function affinityApiRequest(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('affinityApi'); + + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + const apiKey = `:${credentials.apiKey}`; + + const endpoint = 'https://api.affinity.co'; + + let options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json', + Authorization: `Basic ${Buffer.from(apiKey).toString(BINARY_ENCODING)}`, + }, + method, + body, + qs: query, + uri: uri || `${endpoint}${resource}`, + json: true + }; + if (!Object.keys(body).length) { + delete options.body; + } + 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) { + let errorMessage = error.response.body.message || error.response.body.description || error.message; + throw new Error(`Affinity error response [${error.statusCode}]: ${errorMessage}`); + } + throw error; + } +} + +export async function affinityApiRequestAllItems(this: IHookFunctions | ILoadOptionsFunctions | IExecuteFunctions, propertyName: string, method: string, resource: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + + query.page_size = 500; + + let uri: string | undefined; + + do { + responseData = await affinityApiRequest.call(this, method, resource, body, query, uri); + // @ts-ignore + query.page_token = responseData.page_token; + returnData.push.apply(returnData, responseData[propertyName]); + } while ( + responseData.page_token !== undefined && + responseData.page_token !== null + ); + + return returnData; +} + +export function eventsExist(subscriptions: string[], currentSubsriptions: string[]) { + for (const subscription of currentSubsriptions) { + if (!subscriptions.includes(subscription)) { + return false + } + } + return true; +} + +export function mapResource(key: string) { + //@ts-ignore + return { + person: 'persons', + list: 'lists', + note: 'notes', + organization: 'organizatitons', + list_entry: 'list-entries', + field: 'fields', + file: 'files', + }[key] +} diff --git a/packages/nodes-base/nodes/Affinity/OrganizationDescription.ts b/packages/nodes-base/nodes/Affinity/OrganizationDescription.ts new file mode 100644 index 0000000000..047afba290 --- /dev/null +++ b/packages/nodes-base/nodes/Affinity/OrganizationDescription.ts @@ -0,0 +1,326 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const organizationOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'organization', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a organization', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a organization', + }, + { + name: 'Get', + value: 'get', + description: 'Get a organization', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all organizations', + }, + { + name: 'Update', + value: 'update', + description: 'Update a organization', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const organizationFields = [ + +/* -------------------------------------------------------------------------- */ +/* organization:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Name', + name: 'name', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'organization', + ], + operation: [ + 'create', + ] + }, + }, + description: 'The name of the organization.', + }, + { + displayName: 'Domain', + name: 'domain', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'organization', + ], + operation: [ + 'create', + ] + }, + }, + description: 'The domain name of the organization.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'organization', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Persons', + name: 'persons', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getPersons', + }, + default: [], + description: 'Persons that the new organization will be associated with.', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* organization:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Organization ID', + name: 'organizationId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'organization', + ], + operation: [ + 'update', + ] + }, + }, + description: 'Unique identifier for the organization.', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'organization', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Domain', + name: 'domain', + type: 'string', + default: '', + description: 'The domain name of the organization.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'The name of the organization.', + }, + { + displayName: 'Persons', + name: 'persons', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getPersons', + }, + default: [], + description: 'Persons that the new organization will be associated with.', + }, + ] + }, +/* -------------------------------------------------------------------------- */ +/* organization:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Organization ID', + name: 'organizationId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'organization', + ], + operation: [ + 'get', + ] + }, + }, + description: 'Unique identifier for the organization.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'organization', + ], + operation: [ + 'get', + ], + }, + }, + options: [ + { + displayName: 'With Interaction Dates', + name: 'withInteractionDates', + type: 'boolean', + default: false, + description: 'When true, interaction dates will be present on the returned resources.', + }, + ] + }, +/* -------------------------------------------------------------------------- */ +/* organization:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'organization', + ], + 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: [ + 'organization', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 10, + }, + default: 5, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'organization', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Term', + name: 'term', + type: 'string', + default: '', + description: 'A string used to search all the organizations in your team’s address book. This could be an email address, a first name or a last name.', + }, + { + displayName: 'With Interaction Dates', + name: 'withInteractionDates', + type: 'boolean', + default: false, + description: 'When true, interaction dates will be present on the returned resources.', + }, + ] + }, +/* -------------------------------------------------------------------------- */ +/* organization:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Organization ID', + name: 'organizationId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'organization', + ], + operation: [ + 'delete', + ] + }, + }, + description: 'Unique identifier for the organization.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Affinity/OrganizationInterface.ts b/packages/nodes-base/nodes/Affinity/OrganizationInterface.ts new file mode 100644 index 0000000000..ae29e598ae --- /dev/null +++ b/packages/nodes-base/nodes/Affinity/OrganizationInterface.ts @@ -0,0 +1,6 @@ + +export interface IOrganization { + name?: string; + domain?: string; + person_ids?: number[]; +} diff --git a/packages/nodes-base/nodes/Affinity/PersonDescription.ts b/packages/nodes-base/nodes/Affinity/PersonDescription.ts new file mode 100644 index 0000000000..5cd2c97d22 --- /dev/null +++ b/packages/nodes-base/nodes/Affinity/PersonDescription.ts @@ -0,0 +1,370 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const personOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'person', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a person', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a person', + }, + { + name: 'Get', + value: 'get', + description: 'Get a person', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all persons', + }, + { + name: 'Update', + value: 'update', + description: 'Update a person', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const personFields = [ + +/* -------------------------------------------------------------------------- */ +/* person:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Fist Name', + name: 'firstName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'person', + ], + operation: [ + 'create', + ] + }, + }, + description: 'The first name of the person.', + }, + { + displayName: 'Last Name', + name: 'lastName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'person', + ], + operation: [ + 'create', + ] + }, + }, + description: 'The last name of the person.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'person', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Organizations', + name: 'organizations', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getOrganizations', + }, + default: [], + description: 'Organizations that the person is associated with.', + }, + ], + }, + { + displayName: 'Emails', + name: 'emails', + type: 'string', + description: 'The email addresses of the person', + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add To Email', + }, + displayOptions: { + show: { + resource: [ + 'person', + ], + operation: [ + 'create', + ] + }, + }, + placeholder: 'info@example.com', + default: [], + }, +/* -------------------------------------------------------------------------- */ +/* person:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Person ID', + name: 'personId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'person', + ], + operation: [ + 'update', + ] + }, + }, + description: 'Unique identifier for the person.', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'person', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Fist Name', + name: 'firstName', + type: 'string', + default: '', + description: 'The first name of the person.', + }, + { + displayName: 'Last Name', + name: 'lastName', + type: 'string', + default: '', + description: 'The last name of the person.', + }, + { + displayName: 'Organizations', + name: 'organizations', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getOrganizations', + }, + default: [], + description: 'Organizations that the person is associated with.', + }, + ] + }, + { + displayName: 'Emails', + name: 'emails', + type: 'string', + description: 'The email addresses of the person', + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add To Email', + }, + displayOptions: { + show: { + resource: [ + 'person', + ], + operation: [ + 'update', + ] + }, + }, + placeholder: 'info@example.com', + default: [], + }, +/* -------------------------------------------------------------------------- */ +/* person:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Person ID', + name: 'personId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'person', + ], + operation: [ + 'get', + ] + }, + }, + description: 'Unique identifier for the person.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'person', + ], + operation: [ + 'get', + ], + }, + }, + options: [ + { + displayName: 'With Interaction Dates', + name: 'withInteractionDates', + type: 'boolean', + default: false, + description: 'When true, interaction dates will be present on the returned resources.', + }, + ] + }, +/* -------------------------------------------------------------------------- */ +/* person:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'person', + ], + 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: [ + 'person', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 10, + }, + default: 5, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'person', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Term', + name: 'term', + type: 'string', + default: '', + description: 'A string used to search all the persons in your team’s address book. This could be an email address, a first name or a last name.', + }, + { + displayName: 'With Interaction Dates', + name: 'withInteractionDates', + type: 'boolean', + default: false, + description: 'When true, interaction dates will be present on the returned resources.', + }, + ] + }, +/* -------------------------------------------------------------------------- */ +/* person:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Person ID', + name: 'personId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'person', + ], + operation: [ + 'delete', + ] + }, + }, + description: 'Unique identifier for the person.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Affinity/PersonInterface.ts b/packages/nodes-base/nodes/Affinity/PersonInterface.ts new file mode 100644 index 0000000000..3bf495d3fa --- /dev/null +++ b/packages/nodes-base/nodes/Affinity/PersonInterface.ts @@ -0,0 +1,7 @@ + +export interface IPerson { + first_name?: string; + last_name?: string; + emails?: string[]; + organization_ids?: number[]; +} diff --git a/packages/nodes-base/nodes/Affinity/affinity.png b/packages/nodes-base/nodes/Affinity/affinity.png new file mode 100644 index 0000000000000000000000000000000000000000..e097a35e9689a4bb17e37ea8e1f019edac1f6cd1 GIT binary patch literal 6429 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 Date: Tue, 25 Feb 2020 18:28:41 -0500 Subject: [PATCH 2/3] :zap: handle verification request --- packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts b/packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts index 1cce53de57..230afba160 100644 --- a/packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts +++ b/packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts @@ -240,6 +240,11 @@ export class AffinityTrigger implements INodeType { async webhook(this: IWebhookFunctions): Promise { const bodyData = this.getBodyData(); const resolveData = this.getNodeParameter('resolveData', false) as boolean; + + if (bodyData.type === 'sample.webhook') { + return {} + } + if (resolveData === false) { // Return the data as it got received return { From 4560d02823674f71e2a78843691fabb486348dd7 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sun, 8 Mar 2020 22:16:06 +0100 Subject: [PATCH 3/3] :zap: Small improvements to Affinity-Node --- .../nodes/Affinity/Affinity.node.ts | 2 +- .../nodes/Affinity/AffinityTrigger.node.ts | 25 ++----------------- .../nodes/Affinity/GenericFunctions.ts | 12 ++++----- .../nodes/Affinity/OrganizationDescription.ts | 8 +++--- 4 files changed, 12 insertions(+), 35 deletions(-) diff --git a/packages/nodes-base/nodes/Affinity/Affinity.node.ts b/packages/nodes-base/nodes/Affinity/Affinity.node.ts index 2b024c7517..972c2e7ec3 100644 --- a/packages/nodes-base/nodes/Affinity/Affinity.node.ts +++ b/packages/nodes-base/nodes/Affinity/Affinity.node.ts @@ -101,7 +101,7 @@ export class Affinity implements INodeType { for (const person of persons) { let personName = `${person.first_name} ${person.last_name}`; if (person.primary_email !== null) { - personName+= ` (${person.primary_email})` + personName+= ` (${person.primary_email})`; } const personId = person.id; returnData.push({ diff --git a/packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts b/packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts index 230afba160..828b3ce1da 100644 --- a/packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts +++ b/packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts @@ -98,10 +98,6 @@ export class AffinityTrigger implements INodeType { name: 'list_entry.created', value: 'list_entry.created', }, - { - name: 'list_entry.updated', - value: 'list_entry.updated', - }, { name: 'list_entry.deleted', value: 'list_entry.deleted', @@ -159,13 +155,6 @@ export class AffinityTrigger implements INodeType { required: true, description: 'Webhook events that will be enabled for that endpoint.', }, - { - displayName: 'Resolve Data', - name: 'resolveData', - type: 'boolean', - default: true, - description: 'By default does the webhook-data only contain the ID of the object.
If this option gets activated it will resolve the data automatically.', - }, ], }; @@ -239,25 +228,15 @@ export class AffinityTrigger implements INodeType { async webhook(this: IWebhookFunctions): Promise { const bodyData = this.getBodyData(); - const resolveData = this.getNodeParameter('resolveData', false) as boolean; if (bodyData.type === 'sample.webhook') { - return {} - } - - if (resolveData === false) { - // Return the data as it got received - return { - workflowData: [ - this.helpers.returnJsonArray(bodyData), - ], - }; + return {}; } let responseData: IDataObject = {}; if (bodyData.type && bodyData.body) { - const resource = (bodyData.type as string).split('.')[0] + const resource = (bodyData.type as string).split('.')[0]; //@ts-ignore const id = bodyData.body.id; responseData = await affinityApiRequest.call(this, 'GET', `/${mapResource(resource)}/${id}`); diff --git a/packages/nodes-base/nodes/Affinity/GenericFunctions.ts b/packages/nodes-base/nodes/Affinity/GenericFunctions.ts index 76f73143ee..0c2f680613 100644 --- a/packages/nodes-base/nodes/Affinity/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Affinity/GenericFunctions.ts @@ -42,8 +42,8 @@ export async function affinityApiRequest(this: IExecuteFunctions | IWebhookFunct return await this.helpers.request!(options); } catch (error) { if (error.response) { - let errorMessage = error.response.body.message || error.response.body.description || error.message; - throw new Error(`Affinity error response [${error.statusCode}]: ${errorMessage}`); + const errorMessage = error.response.body.message || error.response.body.description || error.message; + throw new Error(`Affinity error response: ${errorMessage}`); } throw error; } @@ -57,10 +57,8 @@ export async function affinityApiRequestAllItems(this: IHookFunctions | ILoadOpt query.page_size = 500; - let uri: string | undefined; - do { - responseData = await affinityApiRequest.call(this, method, resource, body, query, uri); + responseData = await affinityApiRequest.call(this, method, resource, body, query); // @ts-ignore query.page_token = responseData.page_token; returnData.push.apply(returnData, responseData[propertyName]); @@ -75,7 +73,7 @@ export async function affinityApiRequestAllItems(this: IHookFunctions | ILoadOpt export function eventsExist(subscriptions: string[], currentSubsriptions: string[]) { for (const subscription of currentSubsriptions) { if (!subscriptions.includes(subscription)) { - return false + return false; } } return true; @@ -91,5 +89,5 @@ export function mapResource(key: string) { list_entry: 'list-entries', field: 'fields', file: 'files', - }[key] + }[key]; } diff --git a/packages/nodes-base/nodes/Affinity/OrganizationDescription.ts b/packages/nodes-base/nodes/Affinity/OrganizationDescription.ts index 047afba290..8e8a72f2bd 100644 --- a/packages/nodes-base/nodes/Affinity/OrganizationDescription.ts +++ b/packages/nodes-base/nodes/Affinity/OrganizationDescription.ts @@ -16,17 +16,17 @@ export const organizationOperations = [ { name: 'Create', value: 'create', - description: 'Create a organization', + description: 'Create an organization', }, { name: 'Delete', value: 'delete', - description: 'Delete a organization', + description: 'Delete an organization', }, { name: 'Get', value: 'get', - description: 'Get a organization', + description: 'Get an organization', }, { name: 'Get All', @@ -36,7 +36,7 @@ export const organizationOperations = [ { name: 'Update', value: 'update', - description: 'Update a organization', + description: 'Update an organization', }, ], default: 'create',