diff --git a/packages/nodes-base/nodes/Affinity/Affinity.node.ts b/packages/nodes-base/nodes/Affinity/Affinity.node.ts index 7efbbe4cf6..e422214db3 100644 --- a/packages/nodes-base/nodes/Affinity/Affinity.node.ts +++ b/packages/nodes-base/nodes/Affinity/Affinity.node.ts @@ -1,6 +1,7 @@ import { IExecuteFunctions, } from 'n8n-core'; + import { IDataObject, ILoadOptionsFunctions, @@ -9,27 +10,40 @@ import { INodeType, INodeTypeDescription, } from 'n8n-workflow'; + import { affinityApiRequest, affinityApiRequestAllItems, } from './GenericFunctions'; + import { organizationFields, organizationOperations, } from './OrganizationDescription'; + import { personFields, personOperations, } from './PersonDescription'; + +import { + listFields, + listOperations, +} from './ListDescription'; + +import { + listEntryFields, + listEntryOperations, +} from './ListEntryDescription'; + import { IOrganization, } from './OrganizationInterface'; + import { IPerson, } from './PersonInterface'; -import { snakeCase } from 'change-case'; - export class Affinity implements INodeType { description: INodeTypeDescription = { displayName: 'Affinity', @@ -57,6 +71,14 @@ export class Affinity implements INodeType { name: 'resource', type: 'options', options: [ + { + name: 'List', + value: 'list', + }, + { + name: 'List Entry', + value: 'listEntry', + }, { name: 'Organization', value: 'organization', @@ -69,6 +91,10 @@ export class Affinity implements INodeType { default: 'organization', description: 'Resource to consume.', }, + ...listOperations, + ...listFields, + ...listEntryOperations, + ...listEntryFields, ...organizationOperations, ...organizationFields, ...personOperations, @@ -101,7 +127,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({ @@ -111,6 +137,19 @@ export class Affinity implements INodeType { } return returnData; }, + // Get all the available lists to display them to user so that he can + // select them easily + async getLists(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const lists = await affinityApiRequest.call(this, 'GET', `/lists`); + for (const list of lists) { + returnData.push({ + name: list.name, + value: list.id, + }); + } + return returnData; + }, }, }; @@ -123,6 +162,59 @@ export class Affinity implements INodeType { 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 === 'list') { + //https://api-docs.affinity.co/#get-a-specific-list + if (operation === 'get') { + const listId = this.getNodeParameter('listId', i) as string; + responseData = await affinityApiRequest.call(this, 'GET', `/lists/${listId}`, {}, qs); + } + //https://api-docs.affinity.co/#get-all-lists + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + responseData = await affinityApiRequest.call(this, 'GET', `/lists`, {}, qs); + if (returnAll === false) { + const limit = this.getNodeParameter('limit', i) as number; + responseData = responseData.splice(0, limit); + } + } + } + if (resource === 'listEntry') { + //https://api-docs.affinity.co/#create-a-new-list-entry + if (operation === 'create') { + const listId = this.getNodeParameter('listId', i) as string; + const entityId = this.getNodeParameter('entityId', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IDataObject = { + entity_id: parseInt(entityId, 10), + }; + Object.assign(body, additionalFields); + responseData = await affinityApiRequest.call(this, 'POST', `/lists/${listId}/list-entries`, body); + } + //https://api-docs.affinity.co/#get-a-specific-list-entry + if (operation === 'get') { + const listId = this.getNodeParameter('listId', i) as string; + const listEntryId = this.getNodeParameter('listEntryId', i) as string; + responseData = await affinityApiRequest.call(this, 'GET', `/lists/${listId}/list-entries/${listEntryId}`, {}, qs); + } + //https://api-docs.affinity.co/#get-all-list-entries + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const listId = this.getNodeParameter('listId', i) as string; + if (returnAll === true) { + responseData = await affinityApiRequestAllItems.call(this, 'list_entries', 'GET', `/lists/${listId}/list-entries`, {}, qs); + } else { + qs.page_size = this.getNodeParameter('limit', i) as number; + responseData = await affinityApiRequest.call(this, 'GET', `/lists/${listId}/list-entries`, {}, qs); + responseData = responseData.list_entries; + } + } + //https://api-docs.affinity.co/#delete-a-specific-list-entry + if (operation === 'delete') { + const listId = this.getNodeParameter('listId', i) as string; + const listEntryId = this.getNodeParameter('listEntryId', i) as string; + responseData = await affinityApiRequest.call(this, 'DELETE', `/lists/${listId}/list-entries/${listEntryId}`, {}, qs); + } + } if (resource === 'person') { //https://api-docs.affinity.co/#create-a-new-person if (operation === 'create') { @@ -166,7 +258,7 @@ export class Affinity implements INodeType { if (options.withInteractionDates) { qs.with_interaction_dates = options.withInteractionDates as boolean; } - responseData = await affinityApiRequest.call(this,'GET', `/persons/${personId}`, {}, qs); + responseData = await affinityApiRequest.call(this, 'GET', `/persons/${personId}`, {}, qs); } //https://api-docs.affinity.co/#search-for-persons if (operation === 'getAll') { @@ -230,7 +322,7 @@ export class Affinity implements INodeType { if (options.withInteractionDates) { qs.with_interaction_dates = options.withInteractionDates as boolean; } - responseData = await affinityApiRequest.call(this,'GET', `/organizations/${organizationId}`, {}, qs); + responseData = await affinityApiRequest.call(this, 'GET', `/organizations/${organizationId}`, {}, qs); } //https://api-docs.affinity.co/#search-for-organizations if (operation === 'getAll') { diff --git a/packages/nodes-base/nodes/Affinity/GenericFunctions.ts b/packages/nodes-base/nodes/Affinity/GenericFunctions.ts index 1d3ca50db4..e19ca72739 100644 --- a/packages/nodes-base/nodes/Affinity/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Affinity/GenericFunctions.ts @@ -1,4 +1,6 @@ -import { OptionsWithUri } from 'request'; +import { + OptionsWithUri, +} from 'request'; import { BINARY_ENCODING, @@ -6,7 +8,11 @@ import { ILoadOptionsFunctions, } from 'n8n-core'; -import { IDataObject, IHookFunctions, IWebhookFunctions } from 'n8n-workflow'; +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 @@ -38,12 +44,13 @@ export async function affinityApiRequest(this: IExecuteFunctions | IWebhookFunct delete options.qs; } options = Object.assign({}, options, option); + console.log(options); try { return await this.helpers.request!(options); } catch (error) { if (error.response) { const errorMessage = error.response.body.message || error.response.body.description || error.message; - throw new Error(`Affinity error response: ${errorMessage}`); + throw new Error(`Affinity error response [${error.statusCode}]: ${errorMessage}`); } throw error; } diff --git a/packages/nodes-base/nodes/Affinity/ListDescription.ts b/packages/nodes-base/nodes/Affinity/ListDescription.ts new file mode 100644 index 0000000000..f284ffab33 --- /dev/null +++ b/packages/nodes-base/nodes/Affinity/ListDescription.ts @@ -0,0 +1,100 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const listOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'list', + ], + }, + }, + options: [ + { + name: 'Get', + value: 'get', + description: 'Get a list', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all lists', + }, + ], + default: 'get', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const listFields = [ + /* -------------------------------------------------------------------------- */ + /* list:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'List ID', + name: 'listId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'get', + ], + }, + }, + description: 'The unique id of the list object to be retrieved.', + }, + /* -------------------------------------------------------------------------- */ + /* list:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'list', + ], + 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: [ + 'list', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 10, + }, + default: 5, + description: 'How many results to return.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Affinity/ListEntryDescription.ts b/packages/nodes-base/nodes/Affinity/ListEntryDescription.ts new file mode 100644 index 0000000000..04eae3a2e1 --- /dev/null +++ b/packages/nodes-base/nodes/Affinity/ListEntryDescription.ts @@ -0,0 +1,263 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const listEntryOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'listEntry', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a list entry', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a list entry', + }, + { + name: 'Get', + value: 'get', + description: 'Get a list entry', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all list entries', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const listEntryFields = [ + + /* -------------------------------------------------------------------------- */ + /* listEntry:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'List ID', + name: 'listId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getLists', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'listEntry', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The unique id of the list whose list entries are to be retrieved.', + }, + { + displayName: 'Entity ID', + name: 'entityId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'listEntry', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The unique id of the entity (person, organization, or opportunity) to add to this list.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'listEntry', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Creator ID', + name: 'creator_id', + type: 'string', + default: '', + description: `The id of a Person resource who should be recorded as adding the entry to the list.
+ Must be a person who can access Affinity. If not provided the creator defaults to the owner of the API key.`, + }, + ], + }, + /* -------------------------------------------------------------------------- */ + /* listEntry:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'List ID', + name: 'listId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getLists', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'listEntry', + ], + operation: [ + 'get', + ], + }, + }, + description: 'The unique id of the list that contains the specified list_entry_id.', + }, + { + displayName: 'List Entry ID', + name: 'listEntryId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'listEntry', + ], + operation: [ + 'get', + ], + }, + }, + description: 'The unique id of the list entry object to be retrieved.', + }, + /* -------------------------------------------------------------------------- */ + /* listEntry:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'List ID', + name: 'listId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLists', + }, + displayOptions: { + show: { + resource: [ + 'listEntry', + ], + operation: [ + 'getAll', + ], + }, + }, + default: '', + description: 'The unique id of the list whose list entries are to be retrieved.', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'listEntry', + ], + 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: [ + 'listEntry', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 10, + }, + default: 5, + description: 'How many results to return.', + }, + /* -------------------------------------------------------------------------- */ + /* listEntry:delete */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'List ID', + name: 'listId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLists', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'listEntry', + ], + operation: [ + 'delete', + ], + }, + }, + description: 'The unique id of the list that contains the specified list_entry_id.', + }, + { + displayName: 'List Entry ID', + name: 'listEntryId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'listEntry', + ], + operation: [ + 'delete', + ], + }, + }, + description: 'The unique id of the list entry object to be deleted.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Affinity/OrganizationDescription.ts b/packages/nodes-base/nodes/Affinity/OrganizationDescription.ts index 36d27ce9b4..dd591c63c5 100644 --- a/packages/nodes-base/nodes/Affinity/OrganizationDescription.ts +++ b/packages/nodes-base/nodes/Affinity/OrganizationDescription.ts @@ -1,4 +1,6 @@ -import { INodeProperties } from 'n8n-workflow'; +import { + INodeProperties, +} from 'n8n-workflow'; export const organizationOperations = [ { @@ -46,9 +48,9 @@ export const organizationOperations = [ export const organizationFields = [ -/* -------------------------------------------------------------------------- */ -/* organization:create */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* organization:create */ + /* -------------------------------------------------------------------------- */ { displayName: 'Name', name: 'name', @@ -114,9 +116,9 @@ export const organizationFields = [ }, ], }, -/* -------------------------------------------------------------------------- */ -/* organization:update */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* organization:update */ + /* -------------------------------------------------------------------------- */ { displayName: 'Organization ID', name: 'organizationId', @@ -178,9 +180,9 @@ export const organizationFields = [ }, ], }, -/* -------------------------------------------------------------------------- */ -/* organization:get */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* organization:get */ + /* -------------------------------------------------------------------------- */ { displayName: 'Organization ID', name: 'organizationId', @@ -225,9 +227,9 @@ export const organizationFields = [ }, ], }, -/* -------------------------------------------------------------------------- */ -/* organization:getAll */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* organization:getAll */ + /* -------------------------------------------------------------------------- */ { displayName: 'Return All', name: 'returnAll', @@ -302,9 +304,9 @@ export const organizationFields = [ }, ], }, -/* -------------------------------------------------------------------------- */ -/* organization:delete */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* organization:delete */ + /* -------------------------------------------------------------------------- */ { displayName: 'Organization ID', name: 'organizationId', diff --git a/packages/nodes-base/nodes/Affinity/PersonDescription.ts b/packages/nodes-base/nodes/Affinity/PersonDescription.ts index 99e493c788..c97634a8a3 100644 --- a/packages/nodes-base/nodes/Affinity/PersonDescription.ts +++ b/packages/nodes-base/nodes/Affinity/PersonDescription.ts @@ -1,4 +1,6 @@ -import { INodeProperties } from 'n8n-workflow'; +import { + INodeProperties, +} from 'n8n-workflow'; export const personOperations = [ { @@ -46,9 +48,9 @@ export const personOperations = [ export const personFields = [ -/* -------------------------------------------------------------------------- */ -/* person:create */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* person:create */ + /* -------------------------------------------------------------------------- */ { displayName: 'Fist Name', name: 'firstName', @@ -136,9 +138,9 @@ export const personFields = [ placeholder: 'info@example.com', default: [], }, -/* -------------------------------------------------------------------------- */ -/* person:update */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* person:update */ + /* -------------------------------------------------------------------------- */ { displayName: 'Person ID', name: 'personId', @@ -222,9 +224,9 @@ export const personFields = [ placeholder: 'info@example.com', default: [], }, -/* -------------------------------------------------------------------------- */ -/* person:get */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* person:get */ + /* -------------------------------------------------------------------------- */ { displayName: 'Person ID', name: 'personId', @@ -269,9 +271,9 @@ export const personFields = [ }, ], }, -/* -------------------------------------------------------------------------- */ -/* person:getAll */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* person:getAll */ + /* -------------------------------------------------------------------------- */ { displayName: 'Return All', name: 'returnAll', @@ -346,9 +348,9 @@ export const personFields = [ }, ], }, -/* -------------------------------------------------------------------------- */ -/* person:delete */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* person:delete */ + /* -------------------------------------------------------------------------- */ { displayName: 'Person ID', name: 'personId',