From 82408e030a9d88010b99f0cd43edeff7149d4953 Mon Sep 17 00:00:00 2001 From: trojanh Date: Mon, 20 Jan 2020 18:40:19 +0530 Subject: [PATCH 01/41] Add Trello Checklist create, get, getAll and delete api --- .../nodes-base/nodes/Trello/Trello.node.ts | 323 +++++++++++++++++- 1 file changed, 320 insertions(+), 3 deletions(-) diff --git a/packages/nodes-base/nodes/Trello/Trello.node.ts b/packages/nodes-base/nodes/Trello/Trello.node.ts index 5c242b47f6..13144678b6 100644 --- a/packages/nodes-base/nodes/Trello/Trello.node.ts +++ b/packages/nodes-base/nodes/Trello/Trello.node.ts @@ -51,6 +51,10 @@ export class Trello implements INodeType { name: 'Card', value: 'card', }, + { + name: "Checklist", + value: "checklist", + }, { name: 'List', value: 'list', @@ -1235,12 +1239,12 @@ export class Trello implements INodeType { { name: 'Create', value: 'create', - description: 'Create a new board', + description: 'Create a new attachment for a card', }, { name: 'Delete', value: 'delete', - description: 'Delete a board', + description: 'Delete an attachment', }, { name: 'Get', @@ -1350,7 +1354,7 @@ export class Trello implements INodeType { ], }, }, - description: 'The ID of the card to get delete.', + description: 'The ID of the card that attachment belongs to.', }, { displayName: 'Attachment ID', @@ -1485,7 +1489,259 @@ export class Trello implements INodeType { ], }, + // ---------------------------------- + // checklist + // ---------------------------------- + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'checklist', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new checklist', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a board', + }, + { + name: 'Get', + value: 'get', + description: 'Get the data of a checklist', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Returns all checklists for the card', + } + ], + default: 'getAll', + description: 'The operation to perform.', + }, + + // ---------------------------------- + // checklist:create + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the card to add checklist to.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The URL of the checklist to add.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'checklist', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Id Of Checklist Source', + name: 'idChecklistSource', + type: 'string', + default: '', + description: 'The ID of a source checklist to copy into the new one.', + }, + { + displayName: 'Position', + name: 'pos', + type: 'string', + default: '', + description: 'The position of the checklist on the card. One of: top, bottom, or a positive number.', + }, + ], + }, + + // ---------------------------------- + // checklist:delete + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the card that checklist belongs to.', + }, + { + displayName: 'Checklist ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the checklist to delete.', + }, + + + // ---------------------------------- + // checklist:getAll + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the card to get checklists.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'checklist', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list of fields.', + }, + ], + }, + + // ---------------------------------- + // checklist:get + // ---------------------------------- + { + displayName: 'Checklist ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the checklist to get.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'checklist', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list of fields.', + }, + ], + }, + ], + + }; @@ -1751,6 +2007,67 @@ export class Trello implements INodeType { endpoint = `cards/${cardId}/attachments`; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + Object.assign(qs, additionalFields); + } else { + throw new Error(`The operation "${operation}" is not known!`); + } + } else if (resource === 'checklist') { + + if (operation === 'create') { + // ---------------------------------- + // create + // ---------------------------------- + + requestMethod = 'POST'; + + const cardId = this.getNodeParameter('cardId', i) as string; + const name = this.getNodeParameter('name', i) as string; + + Object.assign(qs, { name }); + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + Object.assign(qs, additionalFields); + + endpoint = `cards/${cardId}/checklists`; + + } else if (operation === 'delete') { + // ---------------------------------- + // delete + // ---------------------------------- + + requestMethod = 'DELETE'; + + const cardId = this.getNodeParameter('cardId', i) as string; + const id = this.getNodeParameter('id', i) as string; + + endpoint = `cards/${cardId}/checklists/${id}`; + + } else if (operation === 'get') { + // ---------------------------------- + // get + // ---------------------------------- + + requestMethod = 'GET'; + + const id = this.getNodeParameter('id', i) as string; + + endpoint = `checklists/${id}`; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + Object.assign(qs, additionalFields); + + } else if (operation === 'getAll') { + // ---------------------------------- + // getAll + // ---------------------------------- + + requestMethod = 'GET'; + + const cardId = this.getNodeParameter('cardId', i) as string; + + endpoint = `cards/${cardId}/checklists`; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; Object.assign(qs, additionalFields); } else { From 29cb669a6b42fee49cea5d59f7c722915e85a6fb Mon Sep 17 00:00:00 2001 From: trojanh Date: Mon, 20 Jan 2020 19:07:27 +0530 Subject: [PATCH 02/41] Add checkItem API to get, update and delete --- .../nodes-base/nodes/Trello/Trello.node.ts | 249 +++++++++++++++++- 1 file changed, 248 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Trello/Trello.node.ts b/packages/nodes-base/nodes/Trello/Trello.node.ts index 13144678b6..4364552873 100644 --- a/packages/nodes-base/nodes/Trello/Trello.node.ts +++ b/packages/nodes-base/nodes/Trello/Trello.node.ts @@ -1512,17 +1512,32 @@ export class Trello implements INodeType { { name: 'Delete', value: 'delete', - description: 'Delete a board', + description: 'Delete a checklist', + }, + { + name: 'Delete CheckItem', + value: 'deleteCheckItem', + description: 'Delete a checklist item', }, { name: 'Get', value: 'get', description: 'Get the data of a checklist', }, + { + name: 'Get CheckItem', + value: 'getCheckItem', + description: 'Get a specific checkItem on a card', + }, { name: 'Get All', value: 'getAll', description: 'Returns all checklists for the card', + }, + { + name: 'Update CheckItem', + value: 'updateCheckItem', + description: 'Update an item in a checklist on a card.', } ], default: 'getAll', @@ -1739,6 +1754,199 @@ export class Trello implements INodeType { ], }, + // ---------------------------------- + // checklist:deleteCheckItem + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'deleteCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the card that checklist belongs to.', + }, + { + displayName: 'CheckItem ID', + name: 'checkItemId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'deleteCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the checklist to delete.', + }, + + // ---------------------------------- + // checklist:getCheckItem + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'getCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the card that checklist belongs to.', + }, + { + displayName: 'CheckItem ID', + name: 'checkItemId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'getCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the checklist to get.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'getCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list of fields.', + }, + ], + }, + + // ---------------------------------- + // checklist:updateCheckItem + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'updateCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the card that checklist belongs to.', + }, + { + displayName: 'CheckItem ID', + name: 'checkItemId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'updateCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the checklist item to update.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'updateCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'The new name for the checklist item.', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + description: 'One of: complete, incomplete', + }, + { + displayName: 'Checklist ID', + name: 'checklistId', + type: 'string', + default: '', + description: 'The ID of the checklist this item is in', + }, + { + displayName: 'Position', + name: 'pos', + type: 'string', + default: '', + description: 'The position of the checklist on the card. One of: top, bottom, or a positive number.', + }, + ], + }, + ], @@ -2068,6 +2276,45 @@ export class Trello implements INodeType { endpoint = `cards/${cardId}/checklists`; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + Object.assign(qs, additionalFields); + } else if (operation === 'getCheckItem') { + // ---------------------------------- + // getCheckItem + // ---------------------------------- + + requestMethod = 'GET'; + + const cardId = this.getNodeParameter('cardId', i) as string; + const checkItemId = this.getNodeParameter('checkItemId', i) as string; + + endpoint = `cards/${cardId}/checkItem/${checkItemId}`; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + Object.assign(qs, additionalFields); + } else if (operation === 'deleteCheckItem') { + // ---------------------------------- + // deleteCheckItem + // ---------------------------------- + + requestMethod = 'DELETE'; + + const cardId = this.getNodeParameter('cardId', i) as string; + const checkItemId = this.getNodeParameter('checkItemId', i) as string; + + endpoint = `cards/${cardId}/checkItem/${checkItemId}`; + } else if (operation === 'updateCheckItem') { + // ---------------------------------- + // updateCheckItem + // ---------------------------------- + + requestMethod = 'PUT'; + + const cardId = this.getNodeParameter('cardId', i) as string; + const checkItemId = this.getNodeParameter('checkItemId', i) as string; + + endpoint = `cards/${cardId}/checkItem/${checkItemId}`; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; Object.assign(qs, additionalFields); } else { From d28a3f7387a7cdcbbc4d49769abdaf482f731b78 Mon Sep 17 00:00:00 2001 From: trojanh Date: Mon, 20 Jan 2020 19:41:23 +0530 Subject: [PATCH 03/41] Add checkItemStates API --- .../nodes-base/nodes/Trello/Trello.node.ts | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/packages/nodes-base/nodes/Trello/Trello.node.ts b/packages/nodes-base/nodes/Trello/Trello.node.ts index 4364552873..a3953b41bc 100644 --- a/packages/nodes-base/nodes/Trello/Trello.node.ts +++ b/packages/nodes-base/nodes/Trello/Trello.node.ts @@ -1504,6 +1504,11 @@ export class Trello implements INodeType { }, }, options: [ + { + name: "Completed CheckItems", + value: "completedCheckItems", + description: "Get the completed checklist items on a card" + }, { name: 'Create', value: 'create', @@ -1947,6 +1952,54 @@ export class Trello implements INodeType { ], }, + // ---------------------------------- + // checklist:completedCheckItems + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'completedCheckItems', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the card for checkItems.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'completedCheckItems', + ], + resource: [ + 'checklist', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list of: "idCheckItem", "state".', + }, + ], + }, + ], @@ -2317,6 +2370,20 @@ export class Trello implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; Object.assign(qs, additionalFields); + } else if(operation ==="completedCheckItems") { + // ---------------------------------- + // updateCheckItem + // ---------------------------------- + + requestMethod = 'GET'; + + const cardId = this.getNodeParameter('cardId', i) as string; + + endpoint = `cards/${cardId}/checkItemStates`; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + Object.assign(qs, additionalFields); + } else { throw new Error(`The operation "${operation}" is not known!`); } From b944ed5f6b6273fca23b3fbe94dfce4b9df93eb5 Mon Sep 17 00:00:00 2001 From: trojanh Date: Mon, 20 Jan 2020 20:58:51 +0530 Subject: [PATCH 04/41] Add label API --- .../nodes-base/nodes/Trello/Trello.node.ts | 476 +++++++++++++++++- 1 file changed, 475 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Trello/Trello.node.ts b/packages/nodes-base/nodes/Trello/Trello.node.ts index a3953b41bc..6b5217acc6 100644 --- a/packages/nodes-base/nodes/Trello/Trello.node.ts +++ b/packages/nodes-base/nodes/Trello/Trello.node.ts @@ -55,6 +55,10 @@ export class Trello implements INodeType { name: "Checklist", value: "checklist", }, + { + name: "Label", + value: "label" + } { name: 'List', value: 'list', @@ -2000,8 +2004,373 @@ export class Trello implements INodeType { ], }, - ], + // ---------------------------------- + // label + // ---------------------------------- + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'label', + ], + }, + }, + options: [ + { + name: 'Add to Card', + value: 'addLabel', + description: 'Add a label to a card.', + }, + { + name: 'Create', + value: 'create', + description: 'Create a new label', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a label', + }, + { + name: 'Get', + value: 'get', + description: 'Get the data of a label', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Returns all label for the board', + }, + { + name: 'Remove From Card', + value: 'removeLabel', + description: 'Remove a label from a card.', + }, + { + name: 'Update', + value: 'update', + description: 'Update a label.', + } + ], + default: 'getAll', + description: 'The operation to perform.', + }, + + // ---------------------------------- + // label:create + // ---------------------------------- + { + displayName: 'Board ID', + name: 'boardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the board to create the label on.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'label', + ], + }, + }, + description: 'Name for the label.', + }, + { + displayName: 'Color', + name: 'color', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The color for the label. See [fields](https://developers.trello.com/reference#label-object) for color options.', + }, + + // ---------------------------------- + // label:delete + // ---------------------------------- + { + displayName: 'Label ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the label to delete.', + }, + + // ---------------------------------- + // label:getAll + // ---------------------------------- + { + displayName: 'Board ID', + name: 'boardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the board to get label.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'label', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list of fields.', + }, + ], + }, + + // ---------------------------------- + // label:get + // ---------------------------------- + { + displayName: 'Label ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'label', + ], + }, + }, + description: 'Get information about a label by ID.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'label', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list of fields.', + }, + ], + }, + + // ---------------------------------- + // label:addLabel + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'addLabel', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the card to get label.', + }, + { + displayName: 'Label ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'addLabel', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the label to add.', + }, + + // ---------------------------------- + // label:removeLabel + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'removeLabel', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the card to remove label from.', + }, + { + displayName: 'Label ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'removeLabel', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the label to remove.', + }, + + // ---------------------------------- + // label:update + // ---------------------------------- + { + displayName: 'Label ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the label to update.', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'label', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'Name of the label.', + }, + { + displayName: 'Color', + name: 'color', + type: 'string', + default: '', + description: 'The color for the label. See [fields](https://developers.trello.com/reference#label-object) for color options.', + } + ], + }, + ], }; @@ -2384,6 +2753,111 @@ export class Trello implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; Object.assign(qs, additionalFields); + } else { + throw new Error(`The operation "${operation}" is not known!`); + } + } else if (resource === 'label') { + + if (operation === 'create') { + // ---------------------------------- + // create + // ---------------------------------- + + requestMethod = 'POST'; + + const idBoard = this.getNodeParameter('boardId', i) as string; + const name = this.getNodeParameter('name', i) as string; + const color = this.getNodeParameter('color', i) as string; + + + Object.assign(qs, { + idBoard, + name, + color + }); + + + endpoint = "labels"; + + } else if (operation === 'delete') { + // ---------------------------------- + // delete + // ---------------------------------- + + requestMethod = 'DELETE'; + + const id = this.getNodeParameter('id', i) as string; + + endpoint = `labels/${id}`; + + } else if (operation === 'get') { + // ---------------------------------- + // get + // ---------------------------------- + + requestMethod = 'GET'; + + const id = this.getNodeParameter('id', i) as string; + + endpoint = `labels/${id}`; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + Object.assign(qs, additionalFields); + + } else if (operation === 'getAll') { + // ---------------------------------- + // getAll + // ---------------------------------- + + requestMethod = 'GET'; + + const idBoard = this.getNodeParameter('boardId', i) as string; + + endpoint = `board/${idBoard}/labels`; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + Object.assign(qs, additionalFields); + } else if (operation === 'update') { + // ---------------------------------- + // update + // ---------------------------------- + + requestMethod = 'PUT'; + + const id = this.getNodeParameter('id', i) as string; + + endpoint = `labels/${id}`; + + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + Object.assign(qs, updateFields); + + } else if (operation === 'addLabel') { + // ---------------------------------- + // addLabel + // ---------------------------------- + + requestMethod = 'POST'; + + const cardId = this.getNodeParameter('cardId', i) as string; + const id = this.getNodeParameter('id', i) as string; + + qs.value = id; + + endpoint = `/cards/${cardId}/idLabels`; + + } else if (operation === 'removeLabel') { + // ---------------------------------- + // addLabel + // ---------------------------------- + + requestMethod = 'DELETE'; + + const cardId = this.getNodeParameter('cardId', i) as string; + const id = this.getNodeParameter('id', i) as string; + + endpoint = `/cards/${cardId}/idLabels/${id}`; + } else { throw new Error(`The operation "${operation}" is not known!`); } From c8c9ba08917599bfa0c4e145b62f98940a01607a Mon Sep 17 00:00:00 2001 From: trojanh Date: Mon, 20 Jan 2020 21:10:17 +0530 Subject: [PATCH 05/41] Fix card fields url --- packages/nodes-base/nodes/Trello/Trello.node.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nodes-base/nodes/Trello/Trello.node.ts b/packages/nodes-base/nodes/Trello/Trello.node.ts index 6b5217acc6..b95025ae8c 100644 --- a/packages/nodes-base/nodes/Trello/Trello.node.ts +++ b/packages/nodes-base/nodes/Trello/Trello.node.ts @@ -2115,7 +2115,7 @@ export class Trello implements INodeType { ], }, }, - description: 'The color for the label. See [fields](https://developers.trello.com/reference#label-object) for color options.', + description: 'The color for the label. See fields for color options.', }, // ---------------------------------- @@ -2366,7 +2366,7 @@ export class Trello implements INodeType { name: 'color', type: 'string', default: '', - description: 'The color for the label. See [fields](https://developers.trello.com/reference#label-object) for color options.', + description: 'The color for the label. See fields for color options.', } ], }, From cbf4818feacae987e3df7925ca088c409f59e452 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 20 Jan 2020 10:22:12 -0600 Subject: [PATCH 06/41] :bug: Fix bug with credentials in integrated workflows --- packages/cli/src/WorkflowExecuteAdditionalData.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index d5b471def9..fa7f0abc0a 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -9,6 +9,7 @@ import { Push, ResponseHelper, WebhookHelpers, + WorkflowCredentials, WorkflowHelpers, } from './'; @@ -306,6 +307,10 @@ export async function executeWorkflow(workflowInfo: IExecuteWorkflowInfo, additi const additionalDataIntegrated = await getBase(additionalData.credentials); additionalDataIntegrated.hooks = getWorkflowHooksIntegrated(mode, executionId, workflowData!, { parentProcessMode: additionalData.hooks!.mode }); + // Get the needed credentials for the current workflow as they will differ to the ones of the + // calling workflow. + additionalDataIntegrated.credentials = await WorkflowCredentials(workflowData!.nodes); + // Find Start-Node const requiredNodeTypes = ['n8n-nodes-base.start']; let startNode: INode | undefined; From 965ce74895e94fcc61936eac354a12d6815dae16 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 20 Jan 2020 10:47:52 -0600 Subject: [PATCH 07/41] :zap: Do not allow to display NodeCreator via tab in readOnly-Mode --- packages/editor-ui/src/views/NodeView.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index b9db49e03f..4df79a9658 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -376,7 +376,7 @@ export default mixins( this.createNodeActive = false; this.$store.commit('setActiveNode', null); } else if (e.key === 'Tab') { - this.createNodeActive = !this.createNodeActive; + this.createNodeActive = !this.createNodeActive && !this.isReadOnly; } else if (e.key === this.controlKeyCode) { this.ctrlKeyPressed = true; } else if (e.key === 'F2') { From 14ab90791e682ba4bb56ff14df593cc71009ba70 Mon Sep 17 00:00:00 2001 From: trojanh Date: Tue, 21 Jan 2020 19:23:54 +0530 Subject: [PATCH 08/41] Add Disqus Forums GET apis --- .../credentials/DisqusApi.credentials.ts | 18 + .../nodes-base/nodes/Disqus/Disqus.node.ts | 626 ++++++++++++++++++ packages/nodes-base/nodes/Disqus/disqus.png | Bin 0 -> 1142 bytes packages/nodes-base/package.json | 2 + 4 files changed, 646 insertions(+) create mode 100644 packages/nodes-base/credentials/DisqusApi.credentials.ts create mode 100644 packages/nodes-base/nodes/Disqus/Disqus.node.ts create mode 100644 packages/nodes-base/nodes/Disqus/disqus.png diff --git a/packages/nodes-base/credentials/DisqusApi.credentials.ts b/packages/nodes-base/credentials/DisqusApi.credentials.ts new file mode 100644 index 0000000000..fbf98c92a2 --- /dev/null +++ b/packages/nodes-base/credentials/DisqusApi.credentials.ts @@ -0,0 +1,18 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class DisqusApi implements ICredentialType { + name = 'disqusApi'; + displayName = 'Disqus API'; + properties = [ + { + displayName: 'Access Token', + name: 'accessToken', + type: 'string' as NodePropertyTypes, + default: '', + description: 'Visit your account details page, and grab the Access Token. See Disqus auth.' + }, + ]; +} diff --git a/packages/nodes-base/nodes/Disqus/Disqus.node.ts b/packages/nodes-base/nodes/Disqus/Disqus.node.ts new file mode 100644 index 0000000000..401aabb169 --- /dev/null +++ b/packages/nodes-base/nodes/Disqus/Disqus.node.ts @@ -0,0 +1,626 @@ +import { + BINARY_ENCODING, + IExecuteFunctions, +} from 'n8n-core'; +import { + IDataObject, + INodeTypeDescription, + INodeExecutionData, + INodeType, +} from 'n8n-workflow'; + +import { OptionsWithUri } from 'request'; + + +export class Disqus implements INodeType { + description: INodeTypeDescription = { + displayName: 'Disqus', + name: 'disqus', + icon: 'file:disqus.png', + group: ['input'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Access data on Disqus', + defaults: { + name: 'Disqus', + color: '#22BB44', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'disqusApi', + required: true, + } + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Forum', + value: 'forum', + }, + { + name: 'User', + value: 'user', + }, + ], + default: 'forum', + description: 'The resource to operate on.', + }, + + // ---------------------------------- + // forum + // ---------------------------------- + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'forum', + ], + }, + }, + options: [ + { + name: 'Get', + value: 'get', + description: 'Returns forum details.', + }, + { + name: 'Get All Categories', + value: 'getCategories', + description: 'Returns a list of categories within a forum.', + }, + { + name: 'Get All Threads', + value: 'getThreads', + description: 'Returns a list of threads within a forum.', + }, + { + name: 'Get All Posts', + value: 'getPosts', + description: 'Returns a list of posts within a forum.', + } + ], + default: 'get', + description: 'The operation to perform.', + }, + + // ---------------------------------- + // forum:get + // ---------------------------------- + { + displayName: 'Forum name', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'forum', + ], + }, + }, + description: 'The short name(aka ID) of the forum to get.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'forum', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'attach', + name: 'attach', + type: 'string', + default: '[]', + description: 'Choices: followsForum, forumCanDisableAds, forumForumCategory, counters, forumDaysAlive, forumFeatures, forumIntegration, forumNewPolicy, forumPermissions', + }, + { + displayName: 'related', + name: 'related', + type: 'string', + default: false, + description: 'You may specify relations to include with your response. Choices `author`', + }, + ], + }, + + // ---------------------------------- + // forum:getPosts + // ---------------------------------- + { + displayName: 'Forum name', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'getPosts', + ], + resource: [ + 'forum', + ], + }, + }, + description: 'The short name(aka ID) of the forum to get.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'getPosts', + ], + resource: [ + 'forum', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Since', + name: 'since', + type: 'string', + default: `[]`, + description: 'Unix timestamp (or ISO datetime standard)', + }, + { + displayName: 'Related', + name: 'related', + type: 'string', + default: '[]', + description: 'You may specify relations to include with your response. Choices `author`', + }, + { + displayName: 'Cursor', + name: 'cursor', + type: 'string', + default: '', + description: 'You may specify cursor for your response.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'string', + default: 25, + description: 'You may specify relations maximum number of posts to return. Maximum value is 100', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'string', + default: '[]', + description: 'You may specify filters for your response. Choices: `Is_Anonymous`, `Has_Link`, `Has_Low_Rep_Author`, `Has_Bad_Word`, `Is_Flagged`, `No_Issue`, `Is_Toxic`, `Modified_By_Rule`, `Shadow_Banned`, `Has_Media`, `Is_At_Flag_Limit`', + }, + { + displayName: 'Query', + name: 'query', + type: 'string', + default: '', + description: 'You may specify query for your response.', + }, + { + displayName: 'Include', + name: 'include', + type: 'string', + default: false, + description: 'You may specify relations to include with your response. Choices `author`', + }, + { + displayName: 'Order', + name: 'order', + type: 'string', + default: 'asc', + description: 'You may specify order to sort your response.Choices: asc, desc', + }, + ], + }, + + // ---------------------------------- + // forum:getCategories + // ---------------------------------- + { + displayName: 'Forum name', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'getCategories', + ], + resource: [ + 'forum', + ], + }, + }, + description: 'The short name(aka ID) of the forum to get Categories.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'getCategories', + ], + resource: [ + 'forum', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Since ID', + name: 'sinceId', + type: 'string', + default: `[]`, + description: 'You may specify cursor since_id for your response.', + }, + { + displayName: 'Cursor', + name: 'cursor', + type: 'string', + default: '', + description: 'You may specify cursor for your response.', + }, + { + displayName: 'Order', + name: 'order', + type: 'string', + default: 'asc', + description: 'You may specify order to sort your response.Choices: asc, desc', + }, + ], + }, + + // ---------------------------------- + // forum:getThreads + // ---------------------------------- + { + displayName: 'Forum name', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'getThreads', + ], + resource: [ + 'forum', + ], + }, + }, + description: 'The short name(aka ID) of the forum to get Threads.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'getThreads', + ], + resource: [ + 'forum', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Thread', + name: 'threadId', + type: 'string', + default: '', + description: 'Looks up a thread by ID. You may pass us the "ident" query type instead of an ID by including "forum". You may pass us the "link" query type to filter by URL. You must pass the "forum" if you do not have the Pro API Access addon.', + }, + { + displayName: 'Since', + name: 'since', + type: 'string', + default: '', + description: 'Unix timestamp (or ISO datetime standard)', + }, + { + displayName: 'Related', + name: 'related', + type: 'string', + default: '[]', + description: 'You may specify relations to include with your response. Choices `author`', + }, + { + displayName: 'Cursor', + name: 'cursor', + type: 'string', + default: '', + description: 'You may specify cursor for your response.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'string', + default: 25, + description: 'You may specify relations maximum number of posts to return. Maximum value is 100', + }, + { + displayName: 'Include', + name: 'include', + type: 'string', + default: '', + description: 'You may specify relations to include with your response. Choices: open, closed, killed', + }, + { + displayName: 'Order', + name: 'order', + type: 'string', + default: 'desc', + description: 'You may specify order to sort your response.Choices: asc, desc', + }, + ], + }, + + // // ---------------------------------- + // // forum:getThreads + // // ---------------------------------- + // { + // displayName: 'Forum name', + // name: 'id', + // type: 'string', + // default: '', + // required: true, + // displayOptions: { + // show: { + // operation: [ + // 'getThreads', + // ], + // resource: [ + // 'forum', + // ], + // }, + // }, + // description: 'The short name(aka ID) of the forum to get Threads.', + // }, + // { + // displayName: 'Additional Fields', + // name: 'additionalFields', + // type: 'collection', + // placeholder: 'Add Field', + // displayOptions: { + // show: { + // operation: [ + // 'getCategories', + // ], + // resource: [ + // 'forum', + // ], + // }, + // }, + // default: {}, + // options: [ + // { + // displayName: 'Thread', + // name: 'threadId', + // type: 'string', + // default: '', + // description: 'Looks up a thread by ID. You may pass us the "ident" query type instead of an ID by including "forum". You may pass us the "link" query type to filter by URL. You must pass the "forum" if you do not have the Pro API Access addon.', + // }, + // { + // displayName: 'Since', + // name: 'since', + // type: 'string', + // default: '', + // description: 'Unix timestamp (or ISO datetime standard)', + // }, + // { + // displayName: 'Related', + // name: 'related', + // type: 'string', + // default: '[]', + // description: 'You may specify relations to include with your response. Choices `author`', + // }, + // { + // displayName: 'Cursor', + // name: 'cursor', + // type: 'string', + // default: '', + // description: 'You may specify cursor for your response.', + // }, + // { + // displayName: 'Limit', + // name: 'limit', + // type: 'string', + // default: 25, + // description: 'You may specify relations maximum number of posts to return. Maximum value is 100', + // }, + // { + // displayName: 'Include', + // name: 'include', + // type: 'string', + // default: '', + // description: 'You may specify relations to include with your response. Choices: open, closed, killed', + // }, + // { + // displayName: 'Order', + // name: 'order', + // type: 'string', + // default: 'desc', + // description: 'You may specify order to sort your response.Choices: asc, desc', + // }, + // ], + // }, + ], + }; + + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + + const credentials = this.getCredentials('disqusApi'); + + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + + let endpoint = ''; + let requestMethod = ''; + let body: IDataObject | Buffer; + let qs: IDataObject; + + + for (let i = 0; i < items.length; i++) { + body = {}; + qs = {}; + + if (resource === 'forum') { + if (operation === 'get') { + // ---------------------------------- + // get + // ---------------------------------- + + requestMethod = 'GET'; + + endpoint = 'forums/details.json'; + + const id = this.getNodeParameter('id', i) as string; + qs.forum = id; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + Object.assign(qs, additionalFields); + + } else if (operation === 'getPosts') { + // ---------------------------------- + // getPosts + // ---------------------------------- + + requestMethod = 'GET'; + + endpoint = 'forums/listPosts.json'; + + const id = this.getNodeParameter('id', i) as string; + qs.forum = id; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + Object.assign(qs, additionalFields); + + } else if (operation === 'getCategories') { + // ---------------------------------- + // getCategories + // ---------------------------------- + + requestMethod = 'GET'; + + endpoint = 'forums/listCategories.json'; + + const id = this.getNodeParameter('id', i) as string; + qs.forum = id; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + Object.assign(qs, additionalFields); + + } else if (operation === 'getThreads') { + // ---------------------------------- + // getThreads + // ---------------------------------- + + requestMethod = 'GET'; + + endpoint = 'forums/listThreads.json'; + + const id = this.getNodeParameter('id', i) as string; + qs.forum = id; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + Object.assign(qs, additionalFields); + + } else { + throw new Error(`The operation "${operation}" is not known!`); + } + + } else { + throw new Error(`The resource "${resource}" is not known!`); + } + + qs.api_key = credentials.accessToken; + endpoint = `https://disqus.com/api/3.0/${endpoint}`; + + const options: OptionsWithUri = { + method: requestMethod, + qs, + uri: endpoint, + json: true, + }; + + + let responseData; + try { + responseData = await this.helpers.request(options); + } catch (error) { + if (error.statusCode === 401) { + // Return a clear error + throw new Error('The Dropbox credentials are not valid!'); + } + + if (error.error && error.error.error_summary) { + // Try to return the error prettier + throw new Error(`Dropbox error response [${error.statusCode}]: ${error.error.error_summary}`); + } + + // If that data does not exist for some reason return the actual error + throw error; + } + + if (responseData && 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/Disqus/disqus.png b/packages/nodes-base/nodes/Disqus/disqus.png new file mode 100644 index 0000000000000000000000000000000000000000..2b3b5cf3a898d43abfde7409f90d6e2c844a03ea GIT binary patch literal 1142 zcmV-+1d02JP)Px#8&FJCMJk~G|NsB;`TvN?|Fqu!XS)AHtpCmF|J(2X zSe;t>000B|NklP{?ZmIfroehFtOB^7qpA;Guo#Q?>QrmIX^mv^|Xn z_T!W>8sD<;jSSXgxd(TcY-=_Ws)CvV`h2dDf?Dojok621BaaM3EBa$ic|A4Ug!@;k z;beUu>*QXx59l>y3zloc_met@x|6lfu@#sFhO!XuE>!iJQ% z>D1f+VM9$<;b#KzU!{-~>LQfCmo;4_p9wq|UMSS3R6;c3YT0y`FA^vgMhM*rFa)-$ zg7}7l#m0wQ2)sJsE7=KBfDzbdP>#~;Py}`vl->Ba7e^$4U3rx2#R4U;XVb#H8MkFb zn+fc!Xi>+<2`HApPFyT|*>D7QGUu5}UiKDv zg_~AkC$=idQ9yPVmRij6Mv#zE2)s=0Dm}T&O|MrJfJm=XA2@3N9P(Jtd}0!VCnn}2 zUknS~1(?sQRqF&j)w3SDQ}rK%iX~X324YoeKq~eSvtkcQxlK#S6Rc{EQ1H|uKd|Nu zUUJsdiV3Xx0~h_ZHBuvs{-6X}rwAa+E`cW0Jc0sQ_=7JHwbZ*_Qma=HxwsG>oJ+r+D^Jh4T-K zyi7l?^D_Uy)XVfEYY%8)l%c_0IrAUJGuY(msRw6waTs%S`ThN3=Hc<*&lnSr?|)!% z`vHaZ`Zv`NDE#0t?N3-Z91e%W;cz${4u`|xa5x+ehr{t2ANt8T>t{X?82|tP07*qo IM6N<$f~#U7mH+?% literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index df87d196ce..c985f4bf97 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -37,6 +37,7 @@ "dist/credentials/ClickUpApi.credentials.js", "dist/credentials/CodaApi.credentials.js", "dist/credentials/CopperApi.credentials.js", + "dist/credentials/DisqusApi.credentials.js", "dist/credentials/DropboxApi.credentials.js", "dist/credentials/EventbriteApi.credentials.js", "dist/credentials/FreshdeskApi.credentials.js", @@ -104,6 +105,7 @@ "dist/nodes/Copper/CopperTrigger.node.js", "dist/nodes/Cron.node.js", "dist/nodes/Discord/Discord.node.js", + "dist/nodes/Disqus/Disqus.node.js", "dist/nodes/Dropbox/Dropbox.node.js", "dist/nodes/EditImage.node.js", "dist/nodes/EmailReadImap.node.js", From 4977c0b1630dc4f1dbc9c06e724903267cc42fa3 Mon Sep 17 00:00:00 2001 From: trojanh Date: Tue, 21 Jan 2020 19:33:57 +0530 Subject: [PATCH 09/41] Remove Users API from UI --- .../nodes-base/nodes/Disqus/Disqus.node.ts | 98 +------------------ 1 file changed, 2 insertions(+), 96 deletions(-) diff --git a/packages/nodes-base/nodes/Disqus/Disqus.node.ts b/packages/nodes-base/nodes/Disqus/Disqus.node.ts index 401aabb169..5e6561a6f0 100644 --- a/packages/nodes-base/nodes/Disqus/Disqus.node.ts +++ b/packages/nodes-base/nodes/Disqus/Disqus.node.ts @@ -42,11 +42,7 @@ export class Disqus implements INodeType { { name: 'Forum', value: 'forum', - }, - { - name: 'User', - value: 'user', - }, + } ], default: 'forum', description: 'The resource to operate on.', @@ -394,97 +390,7 @@ export class Disqus implements INodeType { description: 'You may specify order to sort your response.Choices: asc, desc', }, ], - }, - - // // ---------------------------------- - // // forum:getThreads - // // ---------------------------------- - // { - // displayName: 'Forum name', - // name: 'id', - // type: 'string', - // default: '', - // required: true, - // displayOptions: { - // show: { - // operation: [ - // 'getThreads', - // ], - // resource: [ - // 'forum', - // ], - // }, - // }, - // description: 'The short name(aka ID) of the forum to get Threads.', - // }, - // { - // displayName: 'Additional Fields', - // name: 'additionalFields', - // type: 'collection', - // placeholder: 'Add Field', - // displayOptions: { - // show: { - // operation: [ - // 'getCategories', - // ], - // resource: [ - // 'forum', - // ], - // }, - // }, - // default: {}, - // options: [ - // { - // displayName: 'Thread', - // name: 'threadId', - // type: 'string', - // default: '', - // description: 'Looks up a thread by ID. You may pass us the "ident" query type instead of an ID by including "forum". You may pass us the "link" query type to filter by URL. You must pass the "forum" if you do not have the Pro API Access addon.', - // }, - // { - // displayName: 'Since', - // name: 'since', - // type: 'string', - // default: '', - // description: 'Unix timestamp (or ISO datetime standard)', - // }, - // { - // displayName: 'Related', - // name: 'related', - // type: 'string', - // default: '[]', - // description: 'You may specify relations to include with your response. Choices `author`', - // }, - // { - // displayName: 'Cursor', - // name: 'cursor', - // type: 'string', - // default: '', - // description: 'You may specify cursor for your response.', - // }, - // { - // displayName: 'Limit', - // name: 'limit', - // type: 'string', - // default: 25, - // description: 'You may specify relations maximum number of posts to return. Maximum value is 100', - // }, - // { - // displayName: 'Include', - // name: 'include', - // type: 'string', - // default: '', - // description: 'You may specify relations to include with your response. Choices: open, closed, killed', - // }, - // { - // displayName: 'Order', - // name: 'order', - // type: 'string', - // default: 'desc', - // description: 'You may specify order to sort your response.Choices: asc, desc', - // }, - // ], - // }, + } ], }; From 0c1428471dd7faa2769115255cb5bce5d42db41c Mon Sep 17 00:00:00 2001 From: trojanh Date: Tue, 21 Jan 2020 19:40:20 +0530 Subject: [PATCH 10/41] Fix error messages --- packages/nodes-base/nodes/Disqus/Disqus.node.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nodes-base/nodes/Disqus/Disqus.node.ts b/packages/nodes-base/nodes/Disqus/Disqus.node.ts index 5e6561a6f0..dde4b33965 100644 --- a/packages/nodes-base/nodes/Disqus/Disqus.node.ts +++ b/packages/nodes-base/nodes/Disqus/Disqus.node.ts @@ -508,12 +508,12 @@ export class Disqus implements INodeType { } catch (error) { if (error.statusCode === 401) { // Return a clear error - throw new Error('The Dropbox credentials are not valid!'); + throw new Error('The Disqus credentials are not valid!'); } if (error.error && error.error.error_summary) { // Try to return the error prettier - throw new Error(`Dropbox error response [${error.statusCode}]: ${error.error.error_summary}`); + throw new Error(`Disqus error response [${error.statusCode}]: ${error.error.error_summary}`); } // If that data does not exist for some reason return the actual error From c0aacbed746e8630ec065fd0ebd5a869245e59b3 Mon Sep 17 00:00:00 2001 From: trojanh Date: Tue, 21 Jan 2020 19:42:28 +0530 Subject: [PATCH 11/41] Update space indentation to tab --- .../credentials/DisqusApi.credentials.ts | 2 +- .../nodes-base/nodes/Disqus/Disqus.node.ts | 110 +++++++++--------- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/packages/nodes-base/credentials/DisqusApi.credentials.ts b/packages/nodes-base/credentials/DisqusApi.credentials.ts index fbf98c92a2..c4b2f33aed 100644 --- a/packages/nodes-base/credentials/DisqusApi.credentials.ts +++ b/packages/nodes-base/credentials/DisqusApi.credentials.ts @@ -11,7 +11,7 @@ export class DisqusApi implements ICredentialType { displayName: 'Access Token', name: 'accessToken', type: 'string' as NodePropertyTypes, - default: '', + default: '', description: 'Visit your account details page, and grab the Access Token. See Disqus auth.' }, ]; diff --git a/packages/nodes-base/nodes/Disqus/Disqus.node.ts b/packages/nodes-base/nodes/Disqus/Disqus.node.ts index dde4b33965..85eb133def 100644 --- a/packages/nodes-base/nodes/Disqus/Disqus.node.ts +++ b/packages/nodes-base/nodes/Disqus/Disqus.node.ts @@ -63,16 +63,16 @@ export class Disqus implements INodeType { }, }, options: [ - { + { name: 'Get', value: 'get', description: 'Returns forum details.', - }, - { + }, + { name: 'Get All Categories', value: 'getCategories', description: 'Returns a list of categories within a forum.', - }, + }, { name: 'Get All Threads', value: 'getThreads', @@ -194,43 +194,43 @@ export class Disqus implements INodeType { type: 'string', default: '[]', description: 'You may specify relations to include with your response. Choices `author`', - }, - { + }, + { displayName: 'Cursor', name: 'cursor', - type: 'string', + type: 'string', default: '', description: 'You may specify cursor for your response.', - }, - { + }, + { displayName: 'Limit', name: 'limit', type: 'string', default: 25, description: 'You may specify relations maximum number of posts to return. Maximum value is 100', - }, - { + }, + { displayName: 'Filters', name: 'filters', type: 'string', default: '[]', - description: 'You may specify filters for your response. Choices: `Is_Anonymous`, `Has_Link`, `Has_Low_Rep_Author`, `Has_Bad_Word`, `Is_Flagged`, `No_Issue`, `Is_Toxic`, `Modified_By_Rule`, `Shadow_Banned`, `Has_Media`, `Is_At_Flag_Limit`', - }, - { + description: 'You may specify filters for your response. Choices: `Is_Anonymous`, `Has_Link`, `Has_Low_Rep_Author`, `Has_Bad_Word`, `Is_Flagged`, `No_Issue`, `Is_Toxic`, `Modified_By_Rule`, `Shadow_Banned`, `Has_Media`, `Is_At_Flag_Limit`', + }, + { displayName: 'Query', name: 'query', type: 'string', default: '', description: 'You may specify query for your response.', - }, - { + }, + { displayName: 'Include', name: 'include', type: 'string', default: false, description: 'You may specify relations to include with your response. Choices `author`', - }, - { + }, + { displayName: 'Order', name: 'order', type: 'string', @@ -238,9 +238,9 @@ export class Disqus implements INodeType { description: 'You may specify order to sort your response.Choices: asc, desc', }, ], - }, + }, - // ---------------------------------- + // ---------------------------------- // forum:getCategories // ---------------------------------- { @@ -285,14 +285,14 @@ export class Disqus implements INodeType { default: `[]`, description: 'You may specify cursor since_id for your response.', }, - { + { displayName: 'Cursor', name: 'cursor', - type: 'string', + type: 'string', default: '', description: 'You may specify cursor for your response.', - }, - { + }, + { displayName: 'Order', name: 'order', type: 'string', @@ -300,9 +300,9 @@ export class Disqus implements INodeType { description: 'You may specify order to sort your response.Choices: asc, desc', }, ], - }, + }, - // ---------------------------------- + // ---------------------------------- // forum:getThreads // ---------------------------------- { @@ -339,8 +339,8 @@ export class Disqus implements INodeType { }, }, default: {}, - options: [ - { + options: [ + { displayName: 'Thread', name: 'threadId', type: 'string', @@ -360,29 +360,29 @@ export class Disqus implements INodeType { type: 'string', default: '[]', description: 'You may specify relations to include with your response. Choices `author`', - }, - { + }, + { displayName: 'Cursor', name: 'cursor', - type: 'string', + type: 'string', default: '', description: 'You may specify cursor for your response.', - }, - { + }, + { displayName: 'Limit', name: 'limit', type: 'string', default: 25, description: 'You may specify relations maximum number of posts to return. Maximum value is 100', - }, - { + }, + { displayName: 'Include', name: 'include', type: 'string', default: '', description: 'You may specify relations to include with your response. Choices: open, closed, killed', - }, - { + }, + { displayName: 'Order', name: 'order', type: 'string', @@ -390,7 +390,7 @@ export class Disqus implements INodeType { description: 'You may specify order to sort your response.Choices: asc, desc', }, ], - } + } ], }; @@ -415,7 +415,7 @@ export class Disqus implements INodeType { for (let i = 0; i < items.length; i++) { - body = {}; + body = {}; qs = {}; if (resource === 'forum') { @@ -426,10 +426,10 @@ export class Disqus implements INodeType { requestMethod = 'GET'; - endpoint = 'forums/details.json'; + endpoint = 'forums/details.json'; - const id = this.getNodeParameter('id', i) as string; - qs.forum = id; + const id = this.getNodeParameter('id', i) as string; + qs.forum = id; const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; @@ -442,10 +442,10 @@ export class Disqus implements INodeType { requestMethod = 'GET'; - endpoint = 'forums/listPosts.json'; + endpoint = 'forums/listPosts.json'; - const id = this.getNodeParameter('id', i) as string; - qs.forum = id; + const id = this.getNodeParameter('id', i) as string; + qs.forum = id; const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; @@ -458,10 +458,10 @@ export class Disqus implements INodeType { requestMethod = 'GET'; - endpoint = 'forums/listCategories.json'; + endpoint = 'forums/listCategories.json'; - const id = this.getNodeParameter('id', i) as string; - qs.forum = id; + const id = this.getNodeParameter('id', i) as string; + qs.forum = id; const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; @@ -474,10 +474,10 @@ export class Disqus implements INodeType { requestMethod = 'GET'; - endpoint = 'forums/listThreads.json'; + endpoint = 'forums/listThreads.json'; - const id = this.getNodeParameter('id', i) as string; - qs.forum = id; + const id = this.getNodeParameter('id', i) as string; + qs.forum = id; const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; @@ -485,14 +485,14 @@ export class Disqus implements INodeType { } else { throw new Error(`The operation "${operation}" is not known!`); - } + } } else { throw new Error(`The resource "${resource}" is not known!`); } - qs.api_key = credentials.accessToken; - endpoint = `https://disqus.com/api/3.0/${endpoint}`; + qs.api_key = credentials.accessToken; + endpoint = `https://disqus.com/api/3.0/${endpoint}`; const options: OptionsWithUri = { method: requestMethod, @@ -527,6 +527,6 @@ export class Disqus implements INodeType { } } - return [this.helpers.returnJsonArray(returnData)]; + return [this.helpers.returnJsonArray(returnData)]; } } From 01a2dd13d5bb0d46ccac0764ab0077ceb63ab72a Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 22 Jan 2020 00:17:29 -0800 Subject: [PATCH 12/41] :zap: Small improvements for Salesmate-Node --- .../nodes/Salesmate/ActivityDescription.ts | 22 ++--- .../nodes/Salesmate/CompanyDescription.ts | 22 ++--- .../nodes/Salesmate/DealDescription.ts | 22 ++--- .../nodes/Salesmate/GenericFunctions.ts | 18 ++++ .../nodes/Salesmate/Salesmate.node.ts | 87 ++++++++++++++----- 5 files changed, 119 insertions(+), 52 deletions(-) diff --git a/packages/nodes-base/nodes/Salesmate/ActivityDescription.ts b/packages/nodes-base/nodes/Salesmate/ActivityDescription.ts index fe737729a7..656bb8f15f 100644 --- a/packages/nodes-base/nodes/Salesmate/ActivityDescription.ts +++ b/packages/nodes-base/nodes/Salesmate/ActivityDescription.ts @@ -19,9 +19,9 @@ export const activityOperations = [ description: 'Create a activity', }, { - name: 'Update', - value: 'update', - description: 'Update a activity', + name: 'Delete', + value: 'delete', + description: 'Delete a activity', }, { name: 'Get', @@ -34,9 +34,9 @@ export const activityOperations = [ description: 'Get all companies', }, { - name: 'Delete', - value: 'delete', - description: 'Delete a activity', + name: 'Update', + value: 'update', + description: 'Update a activity', }, ], default: 'create', @@ -427,12 +427,14 @@ export const activityFields = [ name: 'fields', type: 'string', default: '', + description: 'Comma separated list of fields to return.', }, { displayName: 'Sort By', name: 'sortBy', type: 'string', default: '', + description: 'The field to sort by.', }, { displayName: 'Sort Order', @@ -440,11 +442,11 @@ export const activityFields = [ type: 'options', options: [ { - name: 'Asc', + name: 'ASC', value: 'asc', }, { - name: 'Desc', + name: 'DESC', value: 'desc', }, ], @@ -508,11 +510,11 @@ export const activityFields = [ type: 'options', options: [ { - name: 'And', + name: 'AND', value: 'AND', }, { - name: 'Or', + name: 'OR', value: 'OR', }, ], diff --git a/packages/nodes-base/nodes/Salesmate/CompanyDescription.ts b/packages/nodes-base/nodes/Salesmate/CompanyDescription.ts index a2dbc2e762..dbf20eb1bd 100644 --- a/packages/nodes-base/nodes/Salesmate/CompanyDescription.ts +++ b/packages/nodes-base/nodes/Salesmate/CompanyDescription.ts @@ -19,9 +19,9 @@ export const companyOperations = [ description: 'Create a company', }, { - name: 'Update', - value: 'update', - description: 'Update a company', + name: 'Delete', + value: 'delete', + description: 'Delete a company', }, { name: 'Get', @@ -34,9 +34,9 @@ export const companyOperations = [ description: 'Get all companies', }, { - name: 'Delete', - value: 'delete', - description: 'Delete a company', + name: 'Update', + value: 'update', + description: 'Update a company', }, ], default: 'create', @@ -520,12 +520,14 @@ export const companyFields = [ name: 'fields', type: 'string', default: '', + description: 'Comma separated list of fields to return.', }, { displayName: 'Sort By', name: 'sortBy', type: 'string', default: '', + description: 'The field to sort by.', }, { displayName: 'Sort Order', @@ -533,11 +535,11 @@ export const companyFields = [ type: 'options', options: [ { - name: 'Asc', + name: 'ASC', value: 'asc', }, { - name: 'Desc', + name: 'DESC', value: 'desc', }, ], @@ -601,11 +603,11 @@ export const companyFields = [ type: 'options', options: [ { - name: 'And', + name: 'AND', value: 'AND', }, { - name: 'Or', + name: 'OR', value: 'OR', }, ], diff --git a/packages/nodes-base/nodes/Salesmate/DealDescription.ts b/packages/nodes-base/nodes/Salesmate/DealDescription.ts index 343c02b55a..b4291c872d 100644 --- a/packages/nodes-base/nodes/Salesmate/DealDescription.ts +++ b/packages/nodes-base/nodes/Salesmate/DealDescription.ts @@ -19,9 +19,9 @@ export const dealOperations = [ description: 'Create a deal', }, { - name: 'Update', - value: 'update', - description: 'Update a deal', + name: 'Delete', + value: 'delete', + description: 'Delete a deal', }, { name: 'Get', @@ -34,9 +34,9 @@ export const dealOperations = [ description: 'Get all companies', }, { - name: 'Delete', - value: 'delete', - description: 'Delete a deal', + name: 'Update', + value: 'update', + description: 'Update a deal', }, ], default: 'create', @@ -691,12 +691,14 @@ export const dealFields = [ name: 'fields', type: 'string', default: '', + description: 'Comma separated list of fields to return.', }, { displayName: 'Sort By', name: 'sortBy', type: 'string', default: '', + description: 'The field to sort by.', }, { displayName: 'Sort Order', @@ -704,11 +706,11 @@ export const dealFields = [ type: 'options', options: [ { - name: 'Asc', + name: 'ASC', value: 'asc', }, { - name: 'Desc', + name: 'DESC', value: 'desc', }, ], @@ -772,11 +774,11 @@ export const dealFields = [ type: 'options', options: [ { - name: 'And', + name: 'AND', value: 'AND', }, { - name: 'Or', + name: 'OR', value: 'OR', }, ], diff --git a/packages/nodes-base/nodes/Salesmate/GenericFunctions.ts b/packages/nodes-base/nodes/Salesmate/GenericFunctions.ts index a84dbc95e2..1471ae4a6c 100644 --- a/packages/nodes-base/nodes/Salesmate/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Salesmate/GenericFunctions.ts @@ -65,3 +65,21 @@ export function validateJSON(json: string | undefined): any { // tslint:disable- } return result; } + + +/** + * Converts data from the Salesmate format into a simple object + * + * @export + * @param {IDataObject[]} data + * @returns {IDataObject} + */ +export function simplifySalesmateData(data: IDataObject[]): IDataObject { + const returnData: IDataObject = {}; + + for (const item of data) { + returnData[item.fieldName as string] = item.value; + } + + return returnData; +} diff --git a/packages/nodes-base/nodes/Salesmate/Salesmate.node.ts b/packages/nodes-base/nodes/Salesmate/Salesmate.node.ts index a57b8b7410..e8f5a49528 100644 --- a/packages/nodes-base/nodes/Salesmate/Salesmate.node.ts +++ b/packages/nodes-base/nodes/Salesmate/Salesmate.node.ts @@ -12,6 +12,7 @@ import { import { salesmateApiRequest, salesmateApiRequestAllItems, + simplifySalesmateData, validateJSON, } from './GenericFunctions'; import { @@ -289,12 +290,9 @@ export class Salesmate implements INodeType { const rawData = this.getNodeParameter('rawData', i) as boolean; responseData = await salesmateApiRequest.call(this, 'GET', `/v1/companies/${companyId}`); responseData = responseData.Data; + if (!rawData) { - responseData = responseData.map((company: IDataObject) => { - const aux: IDataObject = {}; - aux[company.fieldName as string] = company.value; - return aux; - }); + responseData = simplifySalesmateData(responseData); } } if (operation === 'getAll') { @@ -314,9 +312,26 @@ export class Salesmate implements INodeType { qs.sortOrder = options.sortOrder as string; } if (options.fields) { + if ((options.fields as string).trim() === '') { + throw new Error('You have to add at least one field'); + } body.fields = (options.fields as string).split(',') as string[]; } else { - throw new Error('You have to add at least one field'); + body.fields = [ + 'name', + 'description', + 'billingAddressLine1', + 'billingAddressLine2', + 'billingCity', + 'billingZipCode', + 'billingState', + 'billingCountry', + 'website', + 'owner', + 'tags', + 'photo', + 'createdAt', + ]; } if (!jsonActive) { const filters: IDataObject[] = []; @@ -332,7 +347,7 @@ export class Salesmate implements INodeType { }; filter.condition = condition.condition; filter.data = condition.value; - filters.push(filter) + filters.push(filter); } } } @@ -440,12 +455,9 @@ export class Salesmate implements INodeType { const rawData = this.getNodeParameter('rawData', i) as boolean; responseData = await salesmateApiRequest.call(this, 'GET', `/v1/activities/${activityId}`); responseData = responseData.Data; + if (!rawData) { - responseData = responseData.map((activity: IDataObject) => { - const aux: IDataObject = {}; - aux[activity.fieldName as string] = activity.value; - return aux; - }); + responseData = simplifySalesmateData(responseData); } } if (operation === 'getAll') { @@ -465,9 +477,27 @@ export class Salesmate implements INodeType { qs.sortOrder = options.sortOrder as string; } if (options.fields) { + if ((options.fields as string).trim() === '') { + throw new Error('You have to add at least one field'); + } body.fields = (options.fields as string).split(',') as string[]; } else { - throw new Error('You have to add at least one field'); + body.fields = [ + 'title', + 'dueDate', + 'description', + 'duration', + 'owner', + 'Deal.title', + 'PrimaryContact.name', + 'PrimaryContact.email', + 'PrimaryCompany.name', + 'PrimaryCompany.email', + 'tags', + 'type', + 'createdAt', + 'isCompleted', + ]; } if (!jsonActive) { const filters: IDataObject[] = []; @@ -483,7 +513,7 @@ export class Salesmate implements INodeType { }; filter.condition = condition.condition; filter.data = condition.value; - filters.push(filter) + filters.push(filter); } } } @@ -617,12 +647,9 @@ export class Salesmate implements INodeType { const rawData = this.getNodeParameter('rawData', i) as boolean; responseData = await salesmateApiRequest.call(this, 'GET', `/v1/deals/${dealId}`); responseData = responseData.Data; + if (!rawData) { - responseData = responseData.map((deal: IDataObject) => { - const aux: IDataObject = {}; - aux[deal.fieldName as string] = deal.value; - return aux; - }); + responseData = simplifySalesmateData(responseData); } } if (operation === 'getAll') { @@ -641,10 +668,26 @@ export class Salesmate implements INodeType { if (options.sortOrder) { qs.sortOrder = options.sortOrder as string; } - if (options.fields) { + if (options.fields !== undefined) { + if ((options.fields as string).trim() === '') { + throw new Error('You have to add at least one field'); + } body.fields = (options.fields as string).split(',') as string[]; } else { - throw new Error('You have to add at least one field'); + body.fields = [ + 'title', + 'PrimaryContact.name', + 'PrimaryContact.email', + 'PrimaryCompany.name', + 'PrimaryCompany.email', + 'dealValue', + 'priority', + 'stage', + 'status', + 'owner', + 'tags', + 'createdAt', + ]; } if (!jsonActive) { const filters: IDataObject[] = []; @@ -660,7 +703,7 @@ export class Salesmate implements INodeType { }; filter.condition = condition.condition; filter.data = condition.value; - filters.push(filter) + filters.push(filter); } } } From aa1899f81d70489740aa1f05706894dcab4625a0 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 22 Jan 2020 15:06:43 -0800 Subject: [PATCH 13/41] :zap: Prepare webhooks for multitenancy --- packages/cli/src/ActiveWorkflowRunner.ts | 48 +++++++------ packages/cli/src/Server.ts | 13 +++- packages/cli/src/TestWebhooks.ts | 53 ++++++++++++--- packages/cli/src/WebhookHelpers.ts | 18 ++--- packages/core/src/ActiveWebhooks.ts | 87 ++++++++---------------- packages/core/src/ActiveWorkflows.ts | 5 +- packages/core/src/Interfaces.ts | 2 - packages/workflow/src/Interfaces.ts | 2 +- packages/workflow/src/NodeHelpers.ts | 2 +- packages/workflow/src/Workflow.ts | 35 ---------- 10 files changed, 121 insertions(+), 144 deletions(-) diff --git a/packages/cli/src/ActiveWorkflowRunner.ts b/packages/cli/src/ActiveWorkflowRunner.ts index 9604fd9de6..25255988dc 100644 --- a/packages/cli/src/ActiveWorkflowRunner.ts +++ b/packages/cli/src/ActiveWorkflowRunner.ts @@ -117,22 +117,24 @@ export class ActiveWorkflowRunner { throw new ResponseHelper.ResponseError('The requested webhook is not registred.', 404, 404); } + const workflowData = await Db.collections.Workflow!.findOne(webhookData.workflowId); + if (workflowData === undefined) { + throw new ResponseHelper.ResponseError(`Could not find workflow with id "${webhookData.workflowId}"`, 404, 404); + } + + const nodeTypes = NodeTypes(); + const workflow = new Workflow(webhookData.workflowId, workflowData.nodes, workflowData.connections, workflowData.active, nodeTypes, workflowData.staticData, workflowData.settings); + // Get the node which has the webhook defined to know where to start from and to // get additional data - const workflowStartNode = webhookData.workflow.getNode(webhookData.node); + const workflowStartNode = workflow.getNode(webhookData.node); if (workflowStartNode === null) { throw new ResponseHelper.ResponseError('Could not find node to process webhook.', 404, 404); } - const executionMode = 'webhook'; - - const workflowData = await Db.collections.Workflow!.findOne(webhookData.workflow.id!); - - if (workflowData === undefined) { - throw new ResponseHelper.ResponseError(`Could not find workflow with id "${webhookData.workflow.id}"`, 404, 404); - } return new Promise((resolve, reject) => { - WebhookHelpers.executeWebhook(webhookData, workflowData, workflowStartNode, executionMode, undefined, req, res, (error: Error | null, data: object) => { + const executionMode = 'webhook'; + WebhookHelpers.executeWebhook(workflow, webhookData, workflowData, workflowStartNode, executionMode, undefined, req, res, (error: Error | null, data: object) => { if (error !== null) { return reject(error); } @@ -202,7 +204,9 @@ export class ActiveWorkflowRunner { const webhooks = WebhookHelpers.getWorkflowWebhooks(workflow, additionalData); for (const webhookData of webhooks) { - await this.activeWebhooks!.add(webhookData, mode); + await this.activeWebhooks!.add(workflow, webhookData, mode); + // Save static data! + await WorkflowHelpers.saveStaticData(workflow); } } @@ -214,8 +218,19 @@ export class ActiveWorkflowRunner { * @returns * @memberof ActiveWorkflowRunner */ - removeWorkflowWebhooks(workflowId: string): Promise { - return this.activeWebhooks!.removeByWorkflowId(workflowId); + async removeWorkflowWebhooks(workflowId: string): Promise { + const workflowData = await Db.collections.Workflow!.findOne(workflowId); + if (workflowData === undefined) { + throw new Error(`Could not find workflow with id "${workflowId}"`); + } + + const nodeTypes = NodeTypes(); + const workflow = new Workflow(workflowId, workflowData.nodes, workflowData.connections, workflowData.active, nodeTypes, workflowData.staticData, workflowData.settings); + + await this.activeWebhooks!.removeWorkflow(workflow); + + // Save the static workflow data if needed + await WorkflowHelpers.saveStaticData(workflow); } @@ -348,7 +363,7 @@ export class ActiveWorkflowRunner { await this.activeWorkflows.add(workflowId, workflowInstance, additionalData, getTriggerFunctions, getPollFunctions); if (this.activationErrors[workflowId] !== undefined) { - // If there were any activation errors delete them + // If there were activation errors delete them delete this.activationErrors[workflowId]; } } catch (error) { @@ -380,16 +395,9 @@ export class ActiveWorkflowRunner { */ async remove(workflowId: string): Promise { if (this.activeWorkflows !== null) { - const workflowData = this.activeWorkflows.get(workflowId); - // Remove all the webhooks of the workflow await this.removeWorkflowWebhooks(workflowId); - if (workflowData) { - // Save the static workflow data if needed - await WorkflowHelpers.saveStaticData(workflowData.workflow); - } - if (this.activationErrors[workflowId] !== undefined) { // If there were any activation errors delete them delete this.activationErrors[workflowId]; diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 8fff1ef217..de561725ed 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -132,7 +132,7 @@ class App { const authIgnoreRegex = new RegExp(`^\/(rest|healthz|${this.endpointWebhook}|${this.endpointWebhookTest})\/?.*$`); // Check for basic auth credentials if activated - const basicAuthActive = config.get('security.basicAuth.active') as boolean; + const basicAuthActive = config.get('security.basicAuth.active') as boolean; if (basicAuthActive === true) { const basicAuthUser = await GenericHelpers.getConfigValue('security.basicAuth.user') as string; if (basicAuthUser === '') { @@ -1072,7 +1072,16 @@ class App { // Removes a test webhook this.app.delete('/rest/test-webhook/:id', ResponseHelper.send(async (req: express.Request, res: express.Response): Promise => { const workflowId = req.params.id; - return this.testWebhooks.cancelTestWebhook(workflowId); + + const workflowData = await Db.collections.Workflow!.findOne(workflowId); + if (workflowData === undefined) { + throw new ResponseHelper.ResponseError(`Could not find workflow with id "${workflowId}" so webhook could not be deleted!`); + } + + const nodeTypes = NodeTypes(); + const workflow = new Workflow(workflowId.toString(), workflowData.nodes, workflowData.connections, workflowData.active, nodeTypes, workflowData.staticData, workflowData.settings); + + return this.testWebhooks.cancelTestWebhook(workflowId, workflow); })); diff --git a/packages/cli/src/TestWebhooks.ts b/packages/cli/src/TestWebhooks.ts index 540aa9e0f8..8631f259ab 100644 --- a/packages/cli/src/TestWebhooks.ts +++ b/packages/cli/src/TestWebhooks.ts @@ -1,11 +1,18 @@ import * as express from 'express'; +import { + In as findIn, + FindManyOptions, +} from 'typeorm'; import { + Db, IResponseCallbackData, + IWorkflowDb, + NodeTypes, Push, ResponseHelper, WebhookHelpers, - IWorkflowDb, + WorkflowHelpers, } from './'; import { @@ -60,9 +67,17 @@ export class TestWebhooks { throw new ResponseHelper.ResponseError('The requested webhook is not registred.', 404, 404); } + const workflowData = await Db.collections.Workflow!.findOne(webhookData.workflowId); + if (workflowData === undefined) { + throw new ResponseHelper.ResponseError(`Could not find workflow with id "${webhookData.workflowId}"`, 404, 404); + } + + const nodeTypes = NodeTypes(); + const workflow = new Workflow(webhookData.workflowId, workflowData.nodes, workflowData.connections, workflowData.active, nodeTypes, workflowData.staticData, workflowData.settings); + // Get the node which has the webhook defined to know where to start from and to // get additional data - const workflowStartNode = webhookData.workflow.getNode(webhookData.node); + const workflowStartNode = workflow.getNode(webhookData.node); if (workflowStartNode === null) { throw new ResponseHelper.ResponseError('Could not find node to process webhook.', 404, 404); } @@ -72,8 +87,7 @@ export class TestWebhooks { return new Promise(async (resolve, reject) => { try { const executionMode = 'manual'; - - const executionId = await WebhookHelpers.executeWebhook(webhookData, this.testWebhookData[webhookKey].workflowData, workflowStartNode, executionMode, this.testWebhookData[webhookKey].sessionId, request, response, (error: Error | null, data: IResponseCallbackData) => { + const executionId = await WebhookHelpers.executeWebhook(workflow, webhookData, this.testWebhookData[webhookKey].workflowData, workflowStartNode, executionMode, this.testWebhookData[webhookKey].sessionId, request, response, (error: Error | null, data: IResponseCallbackData) => { if (error !== null) { return reject(error); } @@ -90,7 +104,7 @@ export class TestWebhooks { // Inform editor-ui that webhook got received if (this.testWebhookData[webhookKey].sessionId !== undefined) { const pushInstance = Push.getInstance(); - pushInstance.send('testWebhookReceived', { workflowId: webhookData.workflow.id, executionId }, this.testWebhookData[webhookKey].sessionId!); + pushInstance.send('testWebhookReceived', { workflowId: webhookData.workflowId, executionId }, this.testWebhookData[webhookKey].sessionId!); } } catch (error) { @@ -100,7 +114,7 @@ export class TestWebhooks { // Remove the webhook clearTimeout(this.testWebhookData[webhookKey].timeout); delete this.testWebhookData[webhookKey]; - this.activeWebhooks!.removeByWorkflowId(webhookData.workflow.id!.toString()); + this.activeWebhooks!.removeWorkflow(workflow); }); } @@ -125,7 +139,7 @@ export class TestWebhooks { // Remove test-webhooks automatically if they do not get called (after 120 seconds) const timeout = setTimeout(() => { - this.cancelTestWebhook(workflowData.id.toString()); + this.cancelTestWebhook(workflowData.id.toString(), workflow); }, 120000); let key: string; @@ -136,9 +150,12 @@ export class TestWebhooks { timeout, workflowData, }; - await this.activeWebhooks!.add(webhookData, mode); + await this.activeWebhooks!.add(workflow, webhookData, mode); } + // Save static data! + await WorkflowHelpers.saveStaticData(workflow); + return true; } @@ -150,7 +167,7 @@ export class TestWebhooks { * @returns {boolean} * @memberof TestWebhooks */ - cancelTestWebhook(workflowId: string): boolean { + cancelTestWebhook(workflowId: string, workflow: Workflow): boolean { let foundWebhook = false; for (const webhookKey of Object.keys(this.testWebhookData)) { const webhookData = this.testWebhookData[webhookKey]; @@ -175,7 +192,7 @@ export class TestWebhooks { // Remove the webhook delete this.testWebhookData[webhookKey]; - this.activeWebhooks!.removeByWorkflowId(workflowId); + this.activeWebhooks!.removeWorkflow(workflow); } return foundWebhook; @@ -189,8 +206,22 @@ export class TestWebhooks { if (this.activeWebhooks === null) { return; } + const nodeTypes = NodeTypes(); - return this.activeWebhooks.removeAll(); + const findQuery = { + where: { + id: findIn(this.activeWebhooks.getWorkflowIds()) + }, + } as FindManyOptions; + + const workflowsDb = await Db.collections.Workflow!.find(findQuery); + const workflows: Workflow[] = []; + for (const workflowData of workflowsDb) { + const workflow = new Workflow(workflowData.id.toString(), workflowData.nodes, workflowData.connections, workflowData.active, nodeTypes, workflowData.staticData, workflowData.settings); + workflows.push(workflow); + } + + return this.activeWebhooks.removeAll(workflows); } } diff --git a/packages/cli/src/WebhookHelpers.ts b/packages/cli/src/WebhookHelpers.ts index b13cc3b0ea..da95f7416a 100644 --- a/packages/cli/src/WebhookHelpers.ts +++ b/packages/cli/src/WebhookHelpers.ts @@ -84,9 +84,9 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo * @param {((error: Error | null, data: IResponseCallbackData) => void)} responseCallback * @returns {(Promise)} */ - export async function executeWebhook(webhookData: IWebhookData, workflowData: IWorkflowDb, workflowStartNode: INode, executionMode: WorkflowExecuteMode, sessionId: string | undefined, req: express.Request, res: express.Response, responseCallback: (error: Error | null, data: IResponseCallbackData) => void): Promise { + export async function executeWebhook(workflow: Workflow, webhookData: IWebhookData, workflowData: IWorkflowDb, workflowStartNode: INode, executionMode: WorkflowExecuteMode, sessionId: string | undefined, req: express.Request, res: express.Response, responseCallback: (error: Error | null, data: IResponseCallbackData) => void): Promise { // Get the nodeType to know which responseMode is set - const nodeType = webhookData.workflow.nodeTypes.getByName(workflowStartNode.type); + const nodeType = workflow.nodeTypes.getByName(workflowStartNode.type); if (nodeType === undefined) { const errorMessage = `The type of the webhook node "${workflowStartNode.name}" is not known.`; responseCallback(new Error(errorMessage), {}); @@ -94,8 +94,8 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo } // Get the responseMode - const responseMode = webhookData.workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseMode'], 'onReceived'); - const responseCode = webhookData.workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseCode'], 200) as number; + const responseMode = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseMode'], 'onReceived'); + const responseCode = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseCode'], 200) as number; if (!['onReceived', 'lastNode'].includes(responseMode as string)) { // If the mode is not known we error. Is probably best like that instead of using @@ -122,7 +122,7 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo let webhookResultData: IWebhookResponseData; try { - webhookResultData = await webhookData.workflow.runWebhook(webhookData, workflowStartNode, additionalData, NodeExecuteFunctions, executionMode); + webhookResultData = await workflow.runWebhook(webhookData, workflowStartNode, additionalData, NodeExecuteFunctions, executionMode); } catch (e) { // Send error response to webhook caller const errorMessage = 'Workflow Webhook Error: Workflow could not be started!'; @@ -287,7 +287,7 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo return data; } - const responseData = webhookData.workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseData'], 'firstEntryJson'); + const responseData = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseData'], 'firstEntryJson'); if (didSendResponse === false) { let data: IDataObject | IDataObject[]; @@ -296,13 +296,13 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo // Return the JSON data of the first entry data = returnData.data!.main[0]![0].json; - const responsePropertyName = webhookData.workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responsePropertyName'], undefined); + const responsePropertyName = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responsePropertyName'], undefined); if (responsePropertyName !== undefined) { data = get(data, responsePropertyName as string) as IDataObject; } - const responseContentType = webhookData.workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseContentType'], undefined); + const responseContentType = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseContentType'], undefined); if (responseContentType !== undefined) { // Send the webhook response manually to be able to set the content-type @@ -329,7 +329,7 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo didSendResponse = true; } - const responseBinaryPropertyName = webhookData.workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseBinaryPropertyName'], 'data'); + const responseBinaryPropertyName = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseBinaryPropertyName'], 'data'); if (responseBinaryPropertyName === undefined && didSendResponse === false) { responseCallback(new Error('No "responseBinaryPropertyName" is set.'), {}); diff --git a/packages/core/src/ActiveWebhooks.ts b/packages/core/src/ActiveWebhooks.ts index 6468044d47..17cf753830 100644 --- a/packages/core/src/ActiveWebhooks.ts +++ b/packages/core/src/ActiveWebhooks.ts @@ -1,6 +1,7 @@ import { IWebhookData, WebhookHttpMethod, + Workflow, WorkflowExecuteMode, } from 'n8n-workflow'; @@ -29,29 +30,26 @@ export class ActiveWebhooks { * @returns {Promise} * @memberof ActiveWebhooks */ - async add(webhookData: IWebhookData, mode: WorkflowExecuteMode): Promise { - if (webhookData.workflow.id === undefined) { + async add(workflow: Workflow, webhookData: IWebhookData, mode: WorkflowExecuteMode): Promise { + if (workflow.id === undefined) { throw new Error('Webhooks can only be added for saved workflows as an id is needed!'); } - if (this.workflowWebhooks[webhookData.workflow.id] === undefined) { - this.workflowWebhooks[webhookData.workflow.id] = []; + if (this.workflowWebhooks[webhookData.workflowId] === undefined) { + this.workflowWebhooks[webhookData.workflowId] = []; } // Make the webhook available directly because sometimes to create it successfully // it gets called this.webhookUrls[this.getWebhookKey(webhookData.httpMethod, webhookData.path)] = webhookData; - const webhookExists = await webhookData.workflow.runWebhookMethod('checkExists', webhookData, NodeExecuteFunctions, mode, this.testWebhooks); + const webhookExists = await workflow.runWebhookMethod('checkExists', webhookData, NodeExecuteFunctions, mode, this.testWebhooks); if (webhookExists === false) { // If webhook does not exist yet create it - await webhookData.workflow.runWebhookMethod('create', webhookData, NodeExecuteFunctions, mode, this.testWebhooks); + await workflow.runWebhookMethod('create', webhookData, NodeExecuteFunctions, mode, this.testWebhooks); } - // Run the "activate" hooks on the nodes - await webhookData.workflow.runNodeHooks('activate', webhookData, NodeExecuteFunctions, mode); - - this.workflowWebhooks[webhookData.workflow.id].push(webhookData); + this.workflowWebhooks[webhookData.workflowId].push(webhookData); } @@ -73,6 +71,17 @@ export class ActiveWebhooks { } + /** + * Returns the ids of all the workflows which have active webhooks + * + * @returns {string[]} + * @memberof ActiveWebhooks + */ + getWorkflowIds(): string[] { + return Object.keys(this.workflowWebhooks); + } + + /** * Returns key to uniquely identify a webhook * @@ -89,11 +98,13 @@ export class ActiveWebhooks { /** * Removes all webhooks of a workflow * - * @param {string} workflowId + * @param {Workflow} workflow * @returns {boolean} * @memberof ActiveWebhooks */ - async removeByWorkflowId(workflowId: string): Promise { + async removeWorkflow(workflow: Workflow): Promise { + const workflowId = workflow.id!.toString(); + if (this.workflowWebhooks[workflowId] === undefined) { // If it did not exist then there is nothing to remove return false; @@ -105,10 +116,7 @@ export class ActiveWebhooks { // Go through all the registered webhooks of the workflow and remove them for (const webhookData of webhooks) { - await webhookData.workflow.runWebhookMethod('delete', webhookData, NodeExecuteFunctions, mode, this.testWebhooks); - - // Run the "deactivate" hooks on the nodes - await webhookData.workflow.runNodeHooks('deactivate', webhookData, NodeExecuteFunctions, mode); + await workflow.runWebhookMethod('delete', webhookData, NodeExecuteFunctions, mode, this.testWebhooks); delete this.webhookUrls[this.getWebhookKey(webhookData.httpMethod, webhookData.path)]; } @@ -121,55 +129,16 @@ export class ActiveWebhooks { /** - * Removes all the currently active webhooks + * Removes all the webhooks of the given workflow */ - async removeAll(): Promise { - const workflowIds = Object.keys(this.workflowWebhooks); - + async removeAll(workflows: Workflow[]): Promise { const removePromises = []; - for (const workflowId of workflowIds) { - removePromises.push(this.removeByWorkflowId(workflowId)); + for (const workflow of workflows) { + removePromises.push(this.removeWorkflow(workflow)); } await Promise.all(removePromises); return; } - - // /** - // * Removes a single webhook by its key. - // * Currently not used, runNodeHooks for "deactivate" is missing - // * - // * @param {string} webhookKey - // * @returns {boolean} - // * @memberof ActiveWebhooks - // */ - // removeByWebhookKey(webhookKey: string): boolean { - // if (this.webhookUrls[webhookKey] === undefined) { - // // If it did not exist then there is nothing to remove - // return false; - // } - - // const webhookData = this.webhookUrls[webhookKey]; - - // // Remove from workflow-webhooks - // const workflowWebhooks = this.workflowWebhooks[webhookData.workflowId]; - // for (let index = 0; index < workflowWebhooks.length; index++) { - // if (workflowWebhooks[index].path === webhookData.path) { - // workflowWebhooks.splice(index, 1); - // break; - // } - // } - - // if (workflowWebhooks.length === 0) { - // // When there are no webhooks left for any workflow remove it totally - // delete this.workflowWebhooks[webhookData.workflowId]; - // } - - // // Remove from webhook urls - // delete this.webhookUrls[webhookKey]; - - // return true; - // } - } diff --git a/packages/core/src/ActiveWorkflows.ts b/packages/core/src/ActiveWorkflows.ts index f450a28064..5576aeed3e 100644 --- a/packages/core/src/ActiveWorkflows.ts +++ b/packages/core/src/ActiveWorkflows.ts @@ -69,9 +69,7 @@ export class ActiveWorkflows { async add(id: string, workflow: Workflow, additionalData: IWorkflowExecuteAdditionalData, getTriggerFunctions: IGetExecuteTriggerFunctions, getPollFunctions: IGetExecutePollFunctions): Promise { console.log('ADD ID (active): ' + id); - this.workflowData[id] = { - workflow - }; + this.workflowData[id] = {}; const triggerNodes = workflow.getTriggerNodes(); let triggerResponse: ITriggerResponse | undefined; @@ -170,7 +168,6 @@ export class ActiveWorkflows { const pollResponse = await workflow.runPoll(node, pollFunctions); if (pollResponse !== null) { - // TODO: Run workflow pollFunctions.__emit(pollResponse); } }; diff --git a/packages/core/src/Interfaces.ts b/packages/core/src/Interfaces.ts index a45d393dc3..cfbc19bb8e 100644 --- a/packages/core/src/Interfaces.ts +++ b/packages/core/src/Interfaces.ts @@ -14,7 +14,6 @@ import { ITriggerResponse, IWebhookFunctions as IWebhookFunctionsBase, IWorkflowSettings as IWorkflowSettingsWorkflow, - Workflow, } from 'n8n-workflow'; @@ -125,5 +124,4 @@ export interface INodeInputDataConnections { export interface IWorkflowData { pollResponses?: IPollResponse[]; triggerResponses?: ITriggerResponse[]; - workflow: Workflow; } diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 8fd424ba19..2070215911 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -495,7 +495,7 @@ export interface IWebhookData { node: string; path: string; webhookDescription: IWebhookDescription; - workflow: Workflow; + workflowId: string; workflowExecuteAdditionalData: IWorkflowExecuteAdditionalData; } diff --git a/packages/workflow/src/NodeHelpers.ts b/packages/workflow/src/NodeHelpers.ts index e3c6a79b8a..76d8f8b55f 100644 --- a/packages/workflow/src/NodeHelpers.ts +++ b/packages/workflow/src/NodeHelpers.ts @@ -771,7 +771,7 @@ export function getNodeWebhooks(workflow: Workflow, node: INode, additionalData: node: node.name, path, webhookDescription, - workflow, + workflowId: workflow.id, workflowExecuteAdditionalData: additionalData, }); } diff --git a/packages/workflow/src/Workflow.ts b/packages/workflow/src/Workflow.ts index 1ee7991391..9bb8391aed 100644 --- a/packages/workflow/src/Workflow.ts +++ b/packages/workflow/src/Workflow.ts @@ -907,41 +907,6 @@ export class Workflow { } - /** - * Executes the hooks of the node - * - * @param {string} hookName The name of the hook to execute - * @param {IWebhookData} webhookData - * @param {INodeExecuteFunctions} nodeExecuteFunctions - * @param {WorkflowExecuteMode} mode - * @returns {Promise} - * @memberof Workflow - */ - async runNodeHooks(hookName: string, webhookData: IWebhookData, nodeExecuteFunctions: INodeExecuteFunctions, mode: WorkflowExecuteMode): Promise { - const node = this.getNode(webhookData.node) as INode; - const nodeType = this.nodeTypes.getByName(node.type) as INodeType; - - if (nodeType.description.hooks === undefined) { - return; - } - - - if (nodeType.description.hooks[hookName] === undefined) { - return; - } - - - if (nodeType.hooks === undefined && nodeType.description.hooks[hookName]!.length !== 0) { - // There should be hook functions but they do not exist - throw new Error('There are hooks defined to run but are not implemented.'); - } - - for (const hookDescription of nodeType.description.hooks[hookName]!) { - const thisArgs = nodeExecuteFunctions.getExecuteHookFunctions(this, node, webhookData.workflowExecuteAdditionalData, mode); - await nodeType.hooks![hookDescription.method].call(thisArgs); - } - } - /** * Executes the Webhooks method of the node From 23e844398b8371094a1a981bae4eb1548e504c0a Mon Sep 17 00:00:00 2001 From: trojanh Date: Thu, 23 Jan 2020 20:43:57 +0530 Subject: [PATCH 14/41] Add returnAll and refactoring --- .../nodes-base/nodes/Disqus/Disqus.node.ts | 233 ++++++++++++++---- .../nodes/Disqus/GenericFunctions.ts | 87 +++++++ 2 files changed, 273 insertions(+), 47 deletions(-) create mode 100644 packages/nodes-base/nodes/Disqus/GenericFunctions.ts diff --git a/packages/nodes-base/nodes/Disqus/Disqus.node.ts b/packages/nodes-base/nodes/Disqus/Disqus.node.ts index 85eb133def..213da8ef4b 100644 --- a/packages/nodes-base/nodes/Disqus/Disqus.node.ts +++ b/packages/nodes-base/nodes/Disqus/Disqus.node.ts @@ -9,7 +9,7 @@ import { INodeType, } from 'n8n-workflow'; -import { OptionsWithUri } from 'request'; +import { disqusApiRequest, disqusApiRequestAllItems } from './GenericFunctions'; export class Disqus implements INodeType { @@ -164,6 +164,47 @@ export class Disqus implements INodeType { }, description: 'The short name(aka ID) of the forum to get.', }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'forum', + ], + operation: [ + 'getPosts', + ], + }, + }, + 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: [ + 'forum', + ], + operation: [ + 'getPosts', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 100, + description: 'How many results to return.', + }, { displayName: 'Additional Fields', name: 'additionalFields', @@ -261,6 +302,47 @@ export class Disqus implements INodeType { }, description: 'The short name(aka ID) of the forum to get Categories.', }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'forum', + ], + operation: [ + 'getCategories', + ], + }, + }, + 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: [ + 'forum', + ], + operation: [ + 'getCategories', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 100, + description: 'How many results to return.', + }, { displayName: 'Additional Fields', name: 'additionalFields', @@ -323,6 +405,47 @@ export class Disqus implements INodeType { }, description: 'The short name(aka ID) of the forum to get Threads.', }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'forum', + ], + operation: [ + 'getThreads', + ], + }, + }, + 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: [ + 'forum', + ], + operation: [ + 'getThreads', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 100, + description: 'How many results to return.', + }, { displayName: 'Additional Fields', name: 'additionalFields', @@ -399,11 +522,6 @@ export class Disqus implements INodeType { const items = this.getInputData(); const returnData: IDataObject[] = []; - const credentials = this.getCredentials('disqusApi'); - - if (credentials === undefined) { - throw new Error('No credentials got returned!'); - } const resource = this.getNodeParameter('resource', 0) as string; const operation = this.getNodeParameter('operation', 0) as string; @@ -435,6 +553,13 @@ export class Disqus implements INodeType { Object.assign(qs, additionalFields); + try { + const responseData = await disqusApiRequest.call(this, requestMethod, qs, endpoint); + returnData.push(responseData.response); + } catch (error) { + throw error; + } + } else if (operation === 'getPosts') { // ---------------------------------- // getPosts @@ -445,12 +570,28 @@ export class Disqus implements INodeType { endpoint = 'forums/listPosts.json'; const id = this.getNodeParameter('id', i) as string; - qs.forum = id; - const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; - Object.assign(qs, additionalFields); + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + qs.forum = id; + qs.limit = 100; + + try { + let responseData: IDataObject = {}; + if(returnAll) { + responseData.response = await disqusApiRequestAllItems.call(this, requestMethod, qs, endpoint); + } else { + const limit = this.getNodeParameter('limit', i) as string; + qs.limit = limit; + responseData = await disqusApiRequest.call(this, requestMethod, qs, endpoint); + } + returnData.push.apply(returnData, responseData.response as IDataObject[]); + } catch (error) { + throw error; + } + } else if (operation === 'getCategories') { // ---------------------------------- // getCategories @@ -461,12 +602,28 @@ export class Disqus implements INodeType { endpoint = 'forums/listCategories.json'; const id = this.getNodeParameter('id', i) as string; - qs.forum = id; - + const returnAll = this.getNodeParameter('returnAll', i) as boolean; const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; - Object.assign(qs, additionalFields); + qs.forum = id; + qs.limit = 100; + + try { + let responseData: IDataObject = {}; + + if(returnAll) { + responseData.response = await disqusApiRequestAllItems.call(this, requestMethod, qs, endpoint); + } else { + const limit = this.getNodeParameter('limit', i) as string; + qs.limit = limit; + responseData = await disqusApiRequest.call(this, requestMethod, qs, endpoint) as IDataObject; + } + returnData.push.apply(returnData, responseData.response as IDataObject[]) ; + } catch (error) { + throw error; + } + } else if (operation === 'getThreads') { // ---------------------------------- // getThreads @@ -477,12 +634,29 @@ export class Disqus implements INodeType { endpoint = 'forums/listThreads.json'; const id = this.getNodeParameter('id', i) as string; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + qs.forum = id; + qs.limit = 100; const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; Object.assign(qs, additionalFields); + try { + let responseData: IDataObject = {}; + if(returnAll) { + responseData.response = await disqusApiRequestAllItems.call(this, requestMethod, qs, endpoint); + } else { + const limit = this.getNodeParameter('limit', i) as string; + qs.limit = limit; + responseData = await disqusApiRequest.call(this, requestMethod, qs, endpoint); + } + returnData.push.apply(returnData, responseData.response as IDataObject[]); + } catch (error) { + throw error; + } + } else { throw new Error(`The operation "${operation}" is not known!`); } @@ -490,41 +664,6 @@ export class Disqus implements INodeType { } else { throw new Error(`The resource "${resource}" is not known!`); } - - qs.api_key = credentials.accessToken; - endpoint = `https://disqus.com/api/3.0/${endpoint}`; - - const options: OptionsWithUri = { - method: requestMethod, - qs, - uri: endpoint, - json: true, - }; - - - let responseData; - try { - responseData = await this.helpers.request(options); - } catch (error) { - if (error.statusCode === 401) { - // Return a clear error - throw new Error('The Disqus credentials are not valid!'); - } - - if (error.error && error.error.error_summary) { - // Try to return the error prettier - throw new Error(`Disqus error response [${error.statusCode}]: ${error.error.error_summary}`); - } - - // If that data does not exist for some reason return the actual error - throw error; - } - - if (responseData && 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/Disqus/GenericFunctions.ts b/packages/nodes-base/nodes/Disqus/GenericFunctions.ts new file mode 100644 index 0000000000..e5e5c1b7df --- /dev/null +++ b/packages/nodes-base/nodes/Disqus/GenericFunctions.ts @@ -0,0 +1,87 @@ +import { OptionsWithUri } from 'request'; +import { + IExecuteFunctions, + IExecuteSingleFunctions, + IHookFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; +import { IDataObject } from 'n8n-workflow'; + +export async function disqusApiRequest( + this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, + method: string, + qs: IDataObject = {}, + uri?: string, + body: any = {}, + option: IDataObject = {} + ): Promise { // tslint:disable-line:no-any + + const credentials = this.getCredentials('disqusApi') as IDataObject; + qs.api_key = credentials.accessToken; + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + let options: OptionsWithUri = { + method, + qs, + body, + uri: `https://disqus.com/api/3.0/${uri}`, + json: true + }; + + options = Object.assign({}, options, option); + if (Object.keys(options.body).length === 0) { + delete options.body; + } + try { + const result = await this.helpers.request!(options) + return result; + } catch (error) { + console.log(error) + if (error.statusCode === 401) { + // Return a clear error + throw new Error('The Disqus credentials are not valid!'); + } + + if (error.error && error.error.error_summary) { + // Try to return the error prettier + throw new Error(`Disqus error response [${error.statusCode}]: ${error.error.error_summary}`); + } + + // If that data does not exist for some reason return the actual error + throw error; + } +} + +/** + * Make an API request to paginated flow endpoint + * and return all results + */ +export async function disqusApiRequestAllItems( + this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, + method: string, + qs: IDataObject = {}, + uri?: string, + body: any = {}, + option: IDataObject = {} + ): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + + try { + do { + responseData = await disqusApiRequest.call(this, method, qs, uri, body, option); + qs.cursor = responseData.cursor.id; + returnData.push.apply(returnData, responseData.response); + } while ( + responseData.cursor.more === true && + responseData.cursor.hasNext === true + ); + return returnData; + } catch(error) { + throw error; + } +} From 70e26681a483181ff2c8daa7378724f4ef721cf3 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Thu, 23 Jan 2020 12:27:28 -0500 Subject: [PATCH 15/41] :sparkles: segment node --- .../credentials/SegmentApi.credentials.ts | 17 + .../nodes/Segment/GenericFunctions.ts | 36 + .../nodes/Segment/IdentifyDescription.ts | 508 ++++++++ .../nodes/Segment/IdentifyInterface.ts | 10 + .../nodes-base/nodes/Segment/Segment.node.ts | 784 +++++++++++ .../nodes/Segment/TrackDescription.ts | 1155 +++++++++++++++++ .../nodes/Segment/TrackInterface.ts | 13 + packages/nodes-base/nodes/Segment/segment.png | Bin 0 -> 1144 bytes packages/nodes-base/package.json | 4 + 9 files changed, 2527 insertions(+) create mode 100644 packages/nodes-base/credentials/SegmentApi.credentials.ts create mode 100644 packages/nodes-base/nodes/Segment/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Segment/IdentifyDescription.ts create mode 100644 packages/nodes-base/nodes/Segment/IdentifyInterface.ts create mode 100644 packages/nodes-base/nodes/Segment/Segment.node.ts create mode 100644 packages/nodes-base/nodes/Segment/TrackDescription.ts create mode 100644 packages/nodes-base/nodes/Segment/TrackInterface.ts create mode 100644 packages/nodes-base/nodes/Segment/segment.png diff --git a/packages/nodes-base/credentials/SegmentApi.credentials.ts b/packages/nodes-base/credentials/SegmentApi.credentials.ts new file mode 100644 index 0000000000..45858d3277 --- /dev/null +++ b/packages/nodes-base/credentials/SegmentApi.credentials.ts @@ -0,0 +1,17 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class SegmentApi implements ICredentialType { + name = 'segmentApi'; + displayName = 'Segment API'; + properties = [ + { + displayName: 'Write Key', + name: 'writekey', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Segment/GenericFunctions.ts b/packages/nodes-base/nodes/Segment/GenericFunctions.ts new file mode 100644 index 0000000000..bd3ba6b877 --- /dev/null +++ b/packages/nodes-base/nodes/Segment/GenericFunctions.ts @@ -0,0 +1,36 @@ +import { OptionsWithUri } from 'request'; +import { + IExecuteFunctions, + IExecuteSingleFunctions, + IHookFunctions, + ILoadOptionsFunctions, + IWebhookFunctions, +} from 'n8n-core'; +import { IDataObject } from 'n8n-workflow'; + +export async function segmentApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + const credentials = this.getCredentials('segmentApi'); + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + const base64Key = Buffer.from(`${credentials.writekey}:`).toString('base64'); + const options: OptionsWithUri = { + headers: { + Authorization: `Basic ${base64Key}`, + 'Content-Type': 'application/json', + }, + method, + qs, + body, + uri: uri ||`https://api.segment.io/v1${resource}`, + json: true + }; + if (!Object.keys(body).length) { + delete options.body; + } + try { + return await this.helpers.request!(options); + } catch (error) { + throw new Error('Segment Error: ' + error); + } +} diff --git a/packages/nodes-base/nodes/Segment/IdentifyDescription.ts b/packages/nodes-base/nodes/Segment/IdentifyDescription.ts new file mode 100644 index 0000000000..750a4d03d4 --- /dev/null +++ b/packages/nodes-base/nodes/Segment/IdentifyDescription.ts @@ -0,0 +1,508 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const identifyOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'identify', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an identity', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const identifyFields = [ + +/* -------------------------------------------------------------------------- */ +/* identify:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'User ID', + name: 'userId', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'identify', + ], + operation: [ + 'create', + ], + }, + }, + required: false, + }, + { + displayName: 'Traits', + name: 'traits', + placeholder: 'Add Trait', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'identify', + ], + operation: [ + 'create', + ], + }, + }, + default: {}, + options: [ + { + name: 'traitsUi', + displayName: 'Trait', + values: [ + { + displayName: 'Email', + name: 'emaiL', + type: 'string', + default: '', + description: 'Email address of a user', + }, + { + displayName: 'First Name', + name: 'firstname', + type: 'string', + default: '', + description: 'First name of a user', + }, + { + displayName: 'Last Name', + name: 'lastname', + type: 'string', + default: '', + description: 'Last name of a user', + }, + { + displayName: 'Gender', + name: 'gender', + type: 'string', + default: '', + description: 'Gender of a user', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + description: 'Phone number of a user', + }, + { + displayName: 'Username', + name: 'username', + type: 'string', + default: '', + description: 'User’s username', + }, + { + displayName: 'Website', + name: 'website', + type: 'string', + default: '', + description: 'Website of a user', + }, + { + displayName: 'Age', + name: 'age', + type: 'number', + default: 1, + description: 'Age of a user', + }, + { + displayName: 'Avatar', + name: 'avatar', + type: 'string', + default: '', + description: 'URL to an avatar image for the user', + }, + { + displayName: 'Birthday', + name: 'birthday', + type: 'dateTime', + default: '', + description: 'User’s birthday', + }, + { + displayName: 'Created At', + name: 'createdAt', + type: 'dateTime', + default: '', + description: 'Date the user’s account was first created', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Description of the user', + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + description: 'Unique ID in your database for a user', + }, + { + displayName: 'Company', + name: 'company', + placeholder: 'Add Company', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'companyUi', + displayName: 'Company', + values: [ + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Industry', + name: 'industry', + type: 'string', + default: '', + }, + { + displayName: 'Employee Count', + name: 'employeeCount', + type: 'number', + default: 1, + }, + { + displayName: 'Plan', + name: 'plan', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Address', + name: 'address', + placeholder: 'Add Address', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'addressUi', + displayName: 'Address', + values: [ + { + displayName: 'Street', + name: 'street', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + }, + { + displayName: 'Postal Code', + name: 'postalCode', + type: 'string', + default: '', + }, + { + displayName: 'Country', + name: 'country', + type: 'string', + default: '', + }, + ] + }, + ], + }, + ] + }, + ], + }, + { + displayName: 'Context', + name: 'context', + placeholder: 'Add Context', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'identify', + ], + operation: [ + 'create', + ], + }, + }, + default: {}, + options: [ + { + name: 'contextUi', + displayName: 'Context', + values: [ + { + displayName: 'Active', + name: 'active', + type: 'boolean', + default: '', + description: 'Whether a user is active', + }, + { + displayName: 'IP', + name: 'ip', + type: 'string', + default: '', + description: 'Current user’s IP address.', + }, + { + displayName: 'Locale', + name: 'locate', + type: 'string', + default: '', + description: 'Locale string for the current user, for example en-US.', + }, + { + displayName: 'Page', + name: 'page', + type: 'string', + default: '', + description: 'Dictionary of information about the current page in the browser, containing hash, path, referrer, search, title and url', + }, + { + displayName: 'Timezone', + name: 'timezone', + type: 'string', + default: '', + description: 'Timezones are sent as tzdata strings to add user timezone information which might be stripped from the timestamp, for example America/New_York', + }, + { + displayName: 'App', + name: 'app', + placeholder: 'Add App', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'appUi', + displayName: 'App', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Version', + name: 'version', + type: 'string', + default: '', + }, + { + displayName: 'Build', + name: 'build', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Campaign', + name: 'campaign', + placeholder: 'Campaign App', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'campaignUi', + displayName: 'Campaign', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Source', + name: 'source', + type: 'string', + default: '', + }, + { + displayName: 'Medium', + name: 'medium', + type: 'string', + default: '', + }, + { + displayName: 'Term', + name: 'term', + type: 'string', + default: '', + }, + { + displayName: 'Content', + name: 'content', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Device', + name: 'device', + placeholder: 'Add Device', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'deviceUi', + displayName: 'Device', + values: [ + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + }, + { + displayName: 'Manufacturer', + name: 'manufacturer', + type: 'string', + default: '', + }, + { + displayName: 'Model', + name: 'model', + type: 'string', + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Type', + name: 'type', + type: 'string', + default: '', + }, + { + displayName: 'Version', + name: 'version', + type: 'string', + default: '', + }, + ], + }, + ], + }, + ] + }, + ], + }, + { + displayName: 'Integration', + name: 'integrations', + placeholder: 'Add Integration', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'identify', + ], + operation: [ + 'create', + ], + }, + }, + default: {}, + options: [ + { + name: 'integrationsUi', + displayName: 'Integration', + values: [ + { + displayName: 'All', + name: 'all', + type: 'boolean', + default: true, + }, + { + displayName: 'Salesforce', + name: 'salesforce', + type: 'boolean', + default: false, + }, + ], + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Segment/IdentifyInterface.ts b/packages/nodes-base/nodes/Segment/IdentifyInterface.ts new file mode 100644 index 0000000000..ae3d1e4c7b --- /dev/null +++ b/packages/nodes-base/nodes/Segment/IdentifyInterface.ts @@ -0,0 +1,10 @@ +import { IDataObject } from "n8n-workflow"; + +export interface IIdentify { + userId?: string; + anonymousId?: string; + traits?: IDataObject; + context?: IDataObject; + integrations?: IDataObject; + timestamp?: string; +} diff --git a/packages/nodes-base/nodes/Segment/Segment.node.ts b/packages/nodes-base/nodes/Segment/Segment.node.ts new file mode 100644 index 0000000000..f34f1c266b --- /dev/null +++ b/packages/nodes-base/nodes/Segment/Segment.node.ts @@ -0,0 +1,784 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; +import { + IDataObject, + INodeExecutionData, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; +import { + segmentApiRequest, +} from './GenericFunctions'; +import { + identifyFields, + identifyOperations, +} from './IdentifyDescription'; +import { + IIdentify, +} from './IdentifyInterface'; +import { + trackOperations, + trackFields, +} from './TrackDescription'; +import { ITrack } from './TrackInterface'; +import * as uuid from 'uuid/v4'; + +export class Segment implements INodeType { + description: INodeTypeDescription = { + displayName: 'Segment', + name: 'segment', + icon: 'file:segment.png', + group: ['output'], + version: 1, + subtitle: '={{$parameter["operation"] + ":" + $parameter["resource"]}}', + description: 'Consume Segment API', + defaults: { + name: 'Segment', + color: '#6ebb99', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'segmentApi', + required: true, + } + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Identify', + value: 'identify', + description: 'Identify lets you tie a user to their actions.' + }, + { + name: 'Track', + value: 'track', + description: 'Track lets you record events', + }, + ], + default: 'identify', + description: 'Resource to consume.', + }, + ...identifyOperations, + ...trackOperations, + ...identifyFields, + ...trackFields, + ], + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + const length = items.length as unknown as number; + const qs: IDataObject = {}; + let responseData; + 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 === 'identify') { + //https://segment.com/docs/connections/sources/catalog/libraries/server/http-api/#identify + if (operation === 'create') { + const userId = this.getNodeParameter('userId', i) as string; + const traits = (this.getNodeParameter('traits', i) as IDataObject).traitsUi as IDataObject; + const context = (this.getNodeParameter('context', i) as IDataObject).contextUi as IDataObject; + const integrations = (this.getNodeParameter('integrations', i) as IDataObject).integrationsUi as IDataObject; + const body: IIdentify = { + traits: { + company: {}, + address: {}, + }, + context: { + app: {}, + campaign: {}, + device: {}, + }, + integrations: {}, + }; + if (userId) { + body.userId = userId as string; + } else { + body.anonymousId = uuid(); + } + if (traits) { + if (traits.email) { + body.traits!.email = traits.email as string; + } + if (traits.firstname) { + body.traits!.firstname = traits.firstname as string; + } + if (traits.lastname) { + body.traits!.lastname = traits.lastname as string; + } + if (traits.gender) { + body.traits!.gender = traits.gender as string; + } + if (traits.phone) { + body.traits!.phone = traits.phone as string; + } + if (traits.username) { + body.traits!.username = traits.username as string; + } + if (traits.website) { + body.traits!.website = traits.website as string; + } + if (traits.age) { + body.traits!.age = traits.age as number; + } + if (traits.avatar) { + body.traits!.avatar = traits.avatar as string; + } + if (traits.birthday) { + body.traits!.birthday = traits.birthday as string; + } + if (traits.createdAt) { + body.traits!.createdAt = traits.createdAt as string; + } + if (traits.description) { + body.traits!.description = traits.description as string; + } + if (traits.id) { + body.traits!.id = traits.id as string; + } + if (traits.company) { + const company = (traits.company as IDataObject).companyUi as IDataObject; + if (company) { + if (company.id) { + //@ts-ignore + body.traits.company.id = company.id as string; + } + if (company.name) { + //@ts-ignore + body.traits.company.name = company.name as string; + } + if (company.industry) { + //@ts-ignore + body.traits.company.industry = company.industry as string; + } + if (company.employeeCount) { + //@ts-ignore + body.traits.company.employeeCount = company.employeeCount as number; + } + if (company.plan) { + //@ts-ignore + body.traits.company.plan = company.plan as string; + } + } + } + if (traits.address) { + const address = (traits.address as IDataObject).addressUi as IDataObject; + if (address) { + if (address.street) { + //@ts-ignore + body.traits.address.street = address.street as string; + } + if (address.city) { + //@ts-ignore + body.traits.address.city = address.city as string; + } + if (address.state) { + //@ts-ignore + body.traits.address.state = address.state as string; + } + if (address.postalCode) { + //@ts-ignore + body.traits.address.postalCode = address.postalCode as string; + } + if (address.country) { + //@ts-ignore + body.traits.address.country = address.country as string; + } + } + } + } + if (context) { + if (context.active) { + body.context!.active = context.active as boolean; + } + if (context.ip) { + body.context!.ip = context.ip as string; + } + if (context.locate) { + body.context!.locate = context.locate as string; + } + if (context.page) { + body.context!.page = context.page as string; + } + if (context.timezone) { + body.context!.timezone = context.timezone as string; + } + if (context.timezone) { + body.context!.timezone = context.timezone as string; + } + if (context.app) { + const app = (context.app as IDataObject).appUi as IDataObject; + if (app) { + if (app.name) { + //@ts-ignore + body.context.app.name = app.name as string; + } + if (app.version) { + //@ts-ignore + body.context.app.version = app.version as string; + } + if (app.build) { + //@ts-ignore + body.context.app.build = app.build as string; + } + } + } + if (context.campaign) { + const campaign = (context.campaign as IDataObject).campaignUi as IDataObject; + if (campaign) { + if (campaign.name) { + //@ts-ignore + body.context.campaign.name = campaign.name as string; + } + if (campaign.source) { + //@ts-ignore + body.context.campaign.source = campaign.source as string; + } + if (campaign.medium) { + //@ts-ignore + body.context.campaign.medium = campaign.medium as string; + } + if (campaign.term) { + //@ts-ignore + body.context.campaign.term = campaign.term as string; + } + if (campaign.content) { + //@ts-ignore + body.context.campaign.content = campaign.content as string; + } + } + } + + if (context.device) { + const device = (context.device as IDataObject).deviceUi as IDataObject; + if (device) { + if (device.id) { + //@ts-ignore + body.context.device.id = device.id as string; + } + if (device.manufacturer) { + //@ts-ignore + body.context.device.manufacturer = device.manufacturer as string; + } + if (device.model) { + //@ts-ignore + body.context.device.model = device.model as string; + } + if (device.type) { + //@ts-ignore + body.context.device.type = device.type as string; + } + if (device.version) { + //@ts-ignore + body.context.device.version = device.version as string; + } + } + } + } + if (integrations) { + if (integrations.all) { + body.integrations!.all = integrations.all as boolean; + } + if (integrations.salesforce) { + body.integrations!.salesforce = integrations.salesforce as boolean; + } + } + responseData = await segmentApiRequest.call(this, 'POST', '/identify', body); + } + } + if (resource === 'track') { + //https://segment.com/docs/connections/sources/catalog/libraries/server/http-api/#track + if (operation === 'event') { + const userId = this.getNodeParameter('userId', i) as string; + const event = this.getNodeParameter('event', i) as string; + const traits = (this.getNodeParameter('traits', i) as IDataObject).traitsUi as IDataObject; + const context = (this.getNodeParameter('context', i) as IDataObject).contextUi as IDataObject; + const integrations = (this.getNodeParameter('integrations', i) as IDataObject).integrationsUi as IDataObject; + const properties = (this.getNodeParameter('properties', i) as IDataObject).propertiesUi as IDataObject; + const body: ITrack = { + event, + traits: { + company: {}, + address: {}, + }, + context: { + app: {}, + campaign: {}, + device: {}, + }, + integrations: {}, + properties: {}, + }; + if (userId) { + body.userId = userId as string; + } else { + body.anonymousId = uuid(); + } + if (userId) { + body.userId = userId as string; + } else { + body.anonymousId = uuid(); + } + if (traits) { + if (traits.email) { + body.traits!.email = traits.email as string; + } + if (traits.firstname) { + body.traits!.firstname = traits.firstname as string; + } + if (traits.lastname) { + body.traits!.lastname = traits.lastname as string; + } + if (traits.gender) { + body.traits!.gender = traits.gender as string; + } + if (traits.phone) { + body.traits!.phone = traits.phone as string; + } + if (traits.username) { + body.traits!.username = traits.username as string; + } + if (traits.website) { + body.traits!.website = traits.website as string; + } + if (traits.age) { + body.traits!.age = traits.age as number; + } + if (traits.avatar) { + body.traits!.avatar = traits.avatar as string; + } + if (traits.birthday) { + body.traits!.birthday = traits.birthday as string; + } + if (traits.createdAt) { + body.traits!.createdAt = traits.createdAt as string; + } + if (traits.description) { + body.traits!.description = traits.description as string; + } + if (traits.id) { + body.traits!.id = traits.id as string; + } + if (traits.company) { + const company = (traits.company as IDataObject).companyUi as IDataObject; + if (company) { + if (company.id) { + //@ts-ignore + body.traits.company.id = company.id as string; + } + if (company.name) { + //@ts-ignore + body.traits.company.name = company.name as string; + } + if (company.industry) { + //@ts-ignore + body.traits.company.industry = company.industry as string; + } + if (company.employeeCount) { + //@ts-ignore + body.traits.company.employeeCount = company.employeeCount as number; + } + if (company.plan) { + //@ts-ignore + body.traits.company.plan = company.plan as string; + } + } + } + if (traits.address) { + const address = (traits.address as IDataObject).addressUi as IDataObject; + if (address) { + if (address.street) { + //@ts-ignore + body.traits.address.street = address.street as string; + } + if (address.city) { + //@ts-ignore + body.traits.address.city = address.city as string; + } + if (address.state) { + //@ts-ignore + body.traits.address.state = address.state as string; + } + if (address.postalCode) { + //@ts-ignore + body.traits.address.postalCode = address.postalCode as string; + } + if (address.country) { + //@ts-ignore + body.traits.address.country = address.country as string; + } + } + } + } + if (context) { + if (context.active) { + body.context!.active = context.active as boolean; + } + if (context.ip) { + body.context!.ip = context.ip as string; + } + if (context.locate) { + body.context!.locate = context.locate as string; + } + if (context.page) { + body.context!.page = context.page as string; + } + if (context.timezone) { + body.context!.timezone = context.timezone as string; + } + if (context.timezone) { + body.context!.timezone = context.timezone as string; + } + if (context.app) { + const app = (context.app as IDataObject).appUi as IDataObject; + if (app) { + if (app.name) { + //@ts-ignore + body.context.app.name = app.name as string; + } + if (app.version) { + //@ts-ignore + body.context.app.version = app.version as string; + } + if (app.build) { + //@ts-ignore + body.context.app.build = app.build as string; + } + } + } + if (context.campaign) { + const campaign = (context.campaign as IDataObject).campaignUi as IDataObject; + if (campaign) { + if (campaign.name) { + //@ts-ignore + body.context.campaign.name = campaign.name as string; + } + if (campaign.source) { + //@ts-ignore + body.context.campaign.source = campaign.source as string; + } + if (campaign.medium) { + //@ts-ignore + body.context.campaign.medium = campaign.medium as string; + } + if (campaign.term) { + //@ts-ignore + body.context.campaign.term = campaign.term as string; + } + if (campaign.content) { + //@ts-ignore + body.context.campaign.content = campaign.content as string; + } + } + } + + if (context.device) { + const device = (context.device as IDataObject).deviceUi as IDataObject; + if (device) { + if (device.id) { + //@ts-ignore + body.context.device.id = device.id as string; + } + if (device.manufacturer) { + //@ts-ignore + body.context.device.manufacturer = device.manufacturer as string; + } + if (device.model) { + //@ts-ignore + body.context.device.model = device.model as string; + } + if (device.type) { + //@ts-ignore + body.context.device.type = device.type as string; + } + if (device.version) { + //@ts-ignore + body.context.device.version = device.version as string; + } + } + } + } + if (integrations) { + if (integrations.all) { + body.integrations!.all = integrations.all as boolean; + } + if (integrations.salesforce) { + body.integrations!.salesforce = integrations.salesforce as boolean; + } + } + if (properties) { + if (properties.revenue) { + body.properties!.revenue = properties.revenue as number; + } + if (properties.currency) { + body.properties!.currency = properties.currency as string; + } + if (properties.value) { + body.properties!.value = properties.value as string; + } + } + responseData = await segmentApiRequest.call(this, 'POST', '/track', body); + } + //https://segment.com/docs/connections/sources/catalog/libraries/server/http-api/#page + if (operation === 'page') { + const userId = this.getNodeParameter('userId', i) as string; + const event = this.getNodeParameter('event', i) as string; + const traits = (this.getNodeParameter('traits', i) as IDataObject).traitsUi as IDataObject; + const context = (this.getNodeParameter('context', i) as IDataObject).contextUi as IDataObject; + const integrations = (this.getNodeParameter('integrations', i) as IDataObject).integrationsUi as IDataObject; + const properties = (this.getNodeParameter('properties', i) as IDataObject).propertiesUi as IDataObject; + const body: ITrack = { + event, + traits: { + company: {}, + address: {}, + }, + context: { + app: {}, + campaign: {}, + device: {}, + }, + integrations: {}, + properties: {}, + }; + if (userId) { + body.userId = userId as string; + } else { + body.anonymousId = uuid(); + } + if (userId) { + body.userId = userId as string; + } else { + body.anonymousId = uuid(); + } + if (traits) { + if (traits.email) { + body.traits!.email = traits.email as string; + } + if (traits.firstname) { + body.traits!.firstname = traits.firstname as string; + } + if (traits.lastname) { + body.traits!.lastname = traits.lastname as string; + } + if (traits.gender) { + body.traits!.gender = traits.gender as string; + } + if (traits.phone) { + body.traits!.phone = traits.phone as string; + } + if (traits.username) { + body.traits!.username = traits.username as string; + } + if (traits.website) { + body.traits!.website = traits.website as string; + } + if (traits.age) { + body.traits!.age = traits.age as number; + } + if (traits.avatar) { + body.traits!.avatar = traits.avatar as string; + } + if (traits.birthday) { + body.traits!.birthday = traits.birthday as string; + } + if (traits.createdAt) { + body.traits!.createdAt = traits.createdAt as string; + } + if (traits.description) { + body.traits!.description = traits.description as string; + } + if (traits.id) { + body.traits!.id = traits.id as string; + } + if (traits.company) { + const company = (traits.company as IDataObject).companyUi as IDataObject; + if (company) { + if (company.id) { + //@ts-ignore + body.traits.company.id = company.id as string; + } + if (company.name) { + //@ts-ignore + body.traits.company.name = company.name as string; + } + if (company.industry) { + //@ts-ignore + body.traits.company.industry = company.industry as string; + } + if (company.employeeCount) { + //@ts-ignore + body.traits.company.employeeCount = company.employeeCount as number; + } + if (company.plan) { + //@ts-ignore + body.traits.company.plan = company.plan as string; + } + } + } + if (traits.address) { + const address = (traits.address as IDataObject).addressUi as IDataObject; + if (address) { + if (address.street) { + //@ts-ignore + body.traits.address.street = address.street as string; + } + if (address.city) { + //@ts-ignore + body.traits.address.city = address.city as string; + } + if (address.state) { + //@ts-ignore + body.traits.address.state = address.state as string; + } + if (address.postalCode) { + //@ts-ignore + body.traits.address.postalCode = address.postalCode as string; + } + if (address.country) { + //@ts-ignore + body.traits.address.country = address.country as string; + } + } + } + } + if (context) { + if (context.active) { + body.context!.active = context.active as boolean; + } + if (context.ip) { + body.context!.ip = context.ip as string; + } + if (context.locate) { + body.context!.locate = context.locate as string; + } + if (context.page) { + body.context!.page = context.page as string; + } + if (context.timezone) { + body.context!.timezone = context.timezone as string; + } + if (context.timezone) { + body.context!.timezone = context.timezone as string; + } + if (context.app) { + const app = (context.app as IDataObject).appUi as IDataObject; + if (app) { + if (app.name) { + //@ts-ignore + body.context.app.name = app.name as string; + } + if (app.version) { + //@ts-ignore + body.context.app.version = app.version as string; + } + if (app.build) { + //@ts-ignore + body.context.app.build = app.build as string; + } + } + } + if (context.campaign) { + const campaign = (context.campaign as IDataObject).campaignUi as IDataObject; + if (campaign) { + if (campaign.name) { + //@ts-ignore + body.context.campaign.name = campaign.name as string; + } + if (campaign.source) { + //@ts-ignore + body.context.campaign.source = campaign.source as string; + } + if (campaign.medium) { + //@ts-ignore + body.context.campaign.medium = campaign.medium as string; + } + if (campaign.term) { + //@ts-ignore + body.context.campaign.term = campaign.term as string; + } + if (campaign.content) { + //@ts-ignore + body.context.campaign.content = campaign.content as string; + } + } + } + + if (context.device) { + const device = (context.device as IDataObject).deviceUi as IDataObject; + if (device) { + if (device.id) { + //@ts-ignore + body.context.device.id = device.id as string; + } + if (device.manufacturer) { + //@ts-ignore + body.context.device.manufacturer = device.manufacturer as string; + } + if (device.model) { + //@ts-ignore + body.context.device.model = device.model as string; + } + if (device.type) { + //@ts-ignore + body.context.device.type = device.type as string; + } + if (device.version) { + //@ts-ignore + body.context.device.version = device.version as string; + } + } + } + } + if (integrations) { + if (integrations.all) { + body.integrations!.all = integrations.all as boolean; + } + if (integrations.salesforce) { + body.integrations!.salesforce = integrations.salesforce as boolean; + } + } + if (properties) { + if (properties.name) { + body.properties!.name = properties.name as number; + } + if (properties.path) { + body.properties!.path = properties.path as string; + } + if (properties.referrer) { + body.properties!.referrer = properties.referrer as string; + } + if (properties.search) { + body.properties!.search = properties.search as string; + } + if (properties.title) { + body.properties!.title = properties.title as string; + } + if (properties.url) { + body.properties!.url = properties.url as string; + } + if (properties.keywords) { + body.properties!.keywords = properties.keywords as string; + } + } + responseData = await segmentApiRequest.call(this, 'POST', '/page', body); + } + } + 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/Segment/TrackDescription.ts b/packages/nodes-base/nodes/Segment/TrackDescription.ts new file mode 100644 index 0000000000..cd03f9064b --- /dev/null +++ b/packages/nodes-base/nodes/Segment/TrackDescription.ts @@ -0,0 +1,1155 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const trackOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'track', + ], + }, + }, + options: [ + { + name: 'Event', + value: 'event', + description: 'lets you record the actions your users perform.Every action triggers what we call an “event”, which can also have associated properties.', + }, + { + name: 'Page', + value: 'page', + description: ' lets you record page views on your website, along with optional extra information about the page being viewed.', + }, + ], + default: 'event', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const trackFields = [ + +/* -------------------------------------------------------------------------- */ +/* track:event */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'User ID', + name: 'userId', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'event', + ], + }, + }, + required: false, + }, + { + displayName: 'Event', + name: 'event', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'event', + ], + }, + }, + description: 'Name of the action that a user has performed.', + required: true, + }, + { + displayName: 'Traits', + name: 'traits', + placeholder: 'Add Trait', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'event', + ], + }, + }, + default: {}, + options: [ + { + name: 'traitsUi', + displayName: 'Trait', + values: [ + { + displayName: 'Email', + name: 'emaiL', + type: 'string', + default: '', + description: 'Email address of a user', + }, + { + displayName: 'First Name', + name: 'firstname', + type: 'string', + default: '', + description: 'First name of a user', + }, + { + displayName: 'Last Name', + name: 'lastname', + type: 'string', + default: '', + description: 'Last name of a user', + }, + { + displayName: 'Gender', + name: 'gender', + type: 'string', + default: '', + description: 'Gender of a user', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + description: 'Phone number of a user', + }, + { + displayName: 'Username', + name: 'username', + type: 'string', + default: '', + description: 'User’s username', + }, + { + displayName: 'Website', + name: 'website', + type: 'string', + default: '', + description: 'Website of a user', + }, + { + displayName: 'Age', + name: 'age', + type: 'number', + default: 1, + description: 'Age of a user', + }, + { + displayName: 'Avatar', + name: 'avatar', + type: 'string', + default: '', + description: 'URL to an avatar image for the user', + }, + { + displayName: 'Birthday', + name: 'birthday', + type: 'dateTime', + default: '', + description: 'User’s birthday', + }, + { + displayName: 'eventd At', + name: 'eventdAt', + type: 'dateTime', + default: '', + description: 'Date the user’s account was first eventd', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Description of the user', + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + description: 'Unique ID in your database for a user', + }, + { + displayName: 'Company', + name: 'company', + placeholder: 'Add Company', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'companyUi', + displayName: 'Company', + values: [ + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Industry', + name: 'industry', + type: 'string', + default: '', + }, + { + displayName: 'Employee Count', + name: 'employeeCount', + type: 'number', + default: 1, + }, + { + displayName: 'Plan', + name: 'plan', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Address', + name: 'address', + placeholder: 'Add Address', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'addressUi', + displayName: 'Address', + values: [ + { + displayName: 'Street', + name: 'street', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + }, + { + displayName: 'Postal Code', + name: 'postalCode', + type: 'string', + default: '', + }, + { + displayName: 'Country', + name: 'country', + type: 'string', + default: '', + }, + ] + }, + ], + }, + ] + }, + ], + }, + { + displayName: 'Context', + name: 'context', + placeholder: 'Add Context', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'event', + ], + }, + }, + default: {}, + options: [ + { + name: 'contextUi', + displayName: 'Context', + values: [ + { + displayName: 'Active', + name: 'active', + type: 'boolean', + default: '', + description: 'Whether a user is active', + }, + { + displayName: 'IP', + name: 'ip', + type: 'string', + default: '', + description: 'Current user’s IP address.', + }, + { + displayName: 'Locale', + name: 'locate', + type: 'string', + default: '', + description: 'Locale string for the current user, for example en-US.', + }, + { + displayName: 'Page', + name: 'page', + type: 'string', + default: '', + description: 'Dictionary of information about the current page in the browser, containing hash, path, referrer, search, title and url', + }, + { + displayName: 'Timezone', + name: 'timezone', + type: 'string', + default: '', + description: 'Timezones are sent as tzdata strings to add user timezone information which might be stripped from the timestamp, for example America/New_York', + }, + { + displayName: 'App', + name: 'app', + placeholder: 'Add App', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'appUi', + displayName: 'App', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Version', + name: 'version', + type: 'string', + default: '', + }, + { + displayName: 'Build', + name: 'build', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Campaign', + name: 'campaign', + placeholder: 'Campaign App', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'campaignUi', + displayName: 'Campaign', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Source', + name: 'source', + type: 'string', + default: '', + }, + { + displayName: 'Medium', + name: 'medium', + type: 'string', + default: '', + }, + { + displayName: 'Term', + name: 'term', + type: 'string', + default: '', + }, + { + displayName: 'Content', + name: 'content', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Device', + name: 'device', + placeholder: 'Add Device', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'deviceUi', + displayName: 'Device', + values: [ + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + }, + { + displayName: 'Manufacturer', + name: 'manufacturer', + type: 'string', + default: '', + }, + { + displayName: 'Model', + name: 'model', + type: 'string', + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Type', + name: 'type', + type: 'string', + default: '', + }, + { + displayName: 'Version', + name: 'version', + type: 'string', + default: '', + }, + ], + }, + ], + }, + ] + }, + ], + }, + { + displayName: 'Integration', + name: 'integrations', + placeholder: 'Add Integration', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'event', + ], + }, + }, + default: {}, + options: [ + { + name: 'integrationsUi', + displayName: 'Integration', + values: [ + { + displayName: 'All', + name: 'all', + type: 'boolean', + default: true, + }, + { + displayName: 'Salesforce', + name: 'salesforce', + type: 'boolean', + default: false, + }, + ], + }, + ], + }, + { + displayName: 'Properties', + name: 'properties', + placeholder: 'Add Properties', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'event', + ], + }, + }, + default: {}, + options: [ + { + name: 'propertiesUi', + displayName: 'Properties', + values: [ + { + displayName: 'Revenue', + name: 'revenue', + type: 'number', + typeOptions: { + numberPrecision: 2, + }, + default: 1, + description: 'Amount of revenue an event resulted in. This should be a decimal value, so a shirt worth $19.99 would result in a revenue of 19.99.' + }, + { + displayName: 'Currency', + name: 'currency', + type: 'string', + default: '', + description: 'Currency of the revenue an event resulted in

This should be sent in the ISO 4127 format. If this is not set, we assume the revenue to be in US dollars.

' + }, + { + displayName: 'Value', + name: 'value', + type: 'number', + default: '', + description: 'An abstract “value” to associate with an event. This is typically used in situations where the event doesn’t generate real-dollar revenue, but has an intrinsic value to a marketing team, like newsletter signups.' + }, + ], + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* track:page */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'User ID', + name: 'userId', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'page', + ], + }, + }, + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'page', + ], + }, + }, + description: 'Name of the page For example, most sites have a “Signup” page that can be useful to tag, so you can see users as they move through your funnel', + }, + { + displayName: 'Traits', + name: 'traits', + placeholder: 'Add Trait', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'page', + ], + }, + }, + default: {}, + options: [ + { + name: 'traitsUi', + displayName: 'Trait', + values: [ + { + displayName: 'Email', + name: 'emaiL', + type: 'string', + default: '', + description: 'Email address of a user', + }, + { + displayName: 'First Name', + name: 'firstname', + type: 'string', + default: '', + description: 'First name of a user', + }, + { + displayName: 'Last Name', + name: 'lastname', + type: 'string', + default: '', + description: 'Last name of a user', + }, + { + displayName: 'Gender', + name: 'gender', + type: 'string', + default: '', + description: 'Gender of a user', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + description: 'Phone number of a user', + }, + { + displayName: 'Username', + name: 'username', + type: 'string', + default: '', + description: 'User’s username', + }, + { + displayName: 'Website', + name: 'website', + type: 'string', + default: '', + description: 'Website of a user', + }, + { + displayName: 'Age', + name: 'age', + type: 'number', + default: 1, + description: 'Age of a user', + }, + { + displayName: 'Avatar', + name: 'avatar', + type: 'string', + default: '', + description: 'URL to an avatar image for the user', + }, + { + displayName: 'Birthday', + name: 'birthday', + type: 'dateTime', + default: '', + description: 'User’s birthday', + }, + { + displayName: 'eventd At', + name: 'eventdAt', + type: 'dateTime', + default: '', + description: 'Date the user’s account was first eventd', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Description of the user', + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + description: 'Unique ID in your database for a user', + }, + { + displayName: 'Company', + name: 'company', + placeholder: 'Add Company', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'companyUi', + displayName: 'Company', + values: [ + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Industry', + name: 'industry', + type: 'string', + default: '', + }, + { + displayName: 'Employee Count', + name: 'employeeCount', + type: 'number', + default: 1, + }, + { + displayName: 'Plan', + name: 'plan', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Address', + name: 'address', + placeholder: 'Add Address', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'addressUi', + displayName: 'Address', + values: [ + { + displayName: 'Street', + name: 'street', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + }, + { + displayName: 'Postal Code', + name: 'postalCode', + type: 'string', + default: '', + }, + { + displayName: 'Country', + name: 'country', + type: 'string', + default: '', + }, + ] + }, + ], + }, + ] + }, + ], + }, + { + displayName: 'Context', + name: 'context', + placeholder: 'Add Context', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'page', + ], + }, + }, + default: {}, + options: [ + { + name: 'contextUi', + displayName: 'Context', + values: [ + { + displayName: 'Active', + name: 'active', + type: 'boolean', + default: '', + description: 'Whether a user is active', + }, + { + displayName: 'IP', + name: 'ip', + type: 'string', + default: '', + description: 'Current user’s IP address.', + }, + { + displayName: 'Locale', + name: 'locate', + type: 'string', + default: '', + description: 'Locale string for the current user, for example en-US.', + }, + { + displayName: 'Page', + name: 'page', + type: 'string', + default: '', + description: 'Dictionary of information about the current page in the browser, containing hash, path, referrer, search, title and url', + }, + { + displayName: 'Timezone', + name: 'timezone', + type: 'string', + default: '', + description: 'Timezones are sent as tzdata strings to add user timezone information which might be stripped from the timestamp, for example America/New_York', + }, + { + displayName: 'App', + name: 'app', + placeholder: 'Add App', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'appUi', + displayName: 'App', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Version', + name: 'version', + type: 'string', + default: '', + }, + { + displayName: 'Build', + name: 'build', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Campaign', + name: 'campaign', + placeholder: 'Campaign App', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'campaignUi', + displayName: 'Campaign', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Source', + name: 'source', + type: 'string', + default: '', + }, + { + displayName: 'Medium', + name: 'medium', + type: 'string', + default: '', + }, + { + displayName: 'Term', + name: 'term', + type: 'string', + default: '', + }, + { + displayName: 'Content', + name: 'content', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Device', + name: 'device', + placeholder: 'Add Device', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'deviceUi', + displayName: 'Device', + values: [ + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + }, + { + displayName: 'Manufacturer', + name: 'manufacturer', + type: 'string', + default: '', + }, + { + displayName: 'Model', + name: 'model', + type: 'string', + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Type', + name: 'type', + type: 'string', + default: '', + }, + { + displayName: 'Version', + name: 'version', + type: 'string', + default: '', + }, + ], + }, + ], + }, + ] + }, + ], + }, + { + displayName: 'Integration', + name: 'integrations', + placeholder: 'Add Integration', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'page', + ], + }, + }, + default: {}, + options: [ + { + name: 'integrationsUi', + displayName: 'Integration', + values: [ + { + displayName: 'All', + name: 'all', + type: 'boolean', + default: true, + }, + { + displayName: 'Salesforce', + name: 'salesforce', + type: 'boolean', + default: false, + }, + ], + }, + ], + }, + { + displayName: 'Properties', + name: 'properties', + placeholder: 'Add Properties', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'page', + ], + }, + }, + default: {}, + options: [ + { + name: 'propertiesUi', + displayName: 'Properties', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'Name of the page. This is reserved for future use.' + }, + { + displayName: 'Path', + name: 'path', + type: 'string', + default: '', + description: 'Path portion of the URL of the page. Equivalent to canonical path which defaults to location.pathname from the DOM API.' + }, + { + displayName: 'Referrer', + name: 'referrer', + type: 'string', + default: '', + description: 'Full URL of the previous page. Equivalent to document.referrer from the DOM API.' + }, + { + displayName: 'Search', + name: 'search', + type: 'string', + default: '', + description: 'Query string portion of the URL of the page. Equivalent to location.search from the DOM API.' + }, + { + displayName: 'Title', + name: 'title', + type: 'string', + default: '', + description: 'Title of the page. Equivalent to document.title from the DOM API.' + }, + { + displayName: 'URL', + name: 'url', + type: 'string', + default: '', + description: 'Full URL of the page. First we look for the canonical url. If the canonical url is not provided, we use location.href from the DOM API.' + }, + { + displayName: 'Keywords', + name: 'keywords', + type: 'string', + default: '', + description: 'A list/array of keywords describing the content of the page. The keywords would most likely be the same as, or similar to, the keywords you would find in an html meta tag for SEO purposes.' + }, + ], + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Segment/TrackInterface.ts b/packages/nodes-base/nodes/Segment/TrackInterface.ts new file mode 100644 index 0000000000..85ce0924e5 --- /dev/null +++ b/packages/nodes-base/nodes/Segment/TrackInterface.ts @@ -0,0 +1,13 @@ +import { IDataObject } from "n8n-workflow"; + +export interface ITrack { + event?: string; + userId?: string; + name?: string; + anonymousId?: string; + traits?: IDataObject; + context?: IDataObject; + timestamp?: string; + properties?: IDataObject; + integrations?: IDataObject; +} diff --git a/packages/nodes-base/nodes/Segment/segment.png b/packages/nodes-base/nodes/Segment/segment.png new file mode 100644 index 0000000000000000000000000000000000000000..5dbfcaf09dc9a98928bee9f20849f28b7d9113df GIT binary patch literal 1144 zcmV-;1c&>HP)cb{$Bwr$(CZQJj)XaAX5Z%#UG+9s3fnfVRcCOKblbaMl| zQEF}-8w-W;J*Hw0uHywhLBNi;cz}~wfj+2#R0s<&bD@GUQ31no5Kr(K!bNX!1&h%J zG0YY;724te-a>eg9p9QIXi{{;B?vG1goZ{3jffUF3E^eukkH7m=`a`XA$(vM0wr~^ zAr~qk8$vx9G!T_=%8gs{4)$XKMxX~eqALbsIyU1HUPJuh4T?$X69<#<1RwDbBM{va zVMFmH9zr;Sk2r-!CymrbB(h4__&5w9aS9m%tWy4X4(yV!Pf^KAVH0R{<_-)%u$2P0 z(U`0sM&~EqOHzHWfX*o*p*71k$ia%nJg9fk1Kpm^5tsS0w|WbrJ+L`GGK*aXJG>!Y&9E zSIroBtOx$2dsj9gvSQ%IlI1D~x>)L#X4??VhEVYwC6piCgD{ED=%OQVdfbLkaS*YT zALU0=G&6y^I@i!T!yyUWAAwdHY$U^ZN!S-CY^AV4=%aCU*CB$H0vE!4N!Sl)V5P8e zumwV5JEB`9Y&eX7UE{sYT2=`gg0^@EAu$i(-RawYLLbyaG!ujk#SeG|A#oL1T-|Xg zT!8q)M=U^WgWvV+g4YldZ_&cl9Y>eUIvn`Lz&CiO;3I^D9m8RBb?4df3_`^Ug!nLM z7^>nBgcC~;)18B4!XpS33lZ$2hAA(m;U$C<8<0fLv$9OmkvG+7OtK;wf^-Cpj^MXK0000< KMNUMnLSTYtR{A{v literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 9cd7f397c5..392bf61714 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -74,6 +74,7 @@ "dist/credentials/Smtp.credentials.js", "dist/credentials/StripeApi.credentials.js", "dist/credentials/SalesmateApi.credentials.js", + "dist/credentials/SegmentApi.credentials.js", "dist/credentials/TelegramApi.credentials.js", "dist/credentials/TodoistApi.credentials.js", "dist/credentials/TrelloApi.credentials.js", @@ -172,6 +173,7 @@ "dist/nodes/Stripe/StripeTrigger.node.js", "dist/nodes/Switch.node.js", "dist/nodes/Salesmate/Salesmate.node.js", + "dist/nodes/Segment/Segment.node.js", "dist/nodes/Telegram/Telegram.node.js", "dist/nodes/Telegram/TelegramTrigger.node.js", "dist/nodes/Todoist/Todoist.node.js", @@ -206,6 +208,7 @@ "@types/nodemailer": "^4.6.5", "@types/redis": "^2.8.11", "@types/request-promise-native": "~1.0.15", + "@types/uuid": "^3.4.6", "@types/xml2js": "^0.4.3", "gulp": "^4.0.0", "jest": "^24.9.0", @@ -237,6 +240,7 @@ "redis": "^2.8.0", "rhea": "^1.0.11", "rss-parser": "^3.7.0", + "uuid": "^3.4.0", "vm2": "^3.6.10", "xlsx": "^0.14.3", "xml2js": "^0.4.22" From 5813ec7c3f658c1403eb30420ca8843762be90b7 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 23 Jan 2020 14:50:22 -0800 Subject: [PATCH 16/41] :zap: Some improvements on Disqus-Node --- .../nodes-base/nodes/Disqus/Disqus.node.ts | 321 ++++++++++++------ .../nodes/Disqus/GenericFunctions.ts | 26 +- 2 files changed, 235 insertions(+), 112 deletions(-) diff --git a/packages/nodes-base/nodes/Disqus/Disqus.node.ts b/packages/nodes-base/nodes/Disqus/Disqus.node.ts index 213da8ef4b..3514fac0c7 100644 --- a/packages/nodes-base/nodes/Disqus/Disqus.node.ts +++ b/packages/nodes-base/nodes/Disqus/Disqus.node.ts @@ -1,5 +1,4 @@ import { - BINARY_ENCODING, IExecuteFunctions, } from 'n8n-core'; import { @@ -42,7 +41,7 @@ export class Disqus implements INodeType { { name: 'Forum', value: 'forum', - } + }, ], default: 'forum', description: 'The resource to operate on.', @@ -127,18 +126,62 @@ export class Disqus implements INodeType { default: {}, options: [ { - displayName: 'attach', + displayName: 'Attach', name: 'attach', - type: 'string', - default: '[]', - description: 'Choices: followsForum, forumCanDisableAds, forumForumCategory, counters, forumDaysAlive, forumFeatures, forumIntegration, forumNewPolicy, forumPermissions', + type: 'multiOptions', + options: [ + { + name: 'counters', + value: 'counters', + }, + { + name: 'followsForum', + value: 'followsForum', + }, + { + name: 'forumCanDisableAds', + value: 'forumCanDisableAds', + }, + { + name: 'forumDaysAlive', + value: 'forumDaysAlive', + }, + { + name: 'forumFeatures', + value: 'forumFeatures', + }, + { + name: 'forumForumCategory', + value: 'forumForumCategory', + }, + { + name: 'forumIntegration', + value: 'forumIntegration', + }, + { + name: 'forumNewPolicy', + value: 'forumNewPolicy', + }, + { + name: 'forumPermissions', + value: 'forumPermissions', + }, + ], + default: [], + description: 'The resource to operate on.', }, { - displayName: 'related', + displayName: 'Related', name: 'related', - type: 'string', - default: false, - description: 'You may specify relations to include with your response. Choices `author`', + type: 'multiOptions', + options: [ + { + name: 'author', + value: 'author', + }, + ], + default: [], + description: 'You may specify relations to include with your response', }, ], }, @@ -222,61 +265,115 @@ export class Disqus implements INodeType { }, default: {}, options: [ - { - displayName: 'Since', - name: 'since', - type: 'string', - default: `[]`, - description: 'Unix timestamp (or ISO datetime standard)', - }, - { - displayName: 'Related', - name: 'related', - type: 'string', - default: '[]', - description: 'You may specify relations to include with your response. Choices `author`', - }, - { - displayName: 'Cursor', - name: 'cursor', - type: 'string', - default: '', - description: 'You may specify cursor for your response.', - }, - { - displayName: 'Limit', - name: 'limit', - type: 'string', - default: 25, - description: 'You may specify relations maximum number of posts to return. Maximum value is 100', - }, { displayName: 'Filters', name: 'filters', - type: 'string', - default: '[]', - description: 'You may specify filters for your response. Choices: `Is_Anonymous`, `Has_Link`, `Has_Low_Rep_Author`, `Has_Bad_Word`, `Is_Flagged`, `No_Issue`, `Is_Toxic`, `Modified_By_Rule`, `Shadow_Banned`, `Has_Media`, `Is_At_Flag_Limit`', + type: 'multiOptions', + options: [ + { + name: 'Has_Bad_Word', + value: 'Has_Bad_Word', + }, + { + name: 'Has_Link', + value: 'Has_Link', + }, + { + name: 'Has_Low_Rep_Author', + value: 'Has_Low_Rep_Author', + }, + { + name: 'Has_Media', + value: 'Has_Media', + }, + { + name: 'Is_Anonymous', + value: 'Is_Anonymous', + }, + { + name: 'Is_Flagged', + value: 'Is_Flagged', + }, + { + name: 'No_Issue', + value: 'No_Issue', + }, + { + name: 'Is_At_Flag_Limit', + value: 'Is_At_Flag_Limit', + }, + { + name: 'Is_Toxic', + value: 'Is_Toxic', + }, + { + name: 'Modified_By_Rule', + value: 'Modified_By_Rule', + }, + { + name: 'Shadow_Banned', + value: 'Shadow_Banned', + }, + ], + default: [], + description: 'You may specify filters for your response. Choices: , ``, ``, ``', + }, + { + displayName: 'Include', + name: 'include', + type: 'multiOptions', + options: [ + { + name: 'author', + value: 'author', + }, + ], + default: [], + description: 'You may specify relations to include with your response.', + }, + { + displayName: 'Order', + name: 'order', + type: 'options', + options: [ + { + name: 'ASC', + value: 'asc', + }, + { + name: 'DESC', + value: 'desc', + } + ], + default: 'asc', + description: 'You may specify order to sort your response.', }, { displayName: 'Query', name: 'query', type: 'string', default: '', - description: 'You may specify query for your response.', + description: 'You may specify query forChoices: asc, desc your response.', }, { - displayName: 'Include', - name: 'include', - type: 'string', - default: false, - description: 'You may specify relations to include with your response. Choices `author`', + displayName: 'Related', + name: 'related', + type: 'multiOptions', + options: [ + { + name: 'thread', + value: 'thread', + }, + ], + default: [], + description: 'You may specify relations to include with your response', }, { - displayName: 'Order', - name: 'order', - type: 'string', - default: 'asc', - description: 'You may specify order to sort your response.Choices: asc, desc', + displayName: 'Since', + name: 'since', + type: 'dateTime', + default: '', + description: 'Unix timestamp (or ISO datetime standard)', }, ], }, @@ -360,26 +457,22 @@ export class Disqus implements INodeType { }, default: {}, options: [ - { - displayName: 'Since ID', - name: 'sinceId', - type: 'string', - default: `[]`, - description: 'You may specify cursor since_id for your response.', - }, - { - displayName: 'Cursor', - name: 'cursor', - type: 'string', - default: '', - description: 'You may specify cursor for your response.', - }, { displayName: 'Order', name: 'order', - type: 'string', + type: 'options', + options: [ + { + name: 'ASC', + value: 'asc', + }, + { + name: 'DESC', + value: 'desc', + } + ], default: 'asc', - description: 'You may specify order to sort your response.Choices: asc, desc', + description: 'You may specify order to sort your response.', }, ], }, @@ -463,54 +556,74 @@ export class Disqus implements INodeType { }, default: {}, options: [ - { - displayName: 'Thread', - name: 'threadId', - type: 'string', - default: '', - description: 'Looks up a thread by ID. You may pass us the "ident" query type instead of an ID by including "forum". You may pass us the "link" query type to filter by URL. You must pass the "forum" if you do not have the Pro API Access addon.', - }, - { - displayName: 'Since', - name: 'since', - type: 'string', - default: '', - description: 'Unix timestamp (or ISO datetime standard)', - }, { displayName: 'Related', name: 'related', - type: 'string', - default: '[]', - description: 'You may specify relations to include with your response. Choices `author`', - }, - { - displayName: 'Cursor', - name: 'cursor', - type: 'string', - default: '', - description: 'You may specify cursor for your response.', - }, - { - displayName: 'Limit', - name: 'limit', - type: 'string', - default: 25, - description: 'You may specify relations maximum number of posts to return. Maximum value is 100', + type: 'multiOptions', + options: [ + { + name: 'author', + value: 'author', + }, + { + name: 'forum', + value: 'forum', + }, + ], + default: [], + description: 'You may specify relations to include with your response', }, { displayName: 'Include', name: 'include', - type: 'string', - default: '', - description: 'You may specify relations to include with your response. Choices: open, closed, killed', + type: 'multiOptions', + options: [ + { + name: 'closed', + value: 'closed', + }, + { + name: 'open', + value: 'open', + }, + { + name: 'killed', + value: 'killed', + }, + ], + default: [], + description: 'You may specify relations to include with your response.', }, { displayName: 'Order', name: 'order', + type: 'options', + options: [ + { + name: 'ASC', + value: 'asc', + }, + { + name: 'DESC', + value: 'desc', + } + ], + default: 'asc', + description: 'You may specify order to sort your response.', + }, + { + displayName: 'Since', + name: 'since', + type: 'dateTime', + default: '', + description: 'Unix timestamp (or ISO datetime standard)', + }, + { + displayName: 'Thread', + name: 'threadId', type: 'string', - default: 'desc', - description: 'You may specify order to sort your response.Choices: asc, desc', + default: '', + description: 'Looks up a thread by ID. You may pass us the "ident"
query type instead of an ID by including "forum". You may
pass us the "link" query type to filter by URL. You must pass
the "forum" if you do not have the Pro API Access addon.', }, ], } diff --git a/packages/nodes-base/nodes/Disqus/GenericFunctions.ts b/packages/nodes-base/nodes/Disqus/GenericFunctions.ts index e5e5c1b7df..5f5f88bfd4 100644 --- a/packages/nodes-base/nodes/Disqus/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Disqus/GenericFunctions.ts @@ -12,8 +12,8 @@ export async function disqusApiRequest( method: string, qs: IDataObject = {}, uri?: string, - body: any = {}, - option: IDataObject = {} + body: IDataObject = {}, + option: IDataObject = {}, ): Promise { // tslint:disable-line:no-any const credentials = this.getCredentials('disqusApi') as IDataObject; @@ -22,11 +22,22 @@ export async function disqusApiRequest( throw new Error('No credentials got returned!'); } + // Convert to query string into a format the API can read + const queryStringElements: string[] = []; + for (const key of Object.keys(qs)) { + if (Array.isArray(qs[key])) { + (qs[key] as string[]).forEach(value => { + queryStringElements.push(`${key}=${value}`); + }); + } else { + queryStringElements.push(`${key}=${qs[key]}`); + } + } + let options: OptionsWithUri = { method, - qs, body, - uri: `https://disqus.com/api/3.0/${uri}`, + uri: `https://disqus.com/api/3.0/${uri}?${queryStringElements.join('&')}`, json: true }; @@ -35,10 +46,9 @@ export async function disqusApiRequest( delete options.body; } try { - const result = await this.helpers.request!(options) + const result = await this.helpers.request!(options); return result; } catch (error) { - console.log(error) if (error.statusCode === 401) { // Return a clear error throw new Error('The Disqus credentials are not valid!'); @@ -63,8 +73,8 @@ export async function disqusApiRequestAllItems( method: string, qs: IDataObject = {}, uri?: string, - body: any = {}, - option: IDataObject = {} + body: IDataObject = {}, + option: IDataObject = {}, ): Promise { // tslint:disable-line:no-any const returnData: IDataObject[] = []; From 62134f639b0b9d0b3614439d7dd1ba7bca097938 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 23 Jan 2020 15:57:34 -0800 Subject: [PATCH 17/41] :bug: Fix issue with deleting fixedCollection with multipleValues=false --- packages/editor-ui/src/components/NodeSettings.vue | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/editor-ui/src/components/NodeSettings.vue b/packages/editor-ui/src/components/NodeSettings.vue index 4189d78c35..ca3cfde88d 100644 --- a/packages/editor-ui/src/components/NodeSettings.vue +++ b/packages/editor-ui/src/components/NodeSettings.vue @@ -57,7 +57,7 @@ import ParameterInputFull from '@/components/ParameterInputFull.vue'; import ParameterInputList from '@/components/ParameterInputList.vue'; import NodeCredentials from '@/components/NodeCredentials.vue'; import NodeWebhooks from '@/components/NodeWebhooks.vue'; -import { get, set } from 'lodash'; +import { get, set, unset } from 'lodash'; import { genericHelpers } from '@/components/mixins/genericHelpers'; import { nodeHelpers } from '@/components/mixins/nodeHelpers'; @@ -369,8 +369,11 @@ export default mixins( Vue.set(nodeParameters as object, path, data); } } else { - // For everything else - set(nodeParameters as object, parameterPath, newValue); + if (newValue === undefined) { + unset(nodeParameters as object, parameterPath); + } else { + set(nodeParameters as object, parameterPath, newValue); + } } // Get the parameters with the now new defaults according to the From de098f7da4bc5e8fd73b8e0210b9c8f15a39a97e Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 23 Jan 2020 16:05:27 -0800 Subject: [PATCH 18/41] :zap: Fix issue that parameter inputs on lower levels are very small --- packages/editor-ui/src/components/ParameterInputFull.vue | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/editor-ui/src/components/ParameterInputFull.vue b/packages/editor-ui/src/components/ParameterInputFull.vue index c7ce18c98a..4d488faf92 100644 --- a/packages/editor-ui/src/components/ParameterInputFull.vue +++ b/packages/editor-ui/src/components/ParameterInputFull.vue @@ -30,6 +30,9 @@ export default Vue }, computed: { isMultiLineParameter () { + if (this.level > 4) { + return true; + } const rows = this.getArgument('rows'); if (rows !== undefined && rows > 1) { return true; @@ -37,6 +40,9 @@ export default Vue return false; }, + level (): number { + return this.path.split('.').length; + }, }, props: [ 'displayOptions', From 828962deebd96cf735e0efd1f5f153c285725445 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 23 Jan 2020 16:40:39 -0800 Subject: [PATCH 19/41] :zap: Some minor fixes on Segment-Node --- .../nodes/Segment/IdentifyDescription.ts | 2 +- .../nodes-base/nodes/Segment/Segment.node.ts | 10 ---------- .../nodes-base/nodes/Segment/TrackDescription.ts | 16 ++++++++-------- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/packages/nodes-base/nodes/Segment/IdentifyDescription.ts b/packages/nodes-base/nodes/Segment/IdentifyDescription.ts index 750a4d03d4..f80fb2bc39 100644 --- a/packages/nodes-base/nodes/Segment/IdentifyDescription.ts +++ b/packages/nodes-base/nodes/Segment/IdentifyDescription.ts @@ -72,7 +72,7 @@ export const identifyFields = [ values: [ { displayName: 'Email', - name: 'emaiL', + name: 'email', type: 'string', default: '', description: 'Email address of a user', diff --git a/packages/nodes-base/nodes/Segment/Segment.node.ts b/packages/nodes-base/nodes/Segment/Segment.node.ts index f34f1c266b..bd281366b0 100644 --- a/packages/nodes-base/nodes/Segment/Segment.node.ts +++ b/packages/nodes-base/nodes/Segment/Segment.node.ts @@ -323,11 +323,6 @@ export class Segment implements INodeType { } else { body.anonymousId = uuid(); } - if (userId) { - body.userId = userId as string; - } else { - body.anonymousId = uuid(); - } if (traits) { if (traits.email) { body.traits!.email = traits.email as string; @@ -555,11 +550,6 @@ export class Segment implements INodeType { } else { body.anonymousId = uuid(); } - if (userId) { - body.userId = userId as string; - } else { - body.anonymousId = uuid(); - } if (traits) { if (traits.email) { body.traits!.email = traits.email as string; diff --git a/packages/nodes-base/nodes/Segment/TrackDescription.ts b/packages/nodes-base/nodes/Segment/TrackDescription.ts index cd03f9064b..8077f412df 100644 --- a/packages/nodes-base/nodes/Segment/TrackDescription.ts +++ b/packages/nodes-base/nodes/Segment/TrackDescription.ts @@ -95,7 +95,7 @@ export const trackFields = [ values: [ { displayName: 'Email', - name: 'emaiL', + name: 'email', type: 'string', default: '', description: 'Email address of a user', @@ -164,11 +164,11 @@ export const trackFields = [ description: 'User’s birthday', }, { - displayName: 'eventd At', - name: 'eventdAt', + displayName: 'Created At', + name: 'createdAt', type: 'dateTime', default: '', - description: 'Date the user’s account was first eventd', + description: 'Date the user’s account was first created at', }, { displayName: 'Description', @@ -642,7 +642,7 @@ export const trackFields = [ values: [ { displayName: 'Email', - name: 'emaiL', + name: 'email', type: 'string', default: '', description: 'Email address of a user', @@ -711,11 +711,11 @@ export const trackFields = [ description: 'User’s birthday', }, { - displayName: 'eventd At', - name: 'eventdAt', + displayName: 'Created At', + name: 'createdAt', type: 'dateTime', default: '', - description: 'Date the user’s account was first eventd', + description: 'Date the user’s account was first created at', }, { displayName: 'Description', From 6f4ee39a05d74086413c6bb88067ee01fc41bc60 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 23 Jan 2020 20:20:38 -0800 Subject: [PATCH 20/41] :zap: Minor improvements on Trello-Node --- .../nodes-base/nodes/Trello/Trello.node.ts | 183 ++++++++++++++---- 1 file changed, 144 insertions(+), 39 deletions(-) diff --git a/packages/nodes-base/nodes/Trello/Trello.node.ts b/packages/nodes-base/nodes/Trello/Trello.node.ts index b95025ae8c..ed2e9c2cee 100644 --- a/packages/nodes-base/nodes/Trello/Trello.node.ts +++ b/packages/nodes-base/nodes/Trello/Trello.node.ts @@ -40,8 +40,8 @@ export class Trello implements INodeType { type: 'options', options: [ { - name: "Attachment", - value: "attachment" + name: 'Attachment', + value: 'attachment' }, { name: 'Board', @@ -52,13 +52,13 @@ export class Trello implements INodeType { value: 'card', }, { - name: "Checklist", - value: "checklist", + name: 'Checklist', + value: 'checklist', }, { - name: "Label", - value: "label" - } + name: 'Label', + value: 'label' + }, { name: 'List', value: 'list', @@ -1508,11 +1508,6 @@ export class Trello implements INodeType { }, }, options: [ - { - name: "Completed CheckItems", - value: "completedCheckItems", - description: "Get the completed checklist items on a card" - }, { name: 'Create', value: 'create', @@ -1524,7 +1519,7 @@ export class Trello implements INodeType { description: 'Delete a checklist', }, { - name: 'Delete CheckItem', + name: 'Delete Checklist Item', value: 'deleteCheckItem', description: 'Delete a checklist item', }, @@ -1533,21 +1528,26 @@ export class Trello implements INodeType { value: 'get', description: 'Get the data of a checklist', }, - { - name: 'Get CheckItem', - value: 'getCheckItem', - description: 'Get a specific checkItem on a card', - }, { name: 'Get All', value: 'getAll', description: 'Returns all checklists for the card', }, { - name: 'Update CheckItem', + name: 'Get Checklist Items', + value: 'getCheckItem', + description: 'Get a specific Checklist on a card', + }, + { + name: 'Get Completed Checklist Items', + value: 'completedCheckItems', + description: 'Get the completed Checklist items on a card', + }, + { + name: 'Update Checklist Item', value: 'updateCheckItem', description: 'Update an item in a checklist on a card.', - } + }, ], default: 'getAll', description: 'The operation to perform.', @@ -1800,7 +1800,7 @@ export class Trello implements INodeType { ], }, }, - description: 'The ID of the checklist to delete.', + description: 'The ID of the checklist item to delete.', }, // ---------------------------------- @@ -1840,7 +1840,7 @@ export class Trello implements INodeType { ], }, }, - description: 'The ID of the checklist to get.', + description: 'The ID of the checklist item to get.', }, { displayName: 'Additional Fields', @@ -1935,9 +1935,19 @@ export class Trello implements INodeType { { displayName: 'State', name: 'state', - type: 'string', - default: '', - description: 'One of: complete, incomplete', + type: 'options', + options: [ + { + name: 'complete', + value: 'complete' + }, + { + name: 'incomplete', + value: 'incomplete', + }, + ], + default: 'complete', + description: 'The resource to operate on.', }, { displayName: 'Checklist ID', @@ -2102,8 +2112,7 @@ export class Trello implements INodeType { { displayName: 'Color', name: 'color', - type: 'string', - default: '', + type: 'options', required: true, displayOptions: { show: { @@ -2115,9 +2124,57 @@ export class Trello implements INodeType { ], }, }, - description: 'The color for the label. See fields for color options.', + options: [ + { + name: 'black', + value: 'black', + }, + { + name: 'blue', + value: 'blue', + }, + { + name: 'green', + value: 'green' + }, + { + name: 'orange', + value: 'orange', + }, + { + name: 'lime', + value: 'lime', + }, + { + name: 'null', + value: 'null', + }, + { + name: 'pink', + value: 'pink', + }, + { + name: 'purple', + value: 'purple', + }, + { + name: 'red', + value: 'red', + }, + { + name: 'sky', + value: 'sky', + }, + { + name: 'yellow', + value: 'yellow' + }, + ], + default: 'null', + description: 'The color for the label.', }, + // ---------------------------------- // label:delete // ---------------------------------- @@ -2364,10 +2421,56 @@ export class Trello implements INodeType { { displayName: 'Color', name: 'color', - type: 'string', - default: '', - description: 'The color for the label. See fields for color options.', - } + type: 'options', + options: [ + { + name: 'black', + value: 'black', + }, + { + name: 'blue', + value: 'blue', + }, + { + name: 'green', + value: 'green' + }, + { + name: 'orange', + value: 'orange', + }, + { + name: 'lime', + value: 'lime', + }, + { + name: 'null', + value: 'null', + }, + { + name: 'pink', + value: 'pink', + }, + { + name: 'purple', + value: 'purple', + }, + { + name: 'red', + value: 'red', + }, + { + name: 'sky', + value: 'sky', + }, + { + name: 'yellow', + value: 'yellow' + }, + ], + default: 'null', + description: 'The color for the label.', + }, ], }, ], @@ -2516,7 +2619,6 @@ export class Trello implements INodeType { throw new Error(`The operation "${operation}" is not known!`); } - } else if (resource === 'list') { if (operation === 'archive') { @@ -2642,6 +2744,7 @@ export class Trello implements INodeType { } else { throw new Error(`The operation "${operation}" is not known!`); } + } else if (resource === 'checklist') { if (operation === 'create') { @@ -2700,6 +2803,7 @@ export class Trello implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; Object.assign(qs, additionalFields); + } else if (operation === 'getCheckItem') { // ---------------------------------- // getCheckItem @@ -2714,6 +2818,7 @@ export class Trello implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; Object.assign(qs, additionalFields); + } else if (operation === 'deleteCheckItem') { // ---------------------------------- // deleteCheckItem @@ -2725,6 +2830,7 @@ export class Trello implements INodeType { const checkItemId = this.getNodeParameter('checkItemId', i) as string; endpoint = `cards/${cardId}/checkItem/${checkItemId}`; + } else if (operation === 'updateCheckItem') { // ---------------------------------- // updateCheckItem @@ -2739,9 +2845,10 @@ export class Trello implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; Object.assign(qs, additionalFields); - } else if(operation ==="completedCheckItems") { + + } else if (operation ==='completedCheckItems') { // ---------------------------------- - // updateCheckItem + // completedCheckItems // ---------------------------------- requestMethod = 'GET'; @@ -2769,15 +2876,13 @@ export class Trello implements INodeType { const name = this.getNodeParameter('name', i) as string; const color = this.getNodeParameter('color', i) as string; - Object.assign(qs, { idBoard, name, color }); - - endpoint = "labels"; + endpoint = 'labels'; } else if (operation === 'delete') { // ---------------------------------- @@ -2848,7 +2953,7 @@ export class Trello implements INodeType { } else if (operation === 'removeLabel') { // ---------------------------------- - // addLabel + // removeLabel // ---------------------------------- requestMethod = 'DELETE'; From 0507c7288410217beea700bdcbce84f22472cf0d Mon Sep 17 00:00:00 2001 From: trojanh Date: Fri, 24 Jan 2020 10:27:59 +0530 Subject: [PATCH 21/41] disqus-node: minor fixes --- packages/nodes-base/nodes/Disqus/Disqus.node.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nodes-base/nodes/Disqus/Disqus.node.ts b/packages/nodes-base/nodes/Disqus/Disqus.node.ts index 3514fac0c7..df07e89456 100644 --- a/packages/nodes-base/nodes/Disqus/Disqus.node.ts +++ b/packages/nodes-base/nodes/Disqus/Disqus.node.ts @@ -316,7 +316,7 @@ export class Disqus implements INodeType { }, ], default: [], - description: 'You may specify filters for your response. Choices: , ``, ``, ``', + description: 'You may specify filters for your response.' }, { displayName: 'Include', @@ -324,8 +324,8 @@ export class Disqus implements INodeType { type: 'multiOptions', options: [ { - name: 'author', - value: 'author', + name: 'approved', + value: 'approved', }, ], default: [], From 0f33582aaab49c1b08bc41e1335269b3a143f038 Mon Sep 17 00:00:00 2001 From: trojanh Date: Fri, 24 Jan 2020 17:58:05 +0530 Subject: [PATCH 22/41] Move operations and fields to new files --- .../nodes/Trello/AttachmentDescription.ts | 276 ++ .../nodes/Trello/BoardDescription.ts | 457 ++++ .../nodes/Trello/CardDescription.ts | 429 +++ .../nodes/Trello/ChecklistDescription.ts | 528 ++++ .../nodes/Trello/LabelDescription.ts | 467 ++++ .../nodes/Trello/ListDescription.ts | 247 ++ .../nodes-base/nodes/Trello/Trello.node.ts | 2431 +---------------- 7 files changed, 2428 insertions(+), 2407 deletions(-) create mode 100644 packages/nodes-base/nodes/Trello/AttachmentDescription.ts create mode 100644 packages/nodes-base/nodes/Trello/BoardDescription.ts create mode 100644 packages/nodes-base/nodes/Trello/CardDescription.ts create mode 100644 packages/nodes-base/nodes/Trello/ChecklistDescription.ts create mode 100644 packages/nodes-base/nodes/Trello/LabelDescription.ts create mode 100644 packages/nodes-base/nodes/Trello/ListDescription.ts diff --git a/packages/nodes-base/nodes/Trello/AttachmentDescription.ts b/packages/nodes-base/nodes/Trello/AttachmentDescription.ts new file mode 100644 index 0000000000..a04f507518 --- /dev/null +++ b/packages/nodes-base/nodes/Trello/AttachmentDescription.ts @@ -0,0 +1,276 @@ +import { INodeProperties } from "n8n-workflow"; + +export const attachmentOperations = [ + // ---------------------------------- + // attachment + // ---------------------------------- + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'attachment', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new attachment for a card', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an attachment', + }, + { + name: 'Get', + value: 'get', + description: 'Get the data of an attachments', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Returns all attachments for the card', + } + ], + default: 'getAll', + description: 'The operation to perform.', + }, + +] as INodeProperties[]; + +export const attachmentFields = [ + + // ---------------------------------- + // attachment:create + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'attachment', + ], + }, + }, + description: 'The ID of the card to add attachment to.', + }, + { + displayName: 'Source URL', + name: 'url', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'attachment', + ], + }, + }, + description: 'The URL of the attachment to add.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'attachment', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'MIME Type', + name: 'mimeType', + type: 'string', + default: '', + placeholder: 'image/png', + description: 'The MIME type of the attachment to add.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'The name of the attachment to add.', + }, + ], + }, + + // ---------------------------------- + // attachment:delete + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'attachment', + ], + }, + }, + description: 'The ID of the card that attachment belongs to.', + }, + { + displayName: 'Attachment ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'attachment', + ], + }, + }, + description: 'The ID of the attachment to delete.', + }, + + // ---------------------------------- + // attachment:getAll + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'attachment', + ], + }, + }, + description: 'The ID of the card to get attachments.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'attachment', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list of fields.', + }, + ], + }, + + // ---------------------------------- + // attachment:get + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'attachment', + ], + }, + }, + description: 'The ID of the card to get attachment.', + }, + { + displayName: 'Attachment ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'attachment', + ], + }, + }, + description: 'The ID of the attachment to get.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'attachment', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list of fields.', + }, + ], + }, + +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Trello/BoardDescription.ts b/packages/nodes-base/nodes/Trello/BoardDescription.ts new file mode 100644 index 0000000000..b089d689ce --- /dev/null +++ b/packages/nodes-base/nodes/Trello/BoardDescription.ts @@ -0,0 +1,457 @@ +import { INodeProperties } from "n8n-workflow"; + +export const boardOperations = [ + // ---------------------------------- + // board + // ---------------------------------- + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'board', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new board', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a board', + }, + { + name: 'Get', + value: 'get', + description: 'Get the data of a board', + }, + { + name: 'Update', + value: 'update', + description: 'Update a board', + }, + ], + default: 'create', + description: 'The operation to perform.', + } +] as INodeProperties[]; + +export const boardFields = [ + + // ---------------------------------- + // board:create + // ---------------------------------- + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + placeholder: 'My board', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'board', + ], + }, + }, + description: 'The name of the board', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'board', + ], + }, + }, + description: 'The description of the board', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'board', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Aging', + name: 'prefs_cardAging', + type: 'options', + options: [ + { + name: 'Pirate', + value: 'pirate', + }, + { + name: 'Regular', + value: 'regular', + }, + ], + default: 'regular', + description: 'Determines the type of card aging that should take place on the board if card aging is enabled.', + }, + { + displayName: 'Background', + name: 'prefs_background', + type: 'string', + default: 'blue', + description: 'The id of a custom background or one of: blue, orange, green, red, purple, pink, lime, sky, grey.', + }, + { + displayName: 'Comments', + name: 'prefs_comments', + type: 'options', + options: [ + { + name: 'Disabled', + value: 'disabled', + }, + { + name: 'Members', + value: 'members', + }, + { + name: 'Observers', + value: 'observers', + }, + { + name: 'Organization', + value: 'org', + }, + { + name: 'Public', + value: 'public', + }, + ], + default: 'members', + description: 'Who can comment on cards on this board.', + }, + { + displayName: 'Covers', + name: 'prefs_cardCovers', + type: 'boolean', + default: true, + description: 'Determines whether card covers are enabled.', + }, + { + displayName: 'Invitations', + name: 'prefs_invitations', + type: 'options', + options: [ + { + name: 'Admins', + value: 'admins', + }, + { + name: 'Members', + value: 'members', + }, + ], + default: 'members', + description: 'Determines what types of members can invite users to join.', + }, + { + displayName: 'Keep From Source', + name: 'keepFromSource', + type: 'string', + default: 'none', + description: 'To keep cards from the original board pass in the value cards.', + }, + { + displayName: 'Labels', + name: 'defaultLabels', + type: 'boolean', + default: true, + description: 'Determines whether to use the default set of labels.', + }, + { + displayName: 'Lists', + name: 'defaultLists', + type: 'boolean', + default: true, + description: 'Determines whether to add the default set of lists to a board(To Do, Doing, Done).It is ignored if idBoardSource is provided.', + }, + { + displayName: 'Organization ID', + name: 'idOrganization', + type: 'string', + default: '', + description: 'The id or name of the team the board should belong to.', + }, + { + displayName: 'Permission Level', + name: 'prefs_permissionLevel', + type: 'options', + options: [ + { + name: 'Organization', + value: 'org', + }, + { + name: 'Private', + value: 'private', + }, + { + name: 'Public', + value: 'public', + }, + ], + default: 'private', + description: 'The permissions level of the board.', + }, + { + displayName: 'Power Ups', + name: 'powerUps', + type: 'options', + options: [ + { + name: 'All', + value: 'all', + }, + { + name: 'Calendar', + value: 'calendar', + }, + { + name: 'Card Aging', + value: 'cardAging', + }, + { + name: 'Recap', + value: 'recap', + }, + { + name: 'Voting', + value: 'voting', + }, + ], + default: 'all', + description: 'The Power-Ups that should be enabled on the new board.', + }, + { + displayName: 'Self Join', + name: 'prefs_selfJoin', + type: 'boolean', + default: true, + description: 'Determines whether users can join the boards themselves or whether they have to be invited.', + }, + { + displayName: 'Source IDs', + name: 'idBoardSource', + type: 'string', + default: '', + description: 'The id of a board to copy into the new board.', + }, + { + displayName: 'Voting', + name: 'prefs_voting', + type: 'options', + options: [ + { + name: 'Disabled', + value: 'disabled', + }, + { + name: 'Members', + value: 'members', + }, + { + name: 'Observers', + value: 'observers', + }, + { + name: 'Organization', + value: 'org', + }, + { + name: 'Public', + value: 'public', + }, + ], + default: 'disabled', + description: 'Who can vote on this board.', + }, + ], + }, + + // ---------------------------------- + // board:delete + // ---------------------------------- + { + displayName: 'Board ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'board', + ], + }, + }, + description: 'The ID of the board to delete.', + }, + + // ---------------------------------- + // board:get + // ---------------------------------- + { + displayName: 'Board ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'board', + ], + }, + }, + description: 'The ID of the board to get.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'board', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list:
closed, dateLastActivity, dateLastView, desc, descData,
idOrganization, invitations, invited, labelNames, memberships,
name, pinned, powerUps, prefs, shortLink, shortUrl,
starred, subscribed, url', + }, + { + displayName: 'Plugin Data', + name: 'pluginData', + type: 'boolean', + default: false, + description: 'Whether to include pluginData on the card with the response.', + }, + ], + }, + + // ---------------------------------- + // board:update + // ---------------------------------- + { + displayName: 'Board ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'board', + ], + }, + }, + description: 'The ID of the board to update.', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'board', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Closed', + name: 'closed', + type: 'boolean', + default: false, + description: 'Whether the board is closed.', + }, + { + displayName: 'Description', + name: 'desc', + type: 'string', + default: '', + description: 'New description of the board', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'New name of the board', + }, + { + displayName: 'Organization ID', + name: 'idOrganization', + type: 'string', + default: '', + description: 'The id of the team the board should be moved to.', + }, + { + displayName: 'Subscribed', + name: 'subscribed', + type: 'boolean', + default: false, + description: 'Whether the acting user is subscribed to the board.', + }, + ], + }, + +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Trello/CardDescription.ts b/packages/nodes-base/nodes/Trello/CardDescription.ts new file mode 100644 index 0000000000..6b5158e6d1 --- /dev/null +++ b/packages/nodes-base/nodes/Trello/CardDescription.ts @@ -0,0 +1,429 @@ +import { INodeProperties } from "n8n-workflow"; + +export const cardOperations = [ + // ---------------------------------- + // card + // ---------------------------------- + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'card', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new card', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a card', + }, + { + name: 'Get', + value: 'get', + description: 'Get the data of a card', + }, + { + name: 'Update', + value: 'update', + description: 'Update a card', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const cardFields = [ + // ---------------------------------- + // card:create + // ---------------------------------- + { + displayName: 'List ID', + name: 'listId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'card', + ], + }, + }, + description: 'The id of the list to create card in', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + placeholder: 'My card', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'card', + ], + }, + }, + description: 'The name of the card', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'card', + ], + }, + }, + description: 'The description of the card', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'card', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Due Date', + name: 'due', + type: 'dateTime', + default: '', + description: 'A due date for the card.', + }, + { + displayName: 'Due Complete', + name: 'dueComplete', + type: 'boolean', + default: false, + description: 'If the card is completed.', + }, + { + displayName: 'Position', + name: 'pos', + type: 'string', + default: 'bottom', + description: 'The position of the new card. top, bottom, or a positive float.', + }, + { + displayName: 'Member IDs', + name: 'idMembers', + type: 'string', + default: '', + description: 'Comma-separated list of member IDs to add to the card.', + }, + { + displayName: 'Label IDs', + name: 'idLabels', + type: 'string', + default: '', + description: 'Comma-separated list of label IDs to add to the card.', + }, + { + displayName: 'URL Source', + name: 'urlSource', + type: 'string', + default: '', + description: 'A source URL to attach to card.', + }, + { + displayName: 'Source ID', + name: 'idCardSource', + type: 'string', + default: '', + description: 'The ID of a card to copy into the new card.', + }, + { + displayName: 'Keep from source', + name: 'keepFromSource', + type: 'string', + default: 'all', + description: 'If using idCardSource you can specify which properties to copy over. all or comma-separated list of: attachments, checklists, comments, due, labels, members, stickers', + }, + ], + }, + + // ---------------------------------- + // card:delete + // ---------------------------------- + { + displayName: 'Card ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'card', + ], + }, + }, + description: 'The ID of the card to delete.', + }, + + // ---------------------------------- + // card:get + // ---------------------------------- + { + displayName: 'Card ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'card', + ], + }, + }, + description: 'The ID of the card to get.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'card', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list:
badges, checkItemStates, closed, dateLastActivity, desc,
descData, due, email, idBoard, idChecklists, idLabels, idList,
idMembers, idShort, idAttachmentCover, manualCoverAttachment
, labels, name, pos, shortUrl, url', + }, + { + displayName: 'Board', + name: 'board', + type: 'boolean', + default: false, + description: 'Whether to return the board object the card is on.', + }, + { + displayName: 'Board Fields', + name: 'board_fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list:
name, desc, descData, closed, idOrganization, pinned, url, prefs', + }, + { + displayName: 'Custom Field Items', + name: 'customFieldItems', + type: 'boolean', + default: false, + description: 'Whether to include the customFieldItems.', + }, + { + displayName: 'Members', + name: 'members', + type: 'boolean', + default: false, + description: 'Whether to return member objects for members on the card.', + }, + { + displayName: 'Member Fields', + name: 'member_fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list:
avatarHash, fullName, initials, username', + }, + { + displayName: 'Plugin Data', + name: 'pluginData', + type: 'boolean', + default: false, + description: 'Whether to include pluginData on the card with the response.', + }, + { + displayName: 'Stickers', + name: 'stickers', + type: 'boolean', + default: false, + description: 'Whether to include sticker models with the response.', + }, + { + displayName: 'Sticker Fields', + name: 'sticker_fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list of sticker fields.', + }, + ], + }, + + // ---------------------------------- + // card:update + // ---------------------------------- + { + displayName: 'Card ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'card', + ], + }, + }, + description: 'The ID of the card to update.', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'card', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Attachment Cover', + name: 'idAttachmentCover', + type: 'string', + default: '', + description: 'The ID of the image attachment the card should use as its cover, or null for none.', + }, + { + displayName: 'Board ID', + name: 'idBoard', + type: 'string', + default: '', + description: 'The ID of the board the card should be on.', + }, + { + displayName: 'Closed', + name: 'closed', + type: 'boolean', + default: false, + description: 'Whether the board is closed.', + }, + { + displayName: 'Description', + name: 'desc', + type: 'string', + default: '', + description: 'New description of the board.', + }, + { + displayName: 'Due Date', + name: 'due', + type: 'dateTime', + default: '', + description: 'A due date for the card.', + }, + { + displayName: 'Due Complete', + name: 'dueComplete', + type: 'boolean', + default: false, + description: 'If the card is completed.', + }, + { + displayName: 'Label IDs', + name: 'idLabels', + type: 'string', + default: '', + description: 'Comma-separated list of label IDs to set on card.', + }, + { + displayName: 'List ID', + name: 'idList', + type: 'string', + default: '', + description: 'The ID of the list the card should be in.', + }, + { + displayName: 'Member IDs', + name: 'idMembers', + type: 'string', + default: '', + description: 'Comma-separated list of member IDs to set on card.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'New name of the board', + }, + { + displayName: 'Position', + name: 'pos', + type: 'string', + default: 'bottom', + description: 'The position of the card. top, bottom, or a positive float.', + }, + { + displayName: 'Subscribed', + name: 'subscribed', + type: 'boolean', + default: false, + description: 'Whether the acting user is subscribed to the board.', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Trello/ChecklistDescription.ts b/packages/nodes-base/nodes/Trello/ChecklistDescription.ts new file mode 100644 index 0000000000..34036f83e2 --- /dev/null +++ b/packages/nodes-base/nodes/Trello/ChecklistDescription.ts @@ -0,0 +1,528 @@ +import { INodeProperties } from "n8n-workflow"; + +export const checklistOperations = [ + // ---------------------------------- + // checklist + // ---------------------------------- + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'checklist', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new checklist', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a checklist', + }, + { + name: 'Delete Checklist Item', + value: 'deleteCheckItem', + description: 'Delete a checklist item', + }, + { + name: 'Get', + value: 'get', + description: 'Get the data of a checklist', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Returns all checklists for the card', + }, + { + name: 'Get Checklist Items', + value: 'getCheckItem', + description: 'Get a specific Checklist on a card', + }, + { + name: 'Get Completed Checklist Items', + value: 'completedCheckItems', + description: 'Get the completed Checklist items on a card', + }, + { + name: 'Update Checklist Item', + value: 'updateCheckItem', + description: 'Update an item in a checklist on a card.', + }, + ], + default: 'getAll', + description: 'The operation to perform.', + }, + +] as INodeProperties[]; + +export const checklistFields = [ + // ---------------------------------- + // checklist:create + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the card to add checklist to.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The URL of the checklist to add.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'checklist', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Id Of Checklist Source', + name: 'idChecklistSource', + type: 'string', + default: '', + description: 'The ID of a source checklist to copy into the new one.', + }, + { + displayName: 'Position', + name: 'pos', + type: 'string', + default: '', + description: 'The position of the checklist on the card. One of: top, bottom, or a positive number.', + }, + ], + }, + + // ---------------------------------- + // checklist:delete + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the card that checklist belongs to.', + }, + { + displayName: 'Checklist ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the checklist to delete.', + }, + + + // ---------------------------------- + // checklist:getAll + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the card to get checklists.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'checklist', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list of fields.', + }, + ], + }, + + // ---------------------------------- + // checklist:get + // ---------------------------------- + { + displayName: 'Checklist ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the checklist to get.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'checklist', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list of fields.', + }, + ], + }, + + // ---------------------------------- + // checklist:deleteCheckItem + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'deleteCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the card that checklist belongs to.', + }, + { + displayName: 'CheckItem ID', + name: 'checkItemId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'deleteCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the checklist item to delete.', + }, + + // ---------------------------------- + // checklist:getCheckItem + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'getCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the card that checklist belongs to.', + }, + { + displayName: 'CheckItem ID', + name: 'checkItemId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'getCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the checklist item to get.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'getCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list of fields.', + }, + ], + }, + + // ---------------------------------- + // checklist:updateCheckItem + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'updateCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the card that checklist belongs to.', + }, + { + displayName: 'CheckItem ID', + name: 'checkItemId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'updateCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the checklist item to update.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'updateCheckItem', + ], + resource: [ + 'checklist', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'The new name for the checklist item.', + }, + { + displayName: 'State', + name: 'state', + type: 'options', + options: [ + { + name: 'complete', + value: 'complete' + }, + { + name: 'incomplete', + value: 'incomplete', + }, + ], + default: 'complete', + description: 'The resource to operate on.', + }, + { + displayName: 'Checklist ID', + name: 'checklistId', + type: 'string', + default: '', + description: 'The ID of the checklist this item is in', + }, + { + displayName: 'Position', + name: 'pos', + type: 'string', + default: '', + description: 'The position of the checklist on the card. One of: top, bottom, or a positive number.', + }, + ], + }, + + // ---------------------------------- + // checklist:completedCheckItems + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'completedCheckItems', + ], + resource: [ + 'checklist', + ], + }, + }, + description: 'The ID of the card for checkItems.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'completedCheckItems', + ], + resource: [ + 'checklist', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list of: "idCheckItem", "state".', + }, + ], + }, + +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Trello/LabelDescription.ts b/packages/nodes-base/nodes/Trello/LabelDescription.ts new file mode 100644 index 0000000000..9c23053282 --- /dev/null +++ b/packages/nodes-base/nodes/Trello/LabelDescription.ts @@ -0,0 +1,467 @@ +import { INodeProperties } from "n8n-workflow"; + +export const labelOperations = [ + // ---------------------------------- + // label + // ---------------------------------- + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'label', + ], + }, + }, + options: [ + { + name: 'Add to Card', + value: 'addLabel', + description: 'Add a label to a card.', + }, + { + name: 'Create', + value: 'create', + description: 'Create a new label', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a label', + }, + { + name: 'Get', + value: 'get', + description: 'Get the data of a label', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Returns all label for the board', + }, + { + name: 'Remove From Card', + value: 'removeLabel', + description: 'Remove a label from a card.', + }, + { + name: 'Update', + value: 'update', + description: 'Update a label.', + } + + ], + default: 'getAll', + description: 'The operation to perform.', + }, + +] as INodeProperties[]; + +export const labelFields = [ + // ---------------------------------- + // label:create + // ---------------------------------- + { + displayName: 'Board ID', + name: 'boardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the board to create the label on.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'label', + ], + }, + }, + description: 'Name for the label.', + }, + { + displayName: 'Color', + name: 'color', + type: 'options', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'label', + ], + }, + }, + options: [ + { + name: 'black', + value: 'black', + }, + { + name: 'blue', + value: 'blue', + }, + { + name: 'green', + value: 'green' + }, + { + name: 'orange', + value: 'orange', + }, + { + name: 'lime', + value: 'lime', + }, + { + name: 'null', + value: 'null', + }, + { + name: 'pink', + value: 'pink', + }, + { + name: 'purple', + value: 'purple', + }, + { + name: 'red', + value: 'red', + }, + { + name: 'sky', + value: 'sky', + }, + { + name: 'yellow', + value: 'yellow' + }, + ], + default: 'null', + description: 'The color for the label.', + }, + + + // ---------------------------------- + // label:delete + // ---------------------------------- + { + displayName: 'Label ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the label to delete.', + }, + + // ---------------------------------- + // label:getAll + // ---------------------------------- + { + displayName: 'Board ID', + name: 'boardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the board to get label.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'label', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list of fields.', + }, + ], + }, + + // ---------------------------------- + // label:get + // ---------------------------------- + { + displayName: 'Label ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'label', + ], + }, + }, + description: 'Get information about a label by ID.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'label', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: 'all', + description: 'Fields to return. Either "all" or a comma-separated list of fields.', + }, + ], + }, + + // ---------------------------------- + // label:addLabel + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'addLabel', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the card to get label.', + }, + { + displayName: 'Label ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'addLabel', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the label to add.', + }, + + // ---------------------------------- + // label:removeLabel + // ---------------------------------- + { + displayName: 'Card ID', + name: 'cardId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'removeLabel', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the card to remove label from.', + }, + { + displayName: 'Label ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'removeLabel', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the label to remove.', + }, + + // ---------------------------------- + // label:update + // ---------------------------------- + { + displayName: 'Label ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'label', + ], + }, + }, + description: 'The ID of the label to update.', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'label', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'Name of the label.', + }, + { + displayName: 'Color', + name: 'color', + type: 'options', + options: [ + { + name: 'black', + value: 'black', + }, + { + name: 'blue', + value: 'blue', + }, + { + name: 'green', + value: 'green' + }, + { + name: 'orange', + value: 'orange', + }, + { + name: 'lime', + value: 'lime', + }, + { + name: 'null', + value: 'null', + }, + { + name: 'pink', + value: 'pink', + }, + { + name: 'purple', + value: 'purple', + }, + { + name: 'red', + value: 'red', + }, + { + name: 'sky', + value: 'sky', + }, + { + name: 'yellow', + value: 'yellow' + }, + ], + default: 'null', + description: 'The color for the label.', + }, + ], + }, + +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Trello/ListDescription.ts b/packages/nodes-base/nodes/Trello/ListDescription.ts new file mode 100644 index 0000000000..b6b4565632 --- /dev/null +++ b/packages/nodes-base/nodes/Trello/ListDescription.ts @@ -0,0 +1,247 @@ +import { INodeProperties } from "n8n-workflow"; + +export const listOperations = [ + // ---------------------------------- + // list + // ---------------------------------- + { + displayName: "Operation", + name: "operation", + type: "options", + displayOptions: { + show: { + resource: ["list"] + } + }, + options: [ + { + name: "Archive", + value: "archive", + description: "Archive/Unarchive a list" + }, + { + name: "Create", + value: "create", + description: "Create a new list" + }, + { + name: "Get", + value: "get", + description: "Get the data of a list" + }, + { + name: "Update", + value: "update", + description: "Update a list" + } + ], + default: "create", + description: "The operation to perform." + } +] as INodeProperties[]; + +export const listFields = [ + // ---------------------------------- + // list:archive + // ---------------------------------- + { + displayName: "List ID", + name: "id", + type: "string", + default: "", + required: true, + displayOptions: { + show: { + operation: ["archive"], + resource: ["list"] + } + }, + description: "The ID of the list to archive or unarchive." + }, + { + displayName: "Archive", + name: "archive", + type: "boolean", + default: false, + displayOptions: { + show: { + operation: ["archive"], + resource: ["list"] + } + }, + description: "If the list should be archived or unarchived." + }, + + // ---------------------------------- + // list:create + // ---------------------------------- + { + displayName: "Board ID", + name: "idBoard", + type: "string", + default: "", + required: true, + displayOptions: { + show: { + operation: ["create"], + resource: ["list"] + } + }, + description: "The ID of the board the list should be created in" + }, + { + displayName: "Name", + name: "name", + type: "string", + default: "", + placeholder: "My list", + required: true, + displayOptions: { + show: { + operation: ["create"], + resource: ["list"] + } + }, + description: "The name of the list" + }, + { + displayName: "Additional Fields", + name: "additionalFields", + type: "collection", + placeholder: "Add Field", + displayOptions: { + show: { + operation: ["create"], + resource: ["list"] + } + }, + default: {}, + options: [ + { + displayName: "List Source", + name: "idListSource", + type: "string", + default: "", + description: "ID of the list to copy into the new list." + }, + { + displayName: "Position", + name: "pos", + type: "string", + default: "bottom", + description: + "The position of the new list. top, bottom, or a positive float." + } + ] + }, + + // ---------------------------------- + // list:get + // ---------------------------------- + { + displayName: "List ID", + name: "id", + type: "string", + default: "", + required: true, + displayOptions: { + show: { + operation: ["get"], + resource: ["list"] + } + }, + description: "The ID of the list to get." + }, + { + displayName: "Additional Fields", + name: "additionalFields", + type: "collection", + placeholder: "Add Field", + displayOptions: { + show: { + operation: ["get"], + resource: ["list"] + } + }, + default: {}, + options: [ + { + displayName: "Fields", + name: "fields", + type: "string", + default: "all", + description: + 'Fields to return. Either "all" or a comma-separated list of fields.' + } + ] + }, + + // ---------------------------------- + // list:update + // ---------------------------------- + { + displayName: "List ID", + name: "id", + type: "string", + default: "", + required: true, + displayOptions: { + show: { + operation: ["update"], + resource: ["list"] + } + }, + description: "The ID of the list to update." + }, + { + displayName: "Update Fields", + name: "updateFields", + type: "collection", + placeholder: "Add Field", + displayOptions: { + show: { + operation: ["update"], + resource: ["list"] + } + }, + default: {}, + options: [ + { + displayName: "Board ID", + name: "idBoard", + type: "string", + default: "", + description: "ID of a board the list should be moved to." + }, + { + displayName: "Closed", + name: "closed", + type: "boolean", + default: false, + description: "Whether the list is closed." + }, + { + displayName: "Name", + name: "name", + type: "string", + default: "", + description: "New name of the list" + }, + { + displayName: "Position", + name: "pos", + type: "string", + default: "bottom", + description: + "The position of the list. top, bottom, or a positive float." + }, + { + displayName: "Subscribed", + name: "subscribed", + type: "boolean", + default: false, + description: "Whether the acting user is subscribed to the list." + } + ] + } +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Trello/Trello.node.ts b/packages/nodes-base/nodes/Trello/Trello.node.ts index ed2e9c2cee..527c94bfb1 100644 --- a/packages/nodes-base/nodes/Trello/Trello.node.ts +++ b/packages/nodes-base/nodes/Trello/Trello.node.ts @@ -1,6 +1,4 @@ -import { - IExecuteFunctions, -} from 'n8n-core'; +import { IExecuteFunctions } from "n8n-core"; import { IDataObject, INodeTypeDescription, @@ -8,9 +6,13 @@ import { INodeType, } from 'n8n-workflow'; -import { - apiRequest, -} from './GenericFunctions'; +import { apiRequest } from "./GenericFunctions"; +import { attachmentOperations, attachmentFields } from './AttachmentDescription'; +import { boardOperations, boardFields } from './BoardDescription'; +import { cardOperations, cardFields } from './CardDescription'; +import { checklistOperations, checklistFields } from './ChecklistDescription'; +import { labelOperations, labelFields } from './LabelDescription'; +import { listOperations, listFields } from './ListDescription'; export class Trello implements INodeType { description: INodeTypeDescription = { @@ -68,2411 +70,26 @@ export class Trello implements INodeType { description: 'The resource to operate on.', }, - + // ---------------------------------- + // operations + // ---------------------------------- + ...attachmentOperations, + ...boardOperations, + ...cardOperations, + ...checklistOperations, + ...labelOperations, + ...listOperations, // ---------------------------------- - // board + // fields // ---------------------------------- - { - displayName: 'Operation', - name: 'operation', - type: 'options', - displayOptions: { - show: { - resource: [ - 'board', - ], - }, - }, - options: [ - { - name: 'Create', - value: 'create', - description: 'Create a new board', - }, - { - name: 'Delete', - value: 'delete', - description: 'Delete a board', - }, - { - name: 'Get', - value: 'get', - description: 'Get the data of a board', - }, - { - name: 'Update', - value: 'update', - description: 'Update a board', - }, - ], - default: 'create', - description: 'The operation to perform.', - }, + ...attachmentFields, + ...boardFields, + ...cardFields, + ...checklistFields, + ...labelFields, + ...listFields - // ---------------------------------- - // board:create - // ---------------------------------- - { - displayName: 'Name', - name: 'name', - type: 'string', - default: '', - placeholder: 'My board', - required: true, - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'board', - ], - }, - }, - description: 'The name of the board', - }, - { - displayName: 'Description', - name: 'description', - type: 'string', - default: '', - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'board', - ], - }, - }, - description: 'The description of the board', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'board', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Aging', - name: 'prefs_cardAging', - type: 'options', - options: [ - { - name: 'Pirate', - value: 'pirate', - }, - { - name: 'Regular', - value: 'regular', - }, - ], - default: 'regular', - description: 'Determines the type of card aging that should take place on the board if card aging is enabled.', - }, - { - displayName: 'Background', - name: 'prefs_background', - type: 'string', - default: 'blue', - description: 'The id of a custom background or one of: blue, orange, green, red, purple, pink, lime, sky, grey.', - }, - { - displayName: 'Comments', - name: 'prefs_comments', - type: 'options', - options: [ - { - name: 'Disabled', - value: 'disabled', - }, - { - name: 'Members', - value: 'members', - }, - { - name: 'Observers', - value: 'observers', - }, - { - name: 'Organization', - value: 'org', - }, - { - name: 'Public', - value: 'public', - }, - ], - default: 'members', - description: 'Who can comment on cards on this board.', - }, - { - displayName: 'Covers', - name: 'prefs_cardCovers', - type: 'boolean', - default: true, - description: 'Determines whether card covers are enabled.', - }, - { - displayName: 'Invitations', - name: 'prefs_invitations', - type: 'options', - options: [ - { - name: 'Admins', - value: 'admins', - }, - { - name: 'Members', - value: 'members', - }, - ], - default: 'members', - description: 'Determines what types of members can invite users to join.', - }, - { - displayName: 'Keep From Source', - name: 'keepFromSource', - type: 'string', - default: 'none', - description: 'To keep cards from the original board pass in the value cards.', - }, - { - displayName: 'Labels', - name: 'defaultLabels', - type: 'boolean', - default: true, - description: 'Determines whether to use the default set of labels.', - }, - { - displayName: 'Lists', - name: 'defaultLists', - type: 'boolean', - default: true, - description: 'Determines whether to add the default set of lists to a board(To Do, Doing, Done).It is ignored if idBoardSource is provided.', - }, - { - displayName: 'Organization ID', - name: 'idOrganization', - type: 'string', - default: '', - description: 'The id or name of the team the board should belong to.', - }, - { - displayName: 'Permission Level', - name: 'prefs_permissionLevel', - type: 'options', - options: [ - { - name: 'Organization', - value: 'org', - }, - { - name: 'Private', - value: 'private', - }, - { - name: 'Public', - value: 'public', - }, - ], - default: 'private', - description: 'The permissions level of the board.', - }, - { - displayName: 'Power Ups', - name: 'powerUps', - type: 'options', - options: [ - { - name: 'All', - value: 'all', - }, - { - name: 'Calendar', - value: 'calendar', - }, - { - name: 'Card Aging', - value: 'cardAging', - }, - { - name: 'Recap', - value: 'recap', - }, - { - name: 'Voting', - value: 'voting', - }, - ], - default: 'all', - description: 'The Power-Ups that should be enabled on the new board.', - }, - { - displayName: 'Self Join', - name: 'prefs_selfJoin', - type: 'boolean', - default: true, - description: 'Determines whether users can join the boards themselves or whether they have to be invited.', - }, - { - displayName: 'Source IDs', - name: 'idBoardSource', - type: 'string', - default: '', - description: 'The id of a board to copy into the new board.', - }, - { - displayName: 'Voting', - name: 'prefs_voting', - type: 'options', - options: [ - { - name: 'Disabled', - value: 'disabled', - }, - { - name: 'Members', - value: 'members', - }, - { - name: 'Observers', - value: 'observers', - }, - { - name: 'Organization', - value: 'org', - }, - { - name: 'Public', - value: 'public', - }, - ], - default: 'disabled', - description: 'Who can vote on this board.', - }, - ], - }, - - // ---------------------------------- - // board:delete - // ---------------------------------- - { - displayName: 'Board ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'delete', - ], - resource: [ - 'board', - ], - }, - }, - description: 'The ID of the board to delete.', - }, - - // ---------------------------------- - // board:get - // ---------------------------------- - { - displayName: 'Board ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'get', - ], - resource: [ - 'board', - ], - }, - }, - description: 'The ID of the board to get.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'get', - ], - resource: [ - 'board', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Fields', - name: 'fields', - type: 'string', - default: 'all', - description: 'Fields to return. Either "all" or a comma-separated list:
closed, dateLastActivity, dateLastView, desc, descData,
idOrganization, invitations, invited, labelNames, memberships,
name, pinned, powerUps, prefs, shortLink, shortUrl,
starred, subscribed, url', - }, - { - displayName: 'Plugin Data', - name: 'pluginData', - type: 'boolean', - default: false, - description: 'Whether to include pluginData on the card with the response.', - }, - ], - }, - - // ---------------------------------- - // board:update - // ---------------------------------- - { - displayName: 'Board ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'update', - ], - resource: [ - 'board', - ], - }, - }, - description: 'The ID of the board to update.', - }, - { - displayName: 'Update Fields', - name: 'updateFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'update', - ], - resource: [ - 'board', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Closed', - name: 'closed', - type: 'boolean', - default: false, - description: 'Whether the board is closed.', - }, - { - displayName: 'Description', - name: 'desc', - type: 'string', - default: '', - description: 'New description of the board', - }, - { - displayName: 'Name', - name: 'name', - type: 'string', - default: '', - description: 'New name of the board', - }, - { - displayName: 'Organization ID', - name: 'idOrganization', - type: 'string', - default: '', - description: 'The id of the team the board should be moved to.', - }, - { - displayName: 'Subscribed', - name: 'subscribed', - type: 'boolean', - default: false, - description: 'Whether the acting user is subscribed to the board.', - }, - ], - }, - - - - // ---------------------------------- - // card - // ---------------------------------- - { - displayName: 'Operation', - name: 'operation', - type: 'options', - displayOptions: { - show: { - resource: [ - 'card', - ], - }, - }, - options: [ - { - name: 'Create', - value: 'create', - description: 'Create a new card', - }, - { - name: 'Delete', - value: 'delete', - description: 'Delete a card', - }, - { - name: 'Get', - value: 'get', - description: 'Get the data of a card', - }, - { - name: 'Update', - value: 'update', - description: 'Update a card', - }, - ], - default: 'create', - description: 'The operation to perform.', - }, - - // ---------------------------------- - // card:create - // ---------------------------------- - { - displayName: 'List ID', - name: 'listId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'card', - ], - }, - }, - description: 'The id of the list to create card in', - }, - { - displayName: 'Name', - name: 'name', - type: 'string', - default: '', - placeholder: 'My card', - required: true, - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'card', - ], - }, - }, - description: 'The name of the card', - }, - { - displayName: 'Description', - name: 'description', - type: 'string', - default: '', - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'card', - ], - }, - }, - description: 'The description of the card', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'card', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Due Date', - name: 'due', - type: 'dateTime', - default: '', - description: 'A due date for the card.', - }, - { - displayName: 'Due Complete', - name: 'dueComplete', - type: 'boolean', - default: false, - description: 'If the card is completed.', - }, - { - displayName: 'Position', - name: 'pos', - type: 'string', - default: 'bottom', - description: 'The position of the new card. top, bottom, or a positive float.', - }, - { - displayName: 'Member IDs', - name: 'idMembers', - type: 'string', - default: '', - description: 'Comma-separated list of member IDs to add to the card.', - }, - { - displayName: 'Label IDs', - name: 'idLabels', - type: 'string', - default: '', - description: 'Comma-separated list of label IDs to add to the card.', - }, - { - displayName: 'URL Source', - name: 'urlSource', - type: 'string', - default: '', - description: 'A source URL to attach to card.', - }, - { - displayName: 'Source ID', - name: 'idCardSource', - type: 'string', - default: '', - description: 'The ID of a card to copy into the new card.', - }, - { - displayName: 'Keep from source', - name: 'keepFromSource', - type: 'string', - default: 'all', - description: 'If using idCardSource you can specify which properties to copy over. all or comma-separated list of: attachments, checklists, comments, due, labels, members, stickers', - }, - ], - }, - - // ---------------------------------- - // card:delete - // ---------------------------------- - { - displayName: 'Card ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'delete', - ], - resource: [ - 'card', - ], - }, - }, - description: 'The ID of the card to delete.', - }, - - // ---------------------------------- - // card:get - // ---------------------------------- - { - displayName: 'Card ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'get', - ], - resource: [ - 'card', - ], - }, - }, - description: 'The ID of the card to get.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'get', - ], - resource: [ - 'card', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Fields', - name: 'fields', - type: 'string', - default: 'all', - description: 'Fields to return. Either "all" or a comma-separated list:
badges, checkItemStates, closed, dateLastActivity, desc,
descData, due, email, idBoard, idChecklists, idLabels, idList,
idMembers, idShort, idAttachmentCover, manualCoverAttachment
, labels, name, pos, shortUrl, url', - }, - { - displayName: 'Board', - name: 'board', - type: 'boolean', - default: false, - description: 'Whether to return the board object the card is on.', - }, - { - displayName: 'Board Fields', - name: 'board_fields', - type: 'string', - default: 'all', - description: 'Fields to return. Either "all" or a comma-separated list:
name, desc, descData, closed, idOrganization, pinned, url, prefs', - }, - { - displayName: 'Custom Field Items', - name: 'customFieldItems', - type: 'boolean', - default: false, - description: 'Whether to include the customFieldItems.', - }, - { - displayName: 'Members', - name: 'members', - type: 'boolean', - default: false, - description: 'Whether to return member objects for members on the card.', - }, - { - displayName: 'Member Fields', - name: 'member_fields', - type: 'string', - default: 'all', - description: 'Fields to return. Either "all" or a comma-separated list:
avatarHash, fullName, initials, username', - }, - { - displayName: 'Plugin Data', - name: 'pluginData', - type: 'boolean', - default: false, - description: 'Whether to include pluginData on the card with the response.', - }, - { - displayName: 'Stickers', - name: 'stickers', - type: 'boolean', - default: false, - description: 'Whether to include sticker models with the response.', - }, - { - displayName: 'Sticker Fields', - name: 'sticker_fields', - type: 'string', - default: 'all', - description: 'Fields to return. Either "all" or a comma-separated list of sticker fields.', - }, - ], - }, - - // ---------------------------------- - // card:update - // ---------------------------------- - { - displayName: 'Card ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'update', - ], - resource: [ - 'card', - ], - }, - }, - description: 'The ID of the card to update.', - }, - { - displayName: 'Update Fields', - name: 'updateFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'update', - ], - resource: [ - 'card', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Attachment Cover', - name: 'idAttachmentCover', - type: 'string', - default: '', - description: 'The ID of the image attachment the card should use as its cover, or null for none.', - }, - { - displayName: 'Board ID', - name: 'idBoard', - type: 'string', - default: '', - description: 'The ID of the board the card should be on.', - }, - { - displayName: 'Closed', - name: 'closed', - type: 'boolean', - default: false, - description: 'Whether the board is closed.', - }, - { - displayName: 'Description', - name: 'desc', - type: 'string', - default: '', - description: 'New description of the board.', - }, - { - displayName: 'Due Date', - name: 'due', - type: 'dateTime', - default: '', - description: 'A due date for the card.', - }, - { - displayName: 'Due Complete', - name: 'dueComplete', - type: 'boolean', - default: false, - description: 'If the card is completed.', - }, - { - displayName: 'Label IDs', - name: 'idLabels', - type: 'string', - default: '', - description: 'Comma-separated list of label IDs to set on card.', - }, - { - displayName: 'List ID', - name: 'idList', - type: 'string', - default: '', - description: 'The ID of the list the card should be in.', - }, - { - displayName: 'Member IDs', - name: 'idMembers', - type: 'string', - default: '', - description: 'Comma-separated list of member IDs to set on card.', - }, - { - displayName: 'Name', - name: 'name', - type: 'string', - default: '', - description: 'New name of the board', - }, - { - displayName: 'Position', - name: 'pos', - type: 'string', - default: 'bottom', - description: 'The position of the card. top, bottom, or a positive float.', - }, - { - displayName: 'Subscribed', - name: 'subscribed', - type: 'boolean', - default: false, - description: 'Whether the acting user is subscribed to the board.', - }, - ], - }, - - - - // ---------------------------------- - // list - // ---------------------------------- - { - displayName: 'Operation', - name: 'operation', - type: 'options', - displayOptions: { - show: { - resource: [ - 'list', - ], - }, - }, - options: [ - { - name: 'Archive', - value: 'archive', - description: 'Archive/Unarchive a list', - }, - { - name: 'Create', - value: 'create', - description: 'Create a new list', - }, - { - name: 'Get', - value: 'get', - description: 'Get the data of a list', - }, - { - name: 'Update', - value: 'update', - description: 'Update a list', - }, - ], - default: 'create', - description: 'The operation to perform.', - }, - - // ---------------------------------- - // list:archive - // ---------------------------------- - { - displayName: 'List ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'archive', - ], - resource: [ - 'list', - ], - }, - }, - description: 'The ID of the list to archive or unarchive.', - }, - { - displayName: 'Archive', - name: 'archive', - type: 'boolean', - default: false, - displayOptions: { - show: { - operation: [ - 'archive', - ], - resource: [ - 'list', - ], - }, - }, - description: 'If the list should be archived or unarchived.', - }, - - // ---------------------------------- - // list:create - // ---------------------------------- - { - displayName: 'Board ID', - name: 'idBoard', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'list', - ], - }, - }, - description: 'The ID of the board the list should be created in', - }, - { - displayName: 'Name', - name: 'name', - type: 'string', - default: '', - placeholder: 'My list', - required: true, - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'list', - ], - }, - }, - description: 'The name of the list', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'list', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'List Source', - name: 'idListSource', - type: 'string', - default: '', - description: 'ID of the list to copy into the new list.', - }, - { - displayName: 'Position', - name: 'pos', - type: 'string', - default: 'bottom', - description: 'The position of the new list. top, bottom, or a positive float.', - }, - ], - }, - - // ---------------------------------- - // list:get - // ---------------------------------- - { - displayName: 'List ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'get', - ], - resource: [ - 'list', - ], - }, - }, - description: 'The ID of the list to get.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'get', - ], - resource: [ - 'list', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Fields', - name: 'fields', - type: 'string', - default: 'all', - description: 'Fields to return. Either "all" or a comma-separated list of fields.', - }, - ], - }, - - // ---------------------------------- - // list:update - // ---------------------------------- - { - displayName: 'List ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'update', - ], - resource: [ - 'list', - ], - }, - }, - description: 'The ID of the list to update.', - }, - { - displayName: 'Update Fields', - name: 'updateFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'update', - ], - resource: [ - 'list', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Board ID', - name: 'idBoard', - type: 'string', - default: '', - description: 'ID of a board the list should be moved to.', - }, - { - displayName: 'Closed', - name: 'closed', - type: 'boolean', - default: false, - description: 'Whether the list is closed.', - }, - { - displayName: 'Name', - name: 'name', - type: 'string', - default: '', - description: 'New name of the list', - }, - { - displayName: 'Position', - name: 'pos', - type: 'string', - default: 'bottom', - description: 'The position of the list. top, bottom, or a positive float.', - }, - { - displayName: 'Subscribed', - name: 'subscribed', - type: 'boolean', - default: false, - description: 'Whether the acting user is subscribed to the list.', - }, - ], - }, - - // ---------------------------------- - // attachment - // ---------------------------------- - { - displayName: 'Operation', - name: 'operation', - type: 'options', - displayOptions: { - show: { - resource: [ - 'attachment', - ], - }, - }, - options: [ - { - name: 'Create', - value: 'create', - description: 'Create a new attachment for a card', - }, - { - name: 'Delete', - value: 'delete', - description: 'Delete an attachment', - }, - { - name: 'Get', - value: 'get', - description: 'Get the data of an attachments', - }, - { - name: 'Get All', - value: 'getAll', - description: 'Returns all attachments for the card', - } - ], - default: 'getAll', - description: 'The operation to perform.', - }, - - // ---------------------------------- - // attachment:create - // ---------------------------------- - { - displayName: 'Card ID', - name: 'cardId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'attachment', - ], - }, - }, - description: 'The ID of the card to add attachment to.', - }, - { - displayName: 'Source URL', - name: 'url', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'attachment', - ], - }, - }, - description: 'The URL of the attachment to add.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'attachment', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'MIME Type', - name: 'mimeType', - type: 'string', - default: '', - placeholder: 'image/png', - description: 'The MIME type of the attachment to add.', - }, - { - displayName: 'Name', - name: 'name', - type: 'string', - default: '', - description: 'The name of the attachment to add.', - }, - ], - }, - - // ---------------------------------- - // attachment:delete - // ---------------------------------- - { - displayName: 'Card ID', - name: 'cardId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'delete', - ], - resource: [ - 'attachment', - ], - }, - }, - description: 'The ID of the card that attachment belongs to.', - }, - { - displayName: 'Attachment ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'delete', - ], - resource: [ - 'attachment', - ], - }, - }, - description: 'The ID of the attachment to delete.', - }, - - // ---------------------------------- - // attachment:getAll - // ---------------------------------- - { - displayName: 'Card ID', - name: 'cardId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'attachment', - ], - }, - }, - description: 'The ID of the card to get attachments.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'attachment', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Fields', - name: 'fields', - type: 'string', - default: 'all', - description: 'Fields to return. Either "all" or a comma-separated list of fields.', - }, - ], - }, - - // ---------------------------------- - // attachment:get - // ---------------------------------- - { - displayName: 'Card ID', - name: 'cardId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'get', - ], - resource: [ - 'attachment', - ], - }, - }, - description: 'The ID of the card to get attachment.', - }, - { - displayName: 'Attachment ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'get', - ], - resource: [ - 'attachment', - ], - }, - }, - description: 'The ID of the attachment to get.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'get', - ], - resource: [ - 'attachment', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Fields', - name: 'fields', - type: 'string', - default: 'all', - description: 'Fields to return. Either "all" or a comma-separated list of fields.', - }, - ], - }, - - // ---------------------------------- - // checklist - // ---------------------------------- - { - displayName: 'Operation', - name: 'operation', - type: 'options', - displayOptions: { - show: { - resource: [ - 'checklist', - ], - }, - }, - options: [ - { - name: 'Create', - value: 'create', - description: 'Create a new checklist', - }, - { - name: 'Delete', - value: 'delete', - description: 'Delete a checklist', - }, - { - name: 'Delete Checklist Item', - value: 'deleteCheckItem', - description: 'Delete a checklist item', - }, - { - name: 'Get', - value: 'get', - description: 'Get the data of a checklist', - }, - { - name: 'Get All', - value: 'getAll', - description: 'Returns all checklists for the card', - }, - { - name: 'Get Checklist Items', - value: 'getCheckItem', - description: 'Get a specific Checklist on a card', - }, - { - name: 'Get Completed Checklist Items', - value: 'completedCheckItems', - description: 'Get the completed Checklist items on a card', - }, - { - name: 'Update Checklist Item', - value: 'updateCheckItem', - description: 'Update an item in a checklist on a card.', - }, - ], - default: 'getAll', - description: 'The operation to perform.', - }, - - // ---------------------------------- - // checklist:create - // ---------------------------------- - { - displayName: 'Card ID', - name: 'cardId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'checklist', - ], - }, - }, - description: 'The ID of the card to add checklist to.', - }, - { - displayName: 'Name', - name: 'name', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'checklist', - ], - }, - }, - description: 'The URL of the checklist to add.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'checklist', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Id Of Checklist Source', - name: 'idChecklistSource', - type: 'string', - default: '', - description: 'The ID of a source checklist to copy into the new one.', - }, - { - displayName: 'Position', - name: 'pos', - type: 'string', - default: '', - description: 'The position of the checklist on the card. One of: top, bottom, or a positive number.', - }, - ], - }, - - // ---------------------------------- - // checklist:delete - // ---------------------------------- - { - displayName: 'Card ID', - name: 'cardId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'delete', - ], - resource: [ - 'checklist', - ], - }, - }, - description: 'The ID of the card that checklist belongs to.', - }, - { - displayName: 'Checklist ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'delete', - ], - resource: [ - 'checklist', - ], - }, - }, - description: 'The ID of the checklist to delete.', - }, - - - // ---------------------------------- - // checklist:getAll - // ---------------------------------- - { - displayName: 'Card ID', - name: 'cardId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'checklist', - ], - }, - }, - description: 'The ID of the card to get checklists.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'checklist', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Fields', - name: 'fields', - type: 'string', - default: 'all', - description: 'Fields to return. Either "all" or a comma-separated list of fields.', - }, - ], - }, - - // ---------------------------------- - // checklist:get - // ---------------------------------- - { - displayName: 'Checklist ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'get', - ], - resource: [ - 'checklist', - ], - }, - }, - description: 'The ID of the checklist to get.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'get', - ], - resource: [ - 'checklist', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Fields', - name: 'fields', - type: 'string', - default: 'all', - description: 'Fields to return. Either "all" or a comma-separated list of fields.', - }, - ], - }, - - // ---------------------------------- - // checklist:deleteCheckItem - // ---------------------------------- - { - displayName: 'Card ID', - name: 'cardId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'deleteCheckItem', - ], - resource: [ - 'checklist', - ], - }, - }, - description: 'The ID of the card that checklist belongs to.', - }, - { - displayName: 'CheckItem ID', - name: 'checkItemId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'deleteCheckItem', - ], - resource: [ - 'checklist', - ], - }, - }, - description: 'The ID of the checklist item to delete.', - }, - - // ---------------------------------- - // checklist:getCheckItem - // ---------------------------------- - { - displayName: 'Card ID', - name: 'cardId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'getCheckItem', - ], - resource: [ - 'checklist', - ], - }, - }, - description: 'The ID of the card that checklist belongs to.', - }, - { - displayName: 'CheckItem ID', - name: 'checkItemId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'getCheckItem', - ], - resource: [ - 'checklist', - ], - }, - }, - description: 'The ID of the checklist item to get.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'getCheckItem', - ], - resource: [ - 'checklist', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Fields', - name: 'fields', - type: 'string', - default: 'all', - description: 'Fields to return. Either "all" or a comma-separated list of fields.', - }, - ], - }, - - // ---------------------------------- - // checklist:updateCheckItem - // ---------------------------------- - { - displayName: 'Card ID', - name: 'cardId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'updateCheckItem', - ], - resource: [ - 'checklist', - ], - }, - }, - description: 'The ID of the card that checklist belongs to.', - }, - { - displayName: 'CheckItem ID', - name: 'checkItemId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'updateCheckItem', - ], - resource: [ - 'checklist', - ], - }, - }, - description: 'The ID of the checklist item to update.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'updateCheckItem', - ], - resource: [ - 'checklist', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Name', - name: 'name', - type: 'string', - default: '', - description: 'The new name for the checklist item.', - }, - { - displayName: 'State', - name: 'state', - type: 'options', - options: [ - { - name: 'complete', - value: 'complete' - }, - { - name: 'incomplete', - value: 'incomplete', - }, - ], - default: 'complete', - description: 'The resource to operate on.', - }, - { - displayName: 'Checklist ID', - name: 'checklistId', - type: 'string', - default: '', - description: 'The ID of the checklist this item is in', - }, - { - displayName: 'Position', - name: 'pos', - type: 'string', - default: '', - description: 'The position of the checklist on the card. One of: top, bottom, or a positive number.', - }, - ], - }, - - // ---------------------------------- - // checklist:completedCheckItems - // ---------------------------------- - { - displayName: 'Card ID', - name: 'cardId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'completedCheckItems', - ], - resource: [ - 'checklist', - ], - }, - }, - description: 'The ID of the card for checkItems.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'completedCheckItems', - ], - resource: [ - 'checklist', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Fields', - name: 'fields', - type: 'string', - default: 'all', - description: 'Fields to return. Either "all" or a comma-separated list of: "idCheckItem", "state".', - }, - ], - }, - - // ---------------------------------- - // label - // ---------------------------------- - { - displayName: 'Operation', - name: 'operation', - type: 'options', - displayOptions: { - show: { - resource: [ - 'label', - ], - }, - }, - options: [ - { - name: 'Add to Card', - value: 'addLabel', - description: 'Add a label to a card.', - }, - { - name: 'Create', - value: 'create', - description: 'Create a new label', - }, - { - name: 'Delete', - value: 'delete', - description: 'Delete a label', - }, - { - name: 'Get', - value: 'get', - description: 'Get the data of a label', - }, - { - name: 'Get All', - value: 'getAll', - description: 'Returns all label for the board', - }, - { - name: 'Remove From Card', - value: 'removeLabel', - description: 'Remove a label from a card.', - }, - { - name: 'Update', - value: 'update', - description: 'Update a label.', - } - - ], - default: 'getAll', - description: 'The operation to perform.', - }, - - // ---------------------------------- - // label:create - // ---------------------------------- - { - displayName: 'Board ID', - name: 'boardId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'label', - ], - }, - }, - description: 'The ID of the board to create the label on.', - }, - { - displayName: 'Name', - name: 'name', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'label', - ], - }, - }, - description: 'Name for the label.', - }, - { - displayName: 'Color', - name: 'color', - type: 'options', - required: true, - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'label', - ], - }, - }, - options: [ - { - name: 'black', - value: 'black', - }, - { - name: 'blue', - value: 'blue', - }, - { - name: 'green', - value: 'green' - }, - { - name: 'orange', - value: 'orange', - }, - { - name: 'lime', - value: 'lime', - }, - { - name: 'null', - value: 'null', - }, - { - name: 'pink', - value: 'pink', - }, - { - name: 'purple', - value: 'purple', - }, - { - name: 'red', - value: 'red', - }, - { - name: 'sky', - value: 'sky', - }, - { - name: 'yellow', - value: 'yellow' - }, - ], - default: 'null', - description: 'The color for the label.', - }, - - - // ---------------------------------- - // label:delete - // ---------------------------------- - { - displayName: 'Label ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'delete', - ], - resource: [ - 'label', - ], - }, - }, - description: 'The ID of the label to delete.', - }, - - // ---------------------------------- - // label:getAll - // ---------------------------------- - { - displayName: 'Board ID', - name: 'boardId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'label', - ], - }, - }, - description: 'The ID of the board to get label.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'label', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Fields', - name: 'fields', - type: 'string', - default: 'all', - description: 'Fields to return. Either "all" or a comma-separated list of fields.', - }, - ], - }, - - // ---------------------------------- - // label:get - // ---------------------------------- - { - displayName: 'Label ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'get', - ], - resource: [ - 'label', - ], - }, - }, - description: 'Get information about a label by ID.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'get', - ], - resource: [ - 'label', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Fields', - name: 'fields', - type: 'string', - default: 'all', - description: 'Fields to return. Either "all" or a comma-separated list of fields.', - }, - ], - }, - - // ---------------------------------- - // label:addLabel - // ---------------------------------- - { - displayName: 'Card ID', - name: 'cardId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'addLabel', - ], - resource: [ - 'label', - ], - }, - }, - description: 'The ID of the card to get label.', - }, - { - displayName: 'Label ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'addLabel', - ], - resource: [ - 'label', - ], - }, - }, - description: 'The ID of the label to add.', - }, - - // ---------------------------------- - // label:removeLabel - // ---------------------------------- - { - displayName: 'Card ID', - name: 'cardId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'removeLabel', - ], - resource: [ - 'label', - ], - }, - }, - description: 'The ID of the card to remove label from.', - }, - { - displayName: 'Label ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'removeLabel', - ], - resource: [ - 'label', - ], - }, - }, - description: 'The ID of the label to remove.', - }, - - // ---------------------------------- - // label:update - // ---------------------------------- - { - displayName: 'Label ID', - name: 'id', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - operation: [ - 'update', - ], - resource: [ - 'label', - ], - }, - }, - description: 'The ID of the label to update.', - }, - { - displayName: 'Update Fields', - name: 'updateFields', - type: 'collection', - placeholder: 'Add Field', - displayOptions: { - show: { - operation: [ - 'update', - ], - resource: [ - 'label', - ], - }, - }, - default: {}, - options: [ - { - displayName: 'Name', - name: 'name', - type: 'string', - default: '', - description: 'Name of the label.', - }, - { - displayName: 'Color', - name: 'color', - type: 'options', - options: [ - { - name: 'black', - value: 'black', - }, - { - name: 'blue', - value: 'blue', - }, - { - name: 'green', - value: 'green' - }, - { - name: 'orange', - value: 'orange', - }, - { - name: 'lime', - value: 'lime', - }, - { - name: 'null', - value: 'null', - }, - { - name: 'pink', - value: 'pink', - }, - { - name: 'purple', - value: 'purple', - }, - { - name: 'red', - value: 'red', - }, - { - name: 'sky', - value: 'sky', - }, - { - name: 'yellow', - value: 'yellow' - }, - ], - default: 'null', - description: 'The color for the label.', - }, - ], - }, ], }; From eab4df1afdd4e3c23aa7d3a0288910adbcb42199 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Fri, 24 Jan 2020 16:16:54 -0500 Subject: [PATCH 23/41] :sparkles: hunter node --- .../credentials/HunterApi.credentials.ts | 17 + .../nodes/Hunter/GenericFunctions.ts | 56 +++ .../nodes-base/nodes/Hunter/Hunter.node.ts | 335 ++++++++++++++++++ packages/nodes-base/nodes/Hunter/hunter.png | Bin 0 -> 43032 bytes packages/nodes-base/package.json | 2 + 5 files changed, 410 insertions(+) create mode 100644 packages/nodes-base/credentials/HunterApi.credentials.ts create mode 100644 packages/nodes-base/nodes/Hunter/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Hunter/Hunter.node.ts create mode 100644 packages/nodes-base/nodes/Hunter/hunter.png diff --git a/packages/nodes-base/credentials/HunterApi.credentials.ts b/packages/nodes-base/credentials/HunterApi.credentials.ts new file mode 100644 index 0000000000..802ff5159b --- /dev/null +++ b/packages/nodes-base/credentials/HunterApi.credentials.ts @@ -0,0 +1,17 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class HunterApi implements ICredentialType { + name = 'hunterApi'; + displayName = 'Hunter API'; + properties = [ + { + displayName: 'API Key', + name: 'apiKey', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Hunter/GenericFunctions.ts b/packages/nodes-base/nodes/Hunter/GenericFunctions.ts new file mode 100644 index 0000000000..02fafdd482 --- /dev/null +++ b/packages/nodes-base/nodes/Hunter/GenericFunctions.ts @@ -0,0 +1,56 @@ +import { OptionsWithUri } from 'request'; +import { + IExecuteFunctions, + IExecuteSingleFunctions, + IHookFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; +import { IDataObject } from 'n8n-workflow'; + +export async function hunterApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + const credentials = this.getCredentials('hunterApi'); + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + qs = Object.assign({ api_key: credentials.apiKey }, qs); + let options: OptionsWithUri = { + method, + qs, + body, + uri: uri ||`https://api.hunter.io/v2${resource}`, + json: true + }; + options = Object.assign({}, options, option); + if (Object.keys(options.body).length === 0) { + delete options.body; + } + try { + return await this.helpers.request!(options); + } catch (err) { + throw new Error(err); + } +} + +/** + * Make an API request to paginated flow endpoint + * and return all results + */ +export async function hunterApiRequestAllItems(this: IHookFunctions | IExecuteFunctions| ILoadOptionsFunctions, propertyName: string, method: string, resource: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + query.offset = 0; + query.limit = 100; + + do { + responseData = await hunterApiRequest.call(this, method, resource, body, query); + returnData.push.apply(returnData, responseData[propertyName]); + query.offset += query.limit; + } while ( + responseData.meta !== undefined && + responseData.meta.results !== undefined && + responseData.meta.offset <= responseData.meta.results + ); + return returnData; +} diff --git a/packages/nodes-base/nodes/Hunter/Hunter.node.ts b/packages/nodes-base/nodes/Hunter/Hunter.node.ts new file mode 100644 index 0000000000..b4df2e1595 --- /dev/null +++ b/packages/nodes-base/nodes/Hunter/Hunter.node.ts @@ -0,0 +1,335 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; +import { + IDataObject, + INodeTypeDescription, + INodeExecutionData, + INodeType, +} from 'n8n-workflow'; +import { + hunterApiRequest, + hunterApiRequestAllItems, +} from './GenericFunctions'; + +export class Hunter implements INodeType { + description: INodeTypeDescription = { + displayName: 'Hunter', + name: 'hunter', + icon: 'file:hunter.png', + group: ['output'], + version: 1, + subtitle: '={{$parameter["operation"]}}', + description: 'Consume Hunter API', + defaults: { + name: 'Hunter', + color: '#ff3807', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'hunterApi', + required: true, + } + ], + properties: [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + options: [ + { + name: ' Domain Search', + value: 'domainSearch', + description: 'Get every email address found on the internet using a given domain name, with sources.', + }, + { + name: ' Email Finder', + value: 'emailFinder', + description: 'Generates or retrieves the most likely email address from a domain name, a first name and a last name.', + }, + { + name: 'Email Verifier', + value: 'emailVerifier', + description: 'Allows you to verify the deliverability of an email address.', + }, + ], + default: 'domainSearch', + description: 'operation to consume.', + }, + { + displayName: 'Domain', + name: 'domain', + type: 'string', + displayOptions: { + show: { + operation: [ + 'domainSearch', + ], + returnAll: [ + false, + ], + }, + }, + default: '', + required: true, + description: 'Domain name from which you want to find the email addresses. For example, "stripe.com".', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'domainSearch', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'domainSearch', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + default: {}, + displayOptions: { + show: { + operation: [ + 'domainSearch', + ], + }, + }, + options: [ + { + displayName: 'Type', + name: 'type', + type: 'options', + default: '', + options: [ + { + name: 'Personal', + value: 'personal', + }, + { + name: 'Generic', + value: 'generic', + }, + ] + }, + { + displayName: 'Seniority', + name: 'seniority', + type: 'multiOptions', + default: [], + options: [ + { + name: 'Junior', + value: 'junior', + }, + { + name: 'Senior', + value: 'senior', + }, + { + name: 'Executive', + value: 'executive', + }, + ] + }, + { + displayName: 'Department', + name: 'department', + type: 'multiOptions', + default: [], + options: [ + { + name: 'Executive', + value: 'executive', + }, + { + name: 'IT', + value: 'it', + }, + { + name: 'Finance', + value: 'finance', + }, + { + name: 'Management', + value: 'management', + }, + { + name: 'Sales', + value: 'sales', + }, + { + name: 'Legal', + value: 'legal', + }, + { + name: 'Support', + value: 'support', + }, + { + name: 'HR', + value: 'hr', + }, + { + name: 'Marketing', + value: 'marketing', + }, + { + name: 'Communication', + value: 'communication', + }, + ] + }, + ], + }, + { + displayName: 'Domain', + name: 'domain', + type: 'string', + default: '', + displayOptions: { + show: { + operation: [ + 'emailFinder' + ], + }, + }, + required: true, + description: 'Domain name from which you want to find the email addresses. For example, "stripe.com".', + }, + { + displayName: 'First Name', + name: 'firstname', + type: 'string', + displayOptions: { + show: { + operation: [ + 'emailFinder' + ], + }, + }, + default: '', + required: true, + description: `The person's first name. It doesn't need to be in lowercase..`, + }, + { + displayName: 'Last Name', + name: 'lastname', + type: 'string', + displayOptions: { + show: { + operation: [ + 'emailFinder' + ], + }, + }, + default: '', + required: true, + description: `The person's last name. It doesn't need to be in lowercase..`, + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + displayOptions: { + show: { + operation: [ + 'emailVerifier' + ], + }, + }, + default: '', + required: true, + description: 'The email address you want to verify', + }, + ], + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + const length = items.length as unknown as number; + const qs: IDataObject = {}; + let responseData; + for (let i = 0; i < length; i++) { + const operation = this.getNodeParameter('operation', 0) as string; + //https://hunter.io/api-documentation/v2#domain-search + if (operation === 'domainSearch') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const filters = this.getNodeParameter('filters', i) as IDataObject; + const domain = this.getNodeParameter('domain', i) as string; + qs.domain = domain; + if (filters.type){ + qs.type = filters.type; + } + if (filters.seniority){ + qs.seniority = (filters.seniority as string[]).join(','); + } + if (filters.department){ + qs.department = (filters.department as string[]).join(','); + } + if (returnAll) { + responseData = await hunterApiRequestAllItems.call(this, 'data', 'GET', '/domain-search', {}, qs); + } else { + const limit = this.getNodeParameter('limit', i) as number; + qs.limit = limit; + responseData = await hunterApiRequest.call(this, 'GET', '/domain-search', {}, qs); + responseData = responseData.data; + } + } + //https://hunter.io/api-documentation/v2#email-finder + if (operation === 'emailFinder') { + const domain = this.getNodeParameter('domain', i) as string; + const firstname = this.getNodeParameter('firstname', i) as string; + const lastname = this.getNodeParameter('lastname', i) as string; + qs.first_name = firstname; + qs.last_name = lastname; + qs.domain = domain; + responseData = await hunterApiRequest.call(this, 'GET', '/email-finder', {}, qs); + responseData = responseData.data; + } + //https://hunter.io/api-documentation/v2#email-verifier + if (operation === 'emailVerifier') { + const email = this.getNodeParameter('email', i) as string; + qs.email = email; + responseData = await hunterApiRequest.call(this, 'GET', '/email-verifier', {}, qs); + responseData = responseData.data; + } + 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/Hunter/hunter.png b/packages/nodes-base/nodes/Hunter/hunter.png new file mode 100644 index 0000000000000000000000000000000000000000..fb214f80ee2efed6e42179b68c30ef3516328fc2 GIT binary patch literal 43032 zcmeHQ2|UyP|DU7CeI%l^6rE!mhGDKbYwnxW%&?kmwz-Rngi1P9t|*igIXXmg6uR$J z3W-RUZYBPo4bdn4zF+jOA3q+CH1E&*^?F~=*ZF?$^NzMOHx}fR=7T^Wf~F=|Yv6w* z>mToA;J2(!)Nu$zz>H*IU}A|e`9rFHppf!mjOISDcpvi72i3?`esy((9K}1_Q#okO7 zkX^1woHw_UbH2g1`sNRUYY%-nLRok$EVjo1A;R0d8WP7Hl|P9dI=hkwa_-V+YbQ|% zR_D;kB&WG{jWr@NH7nMNEU{B}2)|^j?h=0F4yq=4MpOWoZbnC7o-2gulA3IH`Pv4p zqLWbnc-~4e$opy$o_B^?9F;djwk}+pc&z-okUBA}Y{yr%t1s~8vPuC`tLRqz8m^1u zr@is~meTyW-r8}$)Z0dg3~ufwbK7QtcK=0T!JGMRL0|3is5);z=IwAfuInR=Fq2r2 zW~1`)kjC<>>r}axMm?HT{6;G3%Iw&kQ!CG}{BU36iHTw*ZNq-~CvucL|5swET5_hB zoaQAYOX)-~JZ7Bmr-)l*d_0n;lU{9sU)4#Cj;Nd6?U0`KR0Vfgs?ae}n&-G;<&+J?=P#1*eG*Cbh{4)*`HS%I%3I;3L&+;+7bC4!DJ|M5+`I`U8hzT=+0|yQS?t}n zFIwK+?nuzBTevgVCNePM?fcqPSMKsRytr4j#MHTl2}&6|r)`j5`Rr`mw7K3deY0|* zX6Yx_)w;SR9!%QABUv`-aAQ@|LBlCE`_wyLXg&VSjnfL85;bXth>)W$hYM3id~=1UmBGM=p*Q zt)#iDAhx$|PG~F(gv#=#q~-=a2|d+gS&;H_%2wW_h>a2L-13bdRXXJEJ9Orr_-KhNq8bWX`KyZA`vQAnMR_l1}U`K)iE zw2UQQHI=8+e8R)P(DjLrn&7^qww12nrZhl+>m$bc_My@o7I)gDCRuLb?RQqvhxnrF{)_S4^YU$>*3U$3v#(noSV|pG-b6ely{Qo z3au!f8YtJ&4R+JYJ}l-^E}M}nvNPN)S1g-Lb%k;*pa1$z#lKR$Elhe#%W3YQs|I2*=4jo%B}ryruNY2&y$Ux+UeKg_)0U2#^$2gnW=VJs0zbdYa~Ea3Q9dw`R7czF!(Fx<;d9UFi1Ely3X@ zR88mjBa5yl<+vPqm>pvHX|Jr|HMgxt)$DR?bIhovkL63;*2J6*#~7z%>R6Q8_r!JM zzJ+o{uG^?rte0Ab3|}_+nLx7Cc7d4!a|9+u5+hv%Eu{Ss@JLvAMT7D*h8sWd#(qEM4wYJxSl^IvrN-i)Tndo_RU$d{BCV& zx!}LZe-$$XzE1<~|32*o>_Ezv<1#xmJn!sLCSXp7oRK_5Wt2AITM>^5+l&*c_$?$Y z&Uzhvrm9rraPZ#h9Q&MSgk@EHRTSxvO9_qIWnW!h`@i0bIg3H2D5S_P*Q{S$kFHM$ zH42>)da3K`7xAu&u4i9oeGHRq5VxF~tJILAd-7b@xn=7tE@;Gx$38?ITU4zOC0QVE zGW{~&RlaVF<8=3#<@|?*gQQlj~wXBU0vx%~M#Ko%DJB zjXA#NPxj6=x>;~=nQ6|BitP?_>rH*ltPU1rK2J9{_c2+sV_NbFG;S|OVGZTW27!D5 zU!zqD3zStEA)Y-4qfFZs{472uyJ9*oOs!fVZ*vu=n&1roP_0rzDkgo+=XD}Eoyo(z zc@p%YxSLGzU*w|}NpBY1Jyj)!yjMD3!3&(HAP z@;S(j`M9N6t+S(3^DPhmO}>v)xAW)o37ZPYcW$@21avyUTNKN zzWT%^(axzstG0=1rnTo%@)@EmQYpmk>*|M{#CK;Tv9Q|HaYgX3#+*%cl1JU%uk?NJlXS7nOGUG@ML`Z&*^f1id>L_9*wg&4&F?R;Py$?V2|dyNJ(9-+eyz zq`6K zc8T^~caGmVD)R!BUH2x{$d*xp%J#QN3VL+2NKIyvOb(gv(d~l!?u_P^PxYHhOAF;! zE@6}ythr9YqbIKsk;Iy*uv%{0?BO{l!oUvC)+Y)mj z`O%Jm<=u#`s=Ep&GhiWmLKc2nUlLOi*0uA)tu1R1Umtw>`jU!X5w`yov}uK*@=dKH zr*jN%r;)FCztmFcwhVDu@vtn#xTm-&_e0}@ITwuWXQa!*G|_?C?;U%5+d3YLpPpCr zAhWPuV{ho&2Zy6G(dJc&n6FEoD$To=du@iM4D;ig(`y+UuYOIUo^D{IcBXwgA6^>V z6P#O-d7yLq>-4*qy5k?BtNdh3#OTeHcitQ+b!~DxL(_g0{A|lknUfUrvxkf2Pkngm zU-t4@sYDNo=2f<={83EEmrq%*66$k$;@_2ioWEwLv229Ssn77X#w5kid$;PFOT$_f zL$G1CA8McbR)}Alw=qO6M6n0aw)Iu!%}lP6<}m3lr%#vL?yoD=?&Mk^ z&gaEvCceiz%q`*tf{Q6M6DI})qBw{3k0UT$ z#~%WTJ4CW|WIE!Oq6t(VMZ5>qov0Y-Lj!6sGpWYW+|N&x`@ ziUG=sRJxZELPJAC364}kB4I!c7$bo1o04oVB@(}AsB9r|XOgf%Pr4P+*P=#Nvrw8FXEzOTkW<$+` zphP4Sfwq|pfDSR_;A!4eCY9k${RM=+KmKLIv_UZcVGV%?0Fm;LtujfTzkm?yNpD+h znd*hQ7O+HU9Myy58HB|%i5Mgtp#g)VU`Q2Pgc@2|4UK>=f+NvzxRw&A=D>8bc^J@% z0DV6X6bu1}ArQ6*RWwo!jYPrV%HMemstMwO15|+ro{8`0@*jILxGsnfy9FxUmP#dK z^jMk*YB~S}D3A>SMyU#&kHZs46xI?g=pEc(vL89(aA+)%PVy&u7}2RdY^_1V(-}mT zFlx*7OXGJn(>JwjaYoaL6b}+h*%*-V3Es~f2IcYX-^fo9n2_T%D!unjyk{ds(Vxg)3&5GWXdfKZ32AXQah8lIj6n7fAt z3a^4!L%_=1HjsZ!~j%F(4aP1i^ei4HbmbaOZy*?N&gZ)-$4YZax{bL z$qc~LiF#fD4E`;q*;W8VIxvJ@{;Y2G)+2c1DPBYmj1p_+S#9J$j9KeP5=?7foyXJoP0p8V(z^t%>6yALc^n&r<7I7_{8GT_1icJaGbN4u*ltHSX> zkn64rC%_QODoB{R2Hq2WQ7!%-e{|jh+XnaA_FR! zO2-3n=#S$aVErp^^v4>*bV~<_**y;;lF8Hn3|tY;{^=WXU+EBC2a)?wJ%|_soYRs% zLiXSPOy*9;6IOv6?JfLgVg?&3?B&xZ1L05qIt?oSXC{OoJO$hVcG*9ZvJ4@Rp8`|& z7ymN@CJQK*))oeaHa30p#xD9LO{b@M-G;!lZH~ciNu>uG^A3PmM82?pJfHd3$B>t}J zU{9n2Zr`Ys#82Wd;XsUCSJHrlr%b@1-w|L3UH{9GgZGj!hb{p=jyM8EsQ#FP znE(enGo=oimOlfI-+>J{01W^XPTK0K05V1n3{YQ&i3_lBjT0-PF{WXm&Q=(I6Bgmby@7;eusO{+_;29iPA$+h#;r^>3RIWstrdC;9?x9mkHkU^$-O-OiCa z7ocSwrgJeqb;TGqB1o3g`8$jV6121vzyMO4V;C5KXYqT00rn3jfHAg#q1JCmM(cK} zO_&mP_MCM-%NX6~Nc<&@@mq3RK-z+p+xi}nj3T!M_P&RyG{D0dFU}`OV*ZTtsSL90 zBg%UMc`cl_GTZ-TWigaT?tp?l@L@s&JeqL=4X`r(Gtl@Q$oOtgfDfa|Vt{90n79B9 zWt_l5<*#TA&|Hp4VE`G=K4YRXI$VGgm&3%xI5j1pB^pEOf{hDSdTVq^Pc_gC3>Ow4 z!5Sy}0yHUO2NrDKlbsD89V|xac!Go7aRLhv*vAemSTQQFsmf9pqq8DE#bv|P7vSy3 zaRLnFUqJ@A)e$)uz0hF&cG5<&z=2d9`B4n;*Aa$^j-SvqPT(BYwA?8zvj zk_ljZKVZtvlKthcj~QsQ)8fAa7+_CfLSwK`Z-KXs|BR~v4)R81P=G!6{-b@s$59>a z1JA+)IN0C-%gmnv2grzx2pd3FydN8Y1{qD73k=l>Xt1FH-hLiC(D)vp09)~+0R{-B z!^FinjZaiSvodyI0fZztR$Stwtui`XfPn!%3TcGGi2{xxi;`h6#;vYEHlh6aEY|z+yTw zXnZ#(z%h+cc^ae4bd6JU0zTvRXP^OIixEMC9i9MAM&I|FmH%3Nf&w`z%&0t5HF-0M9F@;Y459D zC*F7ZeIY>~dgrTGgnrWXBa$qZ8_&|YER73r9PuF+P-1MDVg;feCkhudsy}0k)io4> z>yK2vFXgCFp)mo9zPmie?i|9zlPp8&;}2L+0)Db#)+C`RFyfmG#$`S@F} zzEov{V!#s^wfR6zXo@kfDZoL~s6jD`*nUD&uw#NBz84OflRx8VD*tXra}@iIpb3~T zAAieyAb+=`Ig0OsPk`dTL{lJsx1))i@F~W;Pw~6`%u(z>PbiB25=Ei%o3j)n-(DOi zkoYg&B+zGFp#t1j_CK{z@KetI&$#L!G>&oNN~SG_J7)4d4`5 z|Hl2_LI(IW-8caT_{%qA3m5D+z5%aT_zDAQNEC6oI(R+ z?#GV8VEGq)w_LE!Ba9puKMBV$GlJkFmg7Wcuz;Zq%uU}Zk3R>DL5+^c`2@b4@>_5L zJ`p@-Ve$VF*bT*#jTF{Z+crbrsLk4jU_Ca$GwewWk~^7b3tU3YpyBC63Lyw%geNnI z-+BC^(!jZ}er}dzJd?<}k{NWF^*>$SjWGc3Vg9G1Z5Ukd-CO@y&xw7R^*>hAXWc$M zbgkafA?|!r&W?MbEk;F%Z_%Pc6-C|+7h1BE}o+5p)=@n9-o%s^8f zegq;Nqvt_&CqgZ;Mo==zosOpmLD{#J0Zazh8br+`d3vHP=p@!f=}Z!pf&tEn{#4di zzjq$6L{sXk^8D7v{F2xbbigWZHoIH1qzCZd2Q9YJywZ}~uL@nAw7KO_ z;1*x0%hD<0DmvX_0^ZvA87s~U2wuKs95(xOOXxO*8y(Z1B`r8?SNLWjq$@*d&ck;Z z@}Dg0?r)%IhpeMQbRR!Ucrd-IERM%cs>_gTX>zoSq;!|~ozJUU-?q&t6uVa0@h(={ z$Ev%A_rUEnmCCo{8@EJn)jzSEXNSuQ9mTJDaE=HOWK5;kW{JIUl}OdQ$M$dXOuBE( zv&Xp!TOu|$TqEmf1dcp<~WvaVa?rpp|Ndbr3! zsNkK=^XxuKPVty$%@gxis?RWP@_4W+F_G#dgVYtJZkK;7H6 z))e}|=LjCHJ+OA~Ma}Zn5#~avk{^Wyt5asbuTW^(uh3154nK1_xvb*JCie?B5?7`) ztu-NwLh__C3b^ovGLT%JXK&mtANn#4X)3}w|Lw#1SPb{k3-ftk+?x+dZR6CvXI7xu zdDf!6{!c7%hPMC@irb!hu2jvalYL+=RL;seqHAL zXN9RfoX=q#jRvj@;f{y7XtLNq8`urX*`5oNIa00iq`5ma2=und1JM>tlzeKmtjI=e zt-H;klHi!m!y4lGaiMQ~ZYjShDZg`_sip#*a=WTU&ZL>6on8$?tkvH94MW8$TidCj zkm=SO3U*KB=H8rXpPK0Uo`Y^#X6F3?YnCVabdmLuB0Y%x#pTi&qOYgr_gtQ0j&j2l z-c#Q4S!2iZD`|;69GulnpJAd1KK1fL#&VF1QoT6gy(QnmcKS$Mzgdv_?&!sw;vFd~LL-r7w^|%B z>yu7#@9PwOe$@E>yVSj$dZwY#c$m#J7agv37^-#nym`ms>+xH5%V`)%;Q~X-onimH;3>~7Vlf@9I;GpT0T z=|TRp5|1V4m5DxmoMIK}R#C9{PHjrVx0NEhOsse9jZT7cx2RZ0&odEIRp82~Oq|PI zb?|yboQTL06k@tC(Q*c65-LJYT6V2s^Od0DuUyM=x9$tie9UYsb`;&${E(wTI%3~Z zX6&au@skx>Lsrj@dAYvG;Xsi@OWHk&6eRyULhxjSdsWmLg*XvKXr-go1KX)F=eBNo zBNbM(2qpY-z2MhwPKMnJ*(~9!91klL(}Njui+F?Mklj~2d(I(5cxPt6niXqbBz>AY zVzX_1`IUE97h6M|I`?}W$=DF%yl2M-T@LQfZ_}euO{wT}8s2M`gx4yxdr|AJUFj|( z6ssM+r|SH!u4w*Jv!$1AQ!i9MJ&bGQ5HBZh%Uc_Fc6An|vnCJe%-@mLzDJL~QOP*f zVulfFcD}~tLy%Y}lAsT*;Ewx@AUQiKCQ}@?ec|k^FWsSxhNF#EP|qhmMrsR}p$Lah2-#aJ7;x zT*NxZr*W(AdIbxsDO2}{YDOjYkOi|gtlpK)Qz{2dz4a+rtKi@?KMNiv7jd$>lpVZc zZ-w|SegpNz`?bo59^R9>6jsc}2J7B$5m~Ps3nH=Qe9N9rj#fhRc)5rINSTM zsHV=O*V#{6T?Du#i81O;!n)2?zKL#z>yqy+F*@b_E-TsH#C}Pa6h-b<P$W=4^=-dr^M+l}Mv%}sYS!f2=Vf7^rA-ewXcTdBpc<_-88xHW>B+X96+mI}tGmM9$6Jf!|v8f4sY>i zzC7|JJvNDrvu?`X0twA+d=Y(K8O<3m)G-7L>bo8F5t^P!L{pOo3Nsb-7Gwfsc&&gdWS-OeKs6Yyn z9il$1Q13eNqtJ_{u5agRciwNq@nKIaDBf{?+o_q99^Bf~2KhM~!(QdO%1F3EH+M{P zJKI@y$dor+5h{IosJfR$&))JyNIbCc!rx$|!Z&#*e|&MSk6&G<$X3 zx@&&bRrWd4kaZSeN0)g9TLo3j%aPiG6|;nhE#~XqnD)A8A9by7p~0d6-(@dQQ`hF) zQu>1SARp7^ed)AH?9uJL{PW+IM5n_`iIZ|#vNd71vMRScb`=xy6fBwl@jcUl^ktWh jLN^qz$&LEd)y@CuM&=jVM@w%A@MCIdjy Date: Fri, 24 Jan 2020 17:01:11 -0800 Subject: [PATCH 24/41] :twisted_rightwards_arrows: Small improvements on Hunter-Node --- .../nodes/Hunter/GenericFunctions.ts | 2 +- .../nodes-base/nodes/Hunter/Hunter.node.ts | 65 +++++++++++++++---- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/packages/nodes-base/nodes/Hunter/GenericFunctions.ts b/packages/nodes-base/nodes/Hunter/GenericFunctions.ts index 02fafdd482..e566db93e8 100644 --- a/packages/nodes-base/nodes/Hunter/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Hunter/GenericFunctions.ts @@ -45,7 +45,7 @@ export async function hunterApiRequestAllItems(this: IHookFunctions | IExecuteFu do { responseData = await hunterApiRequest.call(this, method, resource, body, query); - returnData.push.apply(returnData, responseData[propertyName]); + returnData.push(responseData[propertyName]); query.offset += query.limit; } while ( responseData.meta !== undefined && diff --git a/packages/nodes-base/nodes/Hunter/Hunter.node.ts b/packages/nodes-base/nodes/Hunter/Hunter.node.ts index b4df2e1595..c27da7a1d4 100644 --- a/packages/nodes-base/nodes/Hunter/Hunter.node.ts +++ b/packages/nodes-base/nodes/Hunter/Hunter.node.ts @@ -67,15 +67,26 @@ export class Hunter implements INodeType { operation: [ 'domainSearch', ], - returnAll: [ - false, - ], }, }, default: '', required: true, description: 'Domain name from which you want to find the email addresses. For example, "stripe.com".', }, + { + displayName: 'Only Emails', + name: 'onlyEmails', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'domainSearch', + ], + }, + }, + default: true, + description: 'Return only the the found emails.', + }, { displayName: 'Return All', name: 'returnAll', @@ -108,7 +119,7 @@ export class Hunter implements INodeType { minValue: 1, maxValue: 100, }, - default: 50, + default: 100, description: 'How many results to return.', }, { @@ -219,7 +230,7 @@ export class Hunter implements INodeType { displayOptions: { show: { operation: [ - 'emailFinder' + 'emailFinder', ], }, }, @@ -233,13 +244,13 @@ export class Hunter implements INodeType { displayOptions: { show: { operation: [ - 'emailFinder' + 'emailFinder', ], }, }, default: '', required: true, - description: `The person's first name. It doesn't need to be in lowercase..`, + description: `The person's first name. It doesn't need to be in lowercase.`, }, { displayName: 'Last Name', @@ -248,13 +259,13 @@ export class Hunter implements INodeType { displayOptions: { show: { operation: [ - 'emailFinder' + 'emailFinder', ], }, }, default: '', required: true, - description: `The person's last name. It doesn't need to be in lowercase..`, + description: `The person's last name. It doesn't need to be in lowercase.`, }, { displayName: 'Email', @@ -263,13 +274,13 @@ export class Hunter implements INodeType { displayOptions: { show: { operation: [ - 'emailVerifier' + 'emailVerifier', ], }, }, default: '', required: true, - description: 'The email address you want to verify', + description: 'The email address you want to verify.', }, ], }; @@ -287,6 +298,8 @@ export class Hunter implements INodeType { const returnAll = this.getNodeParameter('returnAll', i) as boolean; const filters = this.getNodeParameter('filters', i) as IDataObject; const domain = this.getNodeParameter('domain', i) as string; + const onlyEmails = this.getNodeParameter('onlyEmails', i, false) as boolean; + qs.domain = domain; if (filters.type){ qs.type = filters.type; @@ -299,12 +312,42 @@ export class Hunter implements INodeType { } if (returnAll) { responseData = await hunterApiRequestAllItems.call(this, 'data', 'GET', '/domain-search', {}, qs); + + // Make sure that the company information is there only once and + // the emails are combined underneath it. + if (onlyEmails === false) { + let tempReturnData: IDataObject = {}; + + for (let i = 0; i < responseData.length; i++) { + if (i === 0) { + tempReturnData = responseData[i]; + continue; + } + ((tempReturnData as IDataObject).emails as IDataObject[]).push.apply(tempReturnData.emails, responseData[i].emails); + } + + responseData = tempReturnData; + } } else { const limit = this.getNodeParameter('limit', i) as number; qs.limit = limit; responseData = await hunterApiRequest.call(this, 'GET', '/domain-search', {}, qs); responseData = responseData.data; } + + if (onlyEmails === true) { + let tempReturnData: IDataObject[] = []; + + if (Array.isArray(responseData)) { + for (const data of responseData) { + tempReturnData.push.apply(tempReturnData, data.emails); + } + } else { + tempReturnData = responseData.emails; + } + + responseData = tempReturnData; + } } //https://hunter.io/api-documentation/v2#email-finder if (operation === 'emailFinder') { From 7ae06b2c7db705fe03be1fe438a3941e06c4542c Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 24 Jan 2020 22:12:30 -0800 Subject: [PATCH 25/41] :bookmark: Release n8n-workflow@0.21.0 --- packages/workflow/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/workflow/package.json b/packages/workflow/package.json index af2733ba3d..f00bfe3b69 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "n8n-workflow", - "version": "0.20.0", + "version": "0.21.0", "description": "Workflow base code of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 005bdbdf414ddf6730741c7bb61bbbc99fa265da Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 24 Jan 2020 22:13:44 -0800 Subject: [PATCH 26/41] :arrow_up: Set n8n-workflow@0.21.0 on n8n-core --- packages/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/package.json b/packages/core/package.json index 8f69797a5e..1d915083ab 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -43,7 +43,7 @@ "crypto-js": "^3.1.9-1", "lodash.get": "^4.4.2", "mmmagic": "^0.5.2", - "n8n-workflow": "~0.20.0", + "n8n-workflow": "~0.21.0", "p-cancelable": "^2.0.0", "request-promise-native": "^1.0.7" }, From a6c5792bc5611cff7229be8119ce2e373a597b2d Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 24 Jan 2020 22:14:18 -0800 Subject: [PATCH 27/41] :bookmark: Release n8n-core@0.23.0 --- packages/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/package.json b/packages/core/package.json index 1d915083ab..bd6ea3e099 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "0.22.0", + "version": "0.23.0", "description": "Core functionality of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 5f8d55bbc62e46200915801bbb6d7e45719031b5 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 24 Jan 2020 22:15:50 -0800 Subject: [PATCH 28/41] :arrow_up: Set n8n-core@0.23.0 and n8n-workflow@0.21.0 on n8n-nodes-base --- packages/nodes-base/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index f7d13755c6..4177e3a6c1 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -216,7 +216,7 @@ "@types/xml2js": "^0.4.3", "gulp": "^4.0.0", "jest": "^24.9.0", - "n8n-workflow": "~0.20.0", + "n8n-workflow": "~0.21.0", "ts-jest": "^24.0.2", "tslint": "^5.17.0", "typescript": "~3.7.4" @@ -237,7 +237,7 @@ "lodash.unset": "^4.5.2", "mongodb": "^3.3.2", "mysql2": "^2.0.1", - "n8n-core": "~0.22.0", + "n8n-core": "~0.23.0", "nodemailer": "^5.1.1", "pdf-parse": "^1.1.1", "pg-promise": "^9.0.3", From b4873876adfa05bf297b9051e5e666db1d874f17 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 24 Jan 2020 22:16:13 -0800 Subject: [PATCH 29/41] :bookmark: Release n8n-nodes-base@0.45.0 --- packages/nodes-base/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 4177e3a6c1..0e28954f98 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.44.0", + "version": "0.45.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From eae8c8137c0aaf07552f0c89ac5905e740573171 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 24 Jan 2020 22:18:10 -0800 Subject: [PATCH 30/41] :arrow_up: Set n8n-workflow@0.21.0 on n8n-editor-ui --- packages/editor-ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index a73819aa91..53546e847a 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -63,7 +63,7 @@ "lodash.debounce": "^4.0.8", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", - "n8n-workflow": "~0.20.0", + "n8n-workflow": "~0.21.0", "node-sass": "^4.12.0", "prismjs": "^1.17.1", "quill": "^2.0.0-dev.3", From 3954941c396e9a6f6781d57b7d0a29b0f95b17ce Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 24 Jan 2020 22:18:35 -0800 Subject: [PATCH 31/41] :bookmark: Release n8n-editor-ui@0.34.0 --- packages/editor-ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 53546e847a..9d6ab56eba 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "0.33.0", + "version": "0.34.0", "description": "Workflow Editor UI for n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From ccddd0d9fe67ffb6c834245d1b835e68c9b7372a Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 24 Jan 2020 22:20:58 -0800 Subject: [PATCH 32/41] :arrow_up: Set n8n-core@0.23.0, n8n-editor-ui@0.34.0, n8n-nodes-base@0.45.0 and n8n-workflow@0.21.0 on n8n --- packages/cli/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index ab390eab24..4dac22e2d6 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -93,10 +93,10 @@ "localtunnel": "^2.0.0", "lodash.get": "^4.4.2", "mongodb": "^3.2.3", - "n8n-core": "~0.22.0", - "n8n-editor-ui": "~0.33.0", - "n8n-nodes-base": "~0.44.0", - "n8n-workflow": "~0.20.0", + "n8n-core": "~0.23.0", + "n8n-editor-ui": "~0.34.0", + "n8n-nodes-base": "~0.45.0", + "n8n-workflow": "~0.21.0", "open": "^7.0.0", "pg": "^7.11.0", "request-promise-native": "^1.0.7", From 4ddeee7d0a8a194e9db79e3236075db66b372392 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 24 Jan 2020 22:21:41 -0800 Subject: [PATCH 33/41] :bookmark: Release n8n@0.50.0 --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 4dac22e2d6..20227a280f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.49.0", + "version": "0.50.0", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 5a2f2ee8d1212bb5e5393a077fa0ecb5632996cb Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 25 Jan 2020 13:21:59 -0800 Subject: [PATCH 34/41] :bug: Fix bug with external modules on Function-Nodes --- packages/nodes-base/nodes/Function.node.ts | 2 +- packages/nodes-base/nodes/FunctionItem.node.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nodes-base/nodes/Function.node.ts b/packages/nodes-base/nodes/Function.node.ts index 7892a0a5c0..7f5ea4c1ea 100644 --- a/packages/nodes-base/nodes/Function.node.ts +++ b/packages/nodes-base/nodes/Function.node.ts @@ -84,7 +84,7 @@ export class Function implements INodeType { try { // Execute the function code - items = (await vm.run(`module.exports = async function() {${functionCode}}()`, './')); + items = (await vm.run(`module.exports = async function() {${functionCode}}()`, __dirname)); } catch (e) { return Promise.reject(e); } diff --git a/packages/nodes-base/nodes/FunctionItem.node.ts b/packages/nodes-base/nodes/FunctionItem.node.ts index 01a134fdc8..6352fc3f98 100644 --- a/packages/nodes-base/nodes/FunctionItem.node.ts +++ b/packages/nodes-base/nodes/FunctionItem.node.ts @@ -90,7 +90,7 @@ export class FunctionItem implements INodeType { let jsonData: IDataObject; try { // Execute the function code - jsonData = await vm.run(`module.exports = async function() {${functionCode}}()`, './'); + jsonData = await vm.run(`module.exports = async function() {${functionCode}}()`, __dirname); } catch (e) { return Promise.reject(e); } From f4b851b495d98d1de038fd929ba4d29879c1555b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 25 Jan 2020 23:54:20 -0800 Subject: [PATCH 35/41] :bookmark: Release n8n-nodes-base@0.45.1 --- packages/nodes-base/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 0e28954f98..bcaa3b2d95 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.45.0", + "version": "0.45.1", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 5138a6b73d7216c1a1909c0ac4d34b81ddb2e376 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 25 Jan 2020 23:56:35 -0800 Subject: [PATCH 36/41] :arrow_up: Set n8n-nodes-base@0.45.1 on n8n --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 20227a280f..29f7de1b6c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -95,7 +95,7 @@ "mongodb": "^3.2.3", "n8n-core": "~0.23.0", "n8n-editor-ui": "~0.34.0", - "n8n-nodes-base": "~0.45.0", + "n8n-nodes-base": "~0.45.1", "n8n-workflow": "~0.21.0", "open": "^7.0.0", "pg": "^7.11.0", From 94d4042450768b1c951ee4958a5057df071d59ea Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 25 Jan 2020 23:57:12 -0800 Subject: [PATCH 37/41] :bookmark: Release n8n@0.50.1 --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 29f7de1b6c..bd01ed6b1b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.50.0", + "version": "0.50.1", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 6336adec8b909b369270a71722b37c550298f24f Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sun, 26 Jan 2020 00:17:48 -0800 Subject: [PATCH 38/41] :books: Update node count in README.md files --- README.md | 2 +- docker/images/n8n/README.md | 2 +- packages/cli/README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 70364ce8e4..fb2a62703e 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ received or lost a star. ## Available integrations -n8n has 80+ different nodes to automate workflows. The list can be found on: [https://n8n.io/nodes](https://n8n.io/nodes) +n8n has 100+ different nodes to automate workflows. The list can be found on: [https://n8n.io/nodes](https://n8n.io/nodes) ## Documentation diff --git a/docker/images/n8n/README.md b/docker/images/n8n/README.md index 90dbfe436b..16d12e226d 100644 --- a/docker/images/n8n/README.md +++ b/docker/images/n8n/README.md @@ -34,7 +34,7 @@ Slack notification every time a Github repository received or lost a star. ## Available integrations -n8n has 50+ different nodes to automate workflows. The list can be found on: [https://n8n.io/nodes](https://n8n.io/nodes) +n8n has 100+ different nodes to automate workflows. The list can be found on: [https://n8n.io/nodes](https://n8n.io/nodes) ## Documentation diff --git a/packages/cli/README.md b/packages/cli/README.md index 7495cecc23..e65df065d6 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -32,7 +32,7 @@ Slack notification every time a Github repository received or lost a star. ## Available integrations -n8n has 80+ different nodes to automate workflows. The list can be found on: [https://n8n.io/nodes](https://n8n.io/nodes) +n8n has 100+ different nodes to automate workflows. The list can be found on: [https://n8n.io/nodes](https://n8n.io/nodes) ## Documentation From 1d9685ea4cc8cb0d0f1c6592d720be5c9cf7492d Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sun, 26 Jan 2020 17:46:10 -0500 Subject: [PATCH 39/41] :zap: added forderless list support --- .../nodes-base/nodes/ClickUp/ClickUp.node.ts | 16 ++++++ .../nodes/ClickUp/TaskDescription.ts | 51 ++++++++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts b/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts index 20ee32b65d..38032890b1 100644 --- a/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts +++ b/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts @@ -125,6 +125,22 @@ export class ClickUp implements INodeType { } return returnData; }, + // Get all the available lists without a folder to display them to user so that he can + // select them easily + async getFolderlessLists(this: ILoadOptionsFunctions): Promise { + const spaceId = this.getCurrentNodeParameter('space') as string; + const returnData: INodePropertyOptions[] = []; + const { lists } = await clickupApiRequest.call(this, 'GET', `/space/${spaceId}/list`); + for (const list of lists) { + const listName = list.name; + const listId = list.id; + returnData.push({ + name: listName, + value: listId, + }); + } + return returnData; + }, // Get all the available assignees to display them to user so that he can // select them easily async getAssignees(this: ILoadOptionsFunctions): Promise { diff --git a/packages/nodes-base/nodes/ClickUp/TaskDescription.ts b/packages/nodes-base/nodes/ClickUp/TaskDescription.ts index b3883040c3..123244e8a4 100644 --- a/packages/nodes-base/nodes/ClickUp/TaskDescription.ts +++ b/packages/nodes-base/nodes/ClickUp/TaskDescription.ts @@ -87,6 +87,23 @@ export const taskFields = [ }, required: true, }, + { + displayName: 'Forderless List', + name: 'fonderlessList', + type: 'boolean', + default: false, + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + }, { displayName: 'Folder', name: 'folder', @@ -100,13 +117,16 @@ export const taskFields = [ operation: [ 'create', ], + fonderlessList: [ + false, + ], }, }, typeOptions: { loadOptionsMethod: 'getFolders', loadOptionsDependsOn: [ 'space', - ] + ], }, required: true, }, @@ -123,6 +143,35 @@ export const taskFields = [ operation: [ 'create', ], + fonderlessList: [ + true, + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getFolderlessLists', + loadOptionsDependsOn: [ + 'space', + ], + }, + required: true, + }, + { + displayName: 'List', + name: 'list', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'create', + ], + fonderlessList: [ + false, + ], }, }, typeOptions: { From 1d4f42d87b99f446b3ad15cc7bc9b6e58b97e0cb Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sun, 26 Jan 2020 19:48:57 -0800 Subject: [PATCH 40/41] :zap: Fix typos on ClickUp-Node --- packages/nodes-base/nodes/ClickUp/TaskDescription.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/nodes-base/nodes/ClickUp/TaskDescription.ts b/packages/nodes-base/nodes/ClickUp/TaskDescription.ts index 123244e8a4..c51baa4950 100644 --- a/packages/nodes-base/nodes/ClickUp/TaskDescription.ts +++ b/packages/nodes-base/nodes/ClickUp/TaskDescription.ts @@ -88,8 +88,8 @@ export const taskFields = [ required: true, }, { - displayName: 'Forderless List', - name: 'fonderlessList', + displayName: 'Folderless List', + name: 'folderless', type: 'boolean', default: false, displayOptions: { @@ -117,7 +117,7 @@ export const taskFields = [ operation: [ 'create', ], - fonderlessList: [ + folderless: [ false, ], }, @@ -143,7 +143,7 @@ export const taskFields = [ operation: [ 'create', ], - fonderlessList: [ + folderless: [ true, ], }, @@ -169,7 +169,7 @@ export const taskFields = [ operation: [ 'create', ], - fonderlessList: [ + folderless: [ false, ], }, From 13c5eef8661dece300955cb3ef731050261b01c6 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sun, 26 Jan 2020 19:50:17 -0800 Subject: [PATCH 41/41] :zap: Add minValue on ClickUp-Node priority parameter --- packages/nodes-base/nodes/ClickUp/TaskDescription.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/nodes-base/nodes/ClickUp/TaskDescription.ts b/packages/nodes-base/nodes/ClickUp/TaskDescription.ts index c51baa4950..62b8c570ff 100644 --- a/packages/nodes-base/nodes/ClickUp/TaskDescription.ts +++ b/packages/nodes-base/nodes/ClickUp/TaskDescription.ts @@ -274,6 +274,7 @@ export const taskFields = [ name: 'priority', type: 'number', typeOptions: { + minValue: 1, maxValue: 4, }, description: 'Integer mapping as 1 : Urgent, 2 : High, 3 : Normal, 4 : Low', @@ -407,6 +408,7 @@ export const taskFields = [ name: 'priority', type: 'number', typeOptions: { + minValue: 1, maxValue: 4, }, description: 'Integer mapping as 1 : Urgent, 2 : High, 3 : Normal, 4 : Low',