From 7ec85f0376da95a169677144dcbded828ec2a99c Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sat, 18 Jan 2020 17:19:31 -0500 Subject: [PATCH 1/2] :sparkles: clickup node and trigger --- .../credentials/ClickUpApi.credentials.ts | 17 + .../nodes-base/nodes/ClickUp/ClickUp.node.ts | 308 +++++++++++++ .../nodes/ClickUp/ClickUpTrigger.node.ts | 328 ++++++++++++++ .../nodes/ClickUp/GenericFunctions.ts | 37 ++ .../nodes/ClickUp/TaskDescription.ts | 418 ++++++++++++++++++ .../nodes-base/nodes/ClickUp/TaskInterface.ts | 16 + packages/nodes-base/nodes/ClickUp/clickup.png | Bin 0 -> 3112 bytes packages/nodes-base/package.json | 3 + 8 files changed, 1127 insertions(+) create mode 100644 packages/nodes-base/credentials/ClickUpApi.credentials.ts create mode 100644 packages/nodes-base/nodes/ClickUp/ClickUp.node.ts create mode 100644 packages/nodes-base/nodes/ClickUp/ClickUpTrigger.node.ts create mode 100644 packages/nodes-base/nodes/ClickUp/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/ClickUp/TaskDescription.ts create mode 100644 packages/nodes-base/nodes/ClickUp/TaskInterface.ts create mode 100755 packages/nodes-base/nodes/ClickUp/clickup.png diff --git a/packages/nodes-base/credentials/ClickUpApi.credentials.ts b/packages/nodes-base/credentials/ClickUpApi.credentials.ts new file mode 100644 index 0000000000..7003b784b3 --- /dev/null +++ b/packages/nodes-base/credentials/ClickUpApi.credentials.ts @@ -0,0 +1,17 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class ClickUpApi implements ICredentialType { + name = 'clickUpApi'; + displayName = 'ClickUp API'; + properties = [ + { + displayName: 'Access Token', + name: 'accessToken', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts b/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts new file mode 100644 index 0000000000..f3fd1db9db --- /dev/null +++ b/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts @@ -0,0 +1,308 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; +import { + IDataObject, + INodeTypeDescription, + INodeExecutionData, + INodeType, + ILoadOptionsFunctions, + INodePropertyOptions, +} from 'n8n-workflow'; +import { + clickupApiRequest, +} from './GenericFunctions'; +import { + taskFields, + taskOperations +} from './TaskDescription'; +import { + ITask, + } from './TaskInterface'; + +export class ClickUp implements INodeType { + description: INodeTypeDescription = { + displayName: 'ClickUp', + name: 'clickup', + icon: 'file:clickup.png', + group: ['output'], + version: 1, + subtitle: '={{$parameter["operation"] + ":" + $parameter["resource"]}}', + description: 'Consume ClickUp API (Beta)', + defaults: { + name: 'ClickUp', + color: '#7B68EE', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'clickUpApi', + required: true, + } + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Task', + value: 'task', + }, + ], + default: 'task', + description: 'Resource to consume.', + }, + ...taskOperations, + ...taskFields, + ], + }; + + methods = { + loadOptions: { + // Get all the available teams to display them to user so that he can + // select them easily + async getTeams(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const { teams } = await clickupApiRequest.call(this, 'GET', '/team'); + for (const team of teams) { + const teamName = team.name; + const teamId = team.id; + returnData.push({ + name: teamName, + value: teamId, + }); + } + return returnData; + }, + // Get all the available spaces to display them to user so that he can + // select them easily + async getSpaces(this: ILoadOptionsFunctions): Promise { + const teamId = this.getCurrentNodeParameter('team') as string; + const returnData: INodePropertyOptions[] = []; + const { spaces } = await clickupApiRequest.call(this, 'GET', `/team/${teamId}/space`); + for (const space of spaces) { + const spaceName = space.name; + const spaceId = space.id; + returnData.push({ + name: spaceName, + value: spaceId, + }); + } + return returnData; + }, + // Get all the available folders to display them to user so that he can + // select them easily + async getFolders(this: ILoadOptionsFunctions): Promise { + const spaceId = this.getCurrentNodeParameter('space') as string; + const returnData: INodePropertyOptions[] = []; + const { folders } = await clickupApiRequest.call(this, 'GET', `/space/${spaceId}/folder`); + for (const folder of folders) { + const folderName = folder.name; + const folderId = folder.id; + returnData.push({ + name: folderName, + value: folderId, + }); + } + return returnData; + }, + // Get all the available lists to display them to user so that he can + // select them easily + async getLists(this: ILoadOptionsFunctions): Promise { + const folderId = this.getCurrentNodeParameter('folder') as string; + const returnData: INodePropertyOptions[] = []; + const { lists } = await clickupApiRequest.call(this, 'GET', `/folder/${folderId}/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 { + const listId = this.getCurrentNodeParameter('list') as string; + const returnData: INodePropertyOptions[] = []; + const { members } = await clickupApiRequest.call(this, 'GET', `/list/${listId}/member`); + for (const member of members) { + const memberName = member.username; + const menberId = member.id; + returnData.push({ + name: memberName, + value: menberId, + }); + } + return returnData; + }, + // Get all the available tags to display them to user so that he can + // select them easily + async getTags(this: ILoadOptionsFunctions): Promise { + const spaceId = this.getCurrentNodeParameter('space') as string; + const returnData: INodePropertyOptions[] = []; + const { tags } = await clickupApiRequest.call(this, 'GET', `/space/${spaceId}/tag`); + for (const tag of tags) { + const tagName = tag.name; + const tagId = tag.id; + returnData.push({ + name: tagName, + value: tagId, + }); + } + return returnData; + }, + // Get all the available tags to display them to user so that he can + // select them easily + async getStatuses(this: ILoadOptionsFunctions): Promise { + const listId = this.getCurrentNodeParameter('list') as string; + const returnData: INodePropertyOptions[] = []; + const { statuses } = await clickupApiRequest.call(this, 'GET', `/list/${listId}`); + for (const status of statuses) { + const statusName = status.status; + const statusId = status.status; + returnData.push({ + name: statusName, + value: statusId, + }); + } + return returnData; + }, + }, + }; + + 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 resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + if (resource === 'task') { + if (operation === 'create') { + const listId = this.getNodeParameter('list', i) as string; + const name = this.getNodeParameter('name', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: ITask = { + name, + }; + if (additionalFields.content) { + body.content = additionalFields.content as string; + } + if (additionalFields.assignees) { + body.assignees = additionalFields.assignees as string[]; + } + if (additionalFields.tags) { + body.tags = additionalFields.tags as string[]; + } + if (additionalFields.status) { + body.status = additionalFields.status as string; + } + if (additionalFields.priority) { + body.priority = additionalFields.priority as number; + } + if (additionalFields.dueDate) { + body.due_date = new Date(additionalFields.dueDate as string).getTime(); + } + if (additionalFields.dueDateTime) { + body.due_date_time = additionalFields.dueDateTime as boolean; + } + if (additionalFields.timeEstimate) { + body.time_estimate = (additionalFields.timeEstimate as number) * 6000; + } + if (additionalFields.startDate) { + body.start_date = new Date(additionalFields.startDate as string).getTime(); + } + if (additionalFields.startDateTime) { + body.start_date_time = additionalFields.startDateTime as boolean; + } + if (additionalFields.notifyAll) { + body.notify_all = additionalFields.notifyAll as boolean; + } + if (additionalFields.parentId) { + body.parent = additionalFields.parentId as string; + } + if (additionalFields.markdownContent) { + delete body.content; + body.markdown_content = additionalFields.content as string; + } + try { + responseData = await clickupApiRequest.call(this, 'POST', `/list/${listId}/task`, body); + } catch (err) { + throw new Error(`ClickUp Error: ${err}`); + } + } + if (operation === 'update') { + const taskId = this.getNodeParameter('id', i) as string; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + const body: ITask = {}; + if (updateFields.content) { + body.content = updateFields.content as string; + } + if (updateFields.priority) { + body.priority = updateFields.priority as number; + } + if (updateFields.dueDate) { + body.due_date = new Date(updateFields.dueDate as string).getTime(); + } + if (updateFields.dueDateTime) { + body.due_date_time = updateFields.dueDateTime as boolean; + } + if (updateFields.timeEstimate) { + body.time_estimate = (updateFields.timeEstimate as number) * 6000; + } + if (updateFields.startDate) { + body.start_date = new Date(updateFields.startDate as string).getTime(); + } + if (updateFields.startDateTime) { + body.start_date_time = updateFields.startDateTime as boolean; + } + if (updateFields.notifyAll) { + body.notify_all = updateFields.notifyAll as boolean; + } + if (updateFields.parentId) { + body.parent = updateFields.parentId as string; + } + if (updateFields.markdownContent) { + delete body.content; + body.markdown_content = updateFields.content as string; + } + try { + responseData = await clickupApiRequest.call(this, 'PUT', `/task/${taskId}`, body); + } catch (err) { + throw new Error(`ClickUp Error: ${err}`); + } + } + if (operation === 'get') { + const taskId = this.getNodeParameter('id', i) as string; + try { + responseData = await clickupApiRequest.call(this, 'GET', `/task/${taskId}`); + } catch (err) { + throw new Error(`ClickUp Error: ${err}`); + } + } + if (operation === 'delete') { + const taskId = this.getNodeParameter('id', i) as string; + try { + responseData = await clickupApiRequest.call(this, 'DELETE', `/task/${taskId}`, {}); + } catch (err) { + throw new Error(`ClickUp Error: ${err}`); + } + } + } + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else { + returnData.push(responseData as IDataObject); + } + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/ClickUp/ClickUpTrigger.node.ts b/packages/nodes-base/nodes/ClickUp/ClickUpTrigger.node.ts new file mode 100644 index 0000000000..4ee6d66bba --- /dev/null +++ b/packages/nodes-base/nodes/ClickUp/ClickUpTrigger.node.ts @@ -0,0 +1,328 @@ +import { + IHookFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { + IDataObject, + INodeTypeDescription, + INodeType, + IWebhookResponseData, + INodePropertyOptions, + ILoadOptionsFunctions, +} from 'n8n-workflow'; + +import { + clickupApiRequest, +} from './GenericFunctions'; + +import { createHmac } from 'crypto'; + +export class ClickUpTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'ClickUp Trigger', + name: 'clickupTrigger', + icon: 'file:clickup.png', + group: ['trigger'], + version: 1, + description: 'Handle ClickUp events via webhooks (Beta)', + defaults: { + name: 'ClickUp Trigger', + color: '#7B68EE', + }, + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'clickUpApi', + required: true, + } + ], + webhooks: [ + { + name: 'default', + httpMethod: 'POST', + responseMode: 'onReceived', + path: 'webhook', + }, + ], + properties: [ + { + displayName: 'Team', + name: 'team', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + default: '', + }, + { + displayName: 'Events', + name: 'events', + type: 'multiOptions', + required: true, + default: [], + options: [ + { + name: '*', + value: '*', + }, + { + name: 'task.created', + value: 'taskCreated', + }, + { + name: 'task.updated', + value: 'taskUpdated', + }, + { + name: 'task.deleted', + value: 'taskDeleted', + }, + { + name: 'task.status.updated', + value: 'taskStatusUpdated', + }, + { + name: 'task.assignee.updated', + value: 'taskAssigneeUpdated', + }, + { + name: 'task.dueDate.updated', + value: 'taskDueDateUpdated', + }, + { + name: 'task.tag.updated', + value: 'taskTagUpdated', + }, + { + name: 'task.moved', + value: 'taskMoved', + }, + { + name: 'task.comment.posted', + value: 'taskCommentPosted', + }, + { + name: 'task.comment.updated', + value: 'taskCommentUpdated', + }, + { + name: 'task.timeEstimate.updated', + value: 'taskTimeEstimateUpdated', + }, + { + name: 'task.timeTracked.updated', + value: 'taskTimeTrackedUpdated', + }, + { + name: 'list.created', + value: 'listCreated', + }, + { + name: 'list.updated', + value: 'listUpdated', + }, + { + name: 'list.deleted', + value: 'listDeleted', + }, + { + name: 'folder.created', + value: 'folderCreated', + }, + { + name: 'folder.updated', + value: 'folderUpdated', + }, + { + name: 'folder.deleted', + value: 'folderDeleted', + }, + { + name: 'space.created', + value: 'spaceCreated', + }, + { + name: 'space.updated', + value: 'spaceUpdated', + }, + { + name: 'space.deleted', + value: 'spaceDeleted', + }, + { + name: 'goal.created', + value: 'goalCreated', + }, + { + name: 'goal.updated', + value: 'goalUpdated', + }, + { + name: 'goal.deleted', + value: 'goalDeleted', + }, + { + name: 'keyResult.created', + value: 'keyResultCreated', + }, + { + name: 'keyResult.updated', + value: 'keyResultUpdated', + }, + { + name: 'keyResult.deleted', + value: 'keyResultDelete', + }, + ], + }, + { + displayName: 'RAW Data', + name: 'rawData', + type: 'boolean', + default: false, + description: 'If the data should be returned RAW instead of parsed.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Field', + default: {}, + options: [ + { + displayName: 'Space ID', + name: 'spaceId', + type: 'string', + default: '', + }, + { + displayName: 'Folder ID', + name: 'folderId', + type: 'string', + default: '', + }, + { + displayName: 'List ID', + name: 'listId', + type: 'string', + default: '', + }, + { + displayName: 'Task ID', + name: 'taskId', + type: 'string', + default: '', + }, + ], + }, + ], + }; + + methods = { + loadOptions: { + // Get all the available teams to display them to user so that he can + // select them easily + async getTeams(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const { teams } = await clickupApiRequest.call(this, 'GET', '/team'); + for (const team of teams) { + const teamName = team.name; + const teamId = team.id; + returnData.push({ + name: teamName, + value: teamId, + }); + } + return returnData; + }, + }, + }; + // @ts-ignore + webhookMethods = { + default: { + async checkExists(this: IHookFunctions): Promise { + const teamId = this.getNodeParameter('team') as string; + const webhookData = this.getWorkflowStaticData('node'); + if (webhookData.webhookId === undefined) { + return false; + } + const endpoint = `/team/${teamId}/webhook`; + const { webhooks } = await clickupApiRequest.call(this, 'GET', endpoint); + if (Array.isArray(webhooks)) { + for (const webhook of webhooks) { + if (webhook.id === webhookData.webhookId) { + return true; + } + } + } + return false; + }, + async create(this: IHookFunctions): Promise { + const webhookUrl = this.getNodeWebhookUrl('default'); + const webhookData = this.getWorkflowStaticData('node'); + const filters = this.getNodeParameter('filters') as IDataObject; + const teamId = this.getNodeParameter('team') as string; + const events = this.getNodeParameter('events') as string[]; + const endpoint = `/team/${teamId}/webhook`; + const body: IDataObject = { + endpoint: webhookUrl, + events, + }; + if (events.includes('*')) { + body.events = '*'; + } + if (filters.listId) { + body.list_id = (filters.listId as string).replace('#',''); + } + if (filters.taskId) { + body.task_id = (filters.taskId as string).replace('#',''); + } + if (filters.spaceId) { + body.space_id = (filters.spaceId as string).replace('#',''); + } + if (filters.folderId) { + body.folder_id = (filters.folderId as string).replace('#',''); + } + const { webhook } = await clickupApiRequest.call(this, 'POST', endpoint, body); + webhookData.webhookId = webhook.id; + webhookData.secret = webhook.secret; + return true; + }, + async delete(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + const endpoint = `/webhook/${webhookData.webhookId}`; + try { + await clickupApiRequest.call(this, 'DELETE', endpoint); + } catch(error) { + return false; + } + delete webhookData.webhookId; + delete webhookData.secret; + return true; + }, + }, + }; + + async webhook(this: IWebhookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + const headerData = this.getHeaderData() as IDataObject; + const rawData = this.getNodeParameter('rawData') as boolean; + const req = this.getRequestObject(); + const computedSignature = createHmac('sha256', webhookData.secret as string).update(JSON.stringify(req.body)).digest('hex'); + if (headerData['x-signature'] !== computedSignature) { + // Signature is not valid so ignore call + return {}; + } + if (!rawData) { + delete req.body.history_items + } + return { + workflowData: [ + this.helpers.returnJsonArray(req.body), + ], + }; + } +} diff --git a/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts b/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts new file mode 100644 index 0000000000..e29a0f99b6 --- /dev/null +++ b/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts @@ -0,0 +1,37 @@ +import { OptionsWithUri } from 'request'; +import { + IExecuteFunctions, + IExecuteSingleFunctions, + IHookFunctions, + ILoadOptionsFunctions, + IWebhookFunctions, +} from 'n8n-core'; +import { IDataObject } from 'n8n-workflow'; + +export async function clickupApiRequest(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('clickUpApi'); + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + let options: OptionsWithUri = { + headers: { + Authorization: credentials.accessToken, + 'Content-Type': 'application/json', + }, + method, + qs, + body, + uri: uri ||`https://api.clickup.com/api/v2${resource}`, + json: true + }; + try { + return await this.helpers.request!(options); + } catch (error) { + let errorMessage = error; + if (error.err) { + errorMessage = error.err + } + throw new Error('Click Up Error: ' + errorMessage); + } +} diff --git a/packages/nodes-base/nodes/ClickUp/TaskDescription.ts b/packages/nodes-base/nodes/ClickUp/TaskDescription.ts new file mode 100644 index 0000000000..1b9b989c07 --- /dev/null +++ b/packages/nodes-base/nodes/ClickUp/TaskDescription.ts @@ -0,0 +1,418 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const taskOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'task', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a task', + }, + { + name: 'Update', + value: 'update', + description: 'Update a task', + }, + { + name: 'Get', + value: 'get', + description: 'Get a task', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a task', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const taskFields = [ + +/* -------------------------------------------------------------------------- */ +/* task:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'create', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Space', + name: 'space', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'create', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getSpaces', + loadOptionsDependsOn: [ + 'team', + ] + }, + required: true, + }, + { + displayName: 'Folder', + name: 'folder', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'create', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getFolders', + loadOptionsDependsOn: [ + 'space', + ] + }, + required: true, + }, + { + displayName: 'List', + name: 'list', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'create', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getLists', + loadOptionsDependsOn: [ + 'folder', + ] + }, + required: true, + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + description: 'The first name on the task', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Content', + name: 'content', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Is Markdown Content', + name: 'markdownContent', + type: 'boolean', + default: false, + }, + { + displayName: 'Assignees', + name: 'assignees', + type: 'multiOptions', + loadOptionsDependsOn: [ + 'list', + ], + typeOptions: { + loadOptionsMethod: 'getAssignees' + }, + + default: [], + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + loadOptionsDependsOn: [ + 'space', + ], + typeOptions: { + loadOptionsMethod: 'getTags', + }, + default: [], + description: 'The array of tags applied to this task', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + loadOptionsDependsOn: [ + 'list', + ], + typeOptions: { + loadOptionsMethod: 'getStatuses', + }, + default: '', + }, + { + displayName: 'Priority', + name: 'priority', + type: 'number', + typeOptions: { + maxValue: 4, + }, + description: 'Integer mapping as 1 : Urgent, 2 : High, 3 : Normal, 4 : Low', + default: 1, + }, + { + displayName: 'Notify All', + name: 'notifyAll', + type: 'boolean', + default: false, + }, + { + displayName: 'Parent ID', + name: 'parentId', + type: 'string', + default: '', + }, + { + displayName: 'Start Date Time', + name: 'startDateTime', + type: 'boolean', + default: false, + }, + { + displayName: 'Due Date Time', + name: 'dueDateTime', + type: 'boolean', + default: false, + }, + { + displayName: 'Due Date', + name: 'dueDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Time Estimate', + name: 'timeEstimate', + type: 'number', + description: 'time estimate in minutes', + default: 1, + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* task:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Task ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'update', + ], + }, + }, + description: 'Task ID', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Content', + name: 'content', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Is Markdown Content', + name: 'markdownContent', + type: 'boolean', + default: false, + }, + { + displayName: 'Priority', + name: 'priority', + type: 'number', + typeOptions: { + maxValue: 4, + }, + description: 'Integer mapping as 1 : Urgent, 2 : High, 3 : Normal, 4 : Low', + default: 1, + }, + { + displayName: 'Notify All', + name: 'notifyAll', + type: 'boolean', + default: false, + }, + { + displayName: 'Parent ID', + name: 'parentId', + type: 'string', + default: '', + }, + { + displayName: 'Start Date Time', + name: 'startDateTime', + type: 'boolean', + default: false, + }, + { + displayName: 'Due Date Time', + name: 'dueDateTime', + type: 'boolean', + default: false, + }, + { + displayName: 'Due Date', + name: 'dueDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Time Estimate', + name: 'timeEstimate', + type: 'number', + description: 'time estimate in minutes', + default: 1, + }, + ], + + }, +/* -------------------------------------------------------------------------- */ +/* task:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Task ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'get', + ], + }, + }, + description: 'Task ID', + }, +/* -------------------------------------------------------------------------- */ +/* task:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'delete', + ], + }, + }, + description: 'task ID', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ClickUp/TaskInterface.ts b/packages/nodes-base/nodes/ClickUp/TaskInterface.ts new file mode 100644 index 0000000000..70a3899e15 --- /dev/null +++ b/packages/nodes-base/nodes/ClickUp/TaskInterface.ts @@ -0,0 +1,16 @@ +export interface ITask { + name?: string; + content?: string; + assignees?: string[]; + tags?: string[]; + status?: string; + priority?: number; + due_date?: number; + due_date_time?: boolean; + time_estimate?: number; + start_date?: number; + start_date_time?: boolean; + markdown_content?: string; + notify_all?: boolean; + parent?: string; +} diff --git a/packages/nodes-base/nodes/ClickUp/clickup.png b/packages/nodes-base/nodes/ClickUp/clickup.png new file mode 100755 index 0000000000000000000000000000000000000000..905deb1c377206025daa2482c08d4796001a5463 GIT binary patch literal 3112 zcmY*bcQ_nM_g+?scB4d1BCAK+)k2gdgy=+8uZt`eYn9bYL>E!RMoZBl%pVi|P)C)9l8q7o0MMwbDL=YcbQq!)V%S2Yt40Dy|&(ue@*nJfSRkqJ`Y7-OuhC1Z<5fvq2- zZ4h7|l z+PY{Ea(My~1&cub<-SOjyNt@{x+4)6%9s4|qH@2H{}cO1M-Fl+{$IuX>GXH>qE&fH zImo})CQqr=&SwGuP$JZo74;oRH%w`s>yI+9A z!N!A4*h^aQ_^Nl($hcotot8qW&V8SvH1}{SF1cQGIkFJ)TWWRGkocs3(^!a z45UV13%8C&vxr#^?{Nrgg;byyMD~t91gJTtv>w*yaagq+zNTjL#vD$az!l<04&fq8 zaoWwRnfBq5Ry8ghlXDJfoH$`fdmpnRJra<1e3mY?Knl3|m%xf5X+OSPY&Bd()T-X@Mp}E$ z*aHS4$t-g{7=VgAJT)R8@av4oG1jh_=lZ$CB$honm?Lr0#ISYH2Tm!v#ifv{=H~qz zJiBP~j9SG_sq>bqkg@&;uyJHb3|lm9%J##e?>cX6p1GLnz}hz57lI@fFg+~d(ozzY-_1LH(ptRY4kYPdjph_wU6j21+T2 z+hEgmbfnX$2})bc@b0Dl)-v>*TG1TYRc8(`e86zMvMH$}_h(dlk7DTx)p)38t*d{b z?}Ad!=IM{os9xrK6cu{lVF1-uH|+uK@WVo$c#oCyJ6kSI>V9IZehSiZ)Rorc#>{`Vrv>=biW{E7({k8ajk0UKte8D>3Q+n zO>!B3Of{`u02`mkuw?ElW;V2~5Mb=dun_P)sjo%uMIrUIB!Sg+wM!<5VV~srLQ*g2 zdf7w#s3j|rmnR>O>X9yQKRNU%d1Ep|sOk-4UB!37{Tbc4+-3r~-Gy?F;b4Nlf|397 z-0f-sU9ItI#GB6(T+=K&x^}0j5uH*nKjSKqVFABu7`{Ea;W*0cW6X1A-#b$P_Qi#~ zM^kP=sb9 z{7~B8YkA@hi{f7gCcL;<5Fm?w7m~IIqbu=Lw{#=g-ulYD9yqgg3N}zBKS^`C<*yZ3 zR#$K!BkA^Ou$|y`9{h#V7~$>NR8=u?kRa$F239Ec8Ph0OSZw$5GK-?M+($G7{8-1O z?OINO34R(Uar6U%lB)RrS^7Y(jEow40>f$8L?67xTl5XRWs-bg*Nx@8_oK}1;WGm5 zkAD%aOt52H2hVw~HsBIhQjTQP@Ta_fu4k6)ycmtQPP%r*zF%LlTp=r1CKrA2AY1R;0oygJGl zf}-bfYuQ!jxYm}9IIF@Fr$nc=rXEIQtG6%eI5%pMd;G33c4gr;kvA97$yqlbGXHVk z{?EP4q;O5KaV4wHJ(A3Cyiru5o1^->p*{O=^6m@r)kTwy$6{2HNmS*^hd{_%vC z@pSy@Mx@zSq_x}N9H9XhvS)PEm>y`HCtB(R5AScE5?8r?Qr0|lSCnazNn~^D@UI=R z&7$G1oU;7*V^qDc%9Agh$f@jYET!q)2-mkZPm)DHZysi$-q0+|0trCZStsKV1GPNp z?xL+hS7L|!L^c$t{hjPrAqcn7xYSiyL#I`q{PG|W+Qu?lUh|}~fWVi6ul>9dLq`Ho z>NM&Lqq1ULP*=p@Z`RMLtg$s>ftX3v&{`SHW;I>ZhYLE zSXo3c`J6q0%yT1VJ1<`VfLplFvR;nHFhsGRNMeWVC-n>}oc2ko`)7G*3C#y&a>y5; zRM!~Eu$xdFT!W+)+l-JosdM2mx#6-=LwxnP`Nf;SQuMKl&&R=Jzvll~J8G4B-Cn@k zam!fe8g5-^(QnSr%<2ETCSqbP>yel lp5~LKvpM{6&gLbyZ>6S}TB(a&^SS)HtE=cJmn*?T{sV=s&|Uxl literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 4afb893073..8835a31ece 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -34,6 +34,7 @@ "dist/credentials/BitbucketApi.credentials.js", "dist/credentials/ChargebeeApi.credentials.js", "dist/credentials/CodaApi.credentials.js", + "dist/credentials/ClickUpApi.credentials.js", "dist/credentials/DropboxApi.credentials.js", "dist/credentials/EventbriteApi.credentials.js", "dist/credentials/FreshdeskApi.credentials.js", @@ -95,6 +96,8 @@ "dist/nodes/Chargebee/Chargebee.node.js", "dist/nodes/Chargebee/ChargebeeTrigger.node.js", "dist/nodes/Coda/Coda.node.js", + "dist/nodes/ClickUp/ClickUp.node.js", + "dist/nodes/ClickUp/ClickUpTrigger.node.js", "dist/nodes/Cron.node.js", "dist/nodes/Discord/Discord.node.js", "dist/nodes/Dropbox/Dropbox.node.js", From 6639ccefc17011cad7b4dcfd84523f959e082d5e Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 18 Jan 2020 20:36:57 -0600 Subject: [PATCH 2/2] :zap: Minor improvements to ClickUp-Node --- .../nodes-base/nodes/ClickUp/ClickUp.node.ts | 42 ++-- .../nodes/ClickUp/ClickUpTrigger.node.ts | 185 ++++++++---------- .../nodes/ClickUp/GenericFunctions.ts | 6 +- .../nodes/ClickUp/TaskDescription.ts | 170 ++++++++-------- packages/nodes-base/package.json | 4 +- 5 files changed, 196 insertions(+), 211 deletions(-) diff --git a/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts b/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts index f3fd1db9db..20ee32b65d 100644 --- a/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts +++ b/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts @@ -3,11 +3,11 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, - INodeExecutionData, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { clickupApiRequest, @@ -23,7 +23,7 @@ import { export class ClickUp implements INodeType { description: INodeTypeDescription = { displayName: 'ClickUp', - name: 'clickup', + name: 'clickUp', icon: 'file:clickup.png', group: ['output'], version: 1, @@ -182,9 +182,11 @@ export class ClickUp implements INodeType { 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++) { - const resource = this.getNodeParameter('resource', 0) as string; - const operation = this.getNodeParameter('operation', 0) as string; if (resource === 'task') { if (operation === 'create') { const listId = this.getNodeParameter('list', i) as string; @@ -233,11 +235,8 @@ export class ClickUp implements INodeType { delete body.content; body.markdown_content = additionalFields.content as string; } - try { - responseData = await clickupApiRequest.call(this, 'POST', `/list/${listId}/task`, body); - } catch (err) { - throw new Error(`ClickUp Error: ${err}`); - } + + responseData = await clickupApiRequest.call(this, 'POST', `/list/${listId}/task`, body); } if (operation === 'update') { const taskId = this.getNodeParameter('id', i) as string; @@ -267,6 +266,9 @@ export class ClickUp implements INodeType { if (updateFields.notifyAll) { body.notify_all = updateFields.notifyAll as boolean; } + if (updateFields.name) { + body.name = updateFields.name as string; + } if (updateFields.parentId) { body.parent = updateFields.parentId as string; } @@ -274,27 +276,15 @@ export class ClickUp implements INodeType { delete body.content; body.markdown_content = updateFields.content as string; } - try { - responseData = await clickupApiRequest.call(this, 'PUT', `/task/${taskId}`, body); - } catch (err) { - throw new Error(`ClickUp Error: ${err}`); - } + responseData = await clickupApiRequest.call(this, 'PUT', `/task/${taskId}`, body); } if (operation === 'get') { const taskId = this.getNodeParameter('id', i) as string; - try { - responseData = await clickupApiRequest.call(this, 'GET', `/task/${taskId}`); - } catch (err) { - throw new Error(`ClickUp Error: ${err}`); - } + responseData = await clickupApiRequest.call(this, 'GET', `/task/${taskId}`); } if (operation === 'delete') { const taskId = this.getNodeParameter('id', i) as string; - try { - responseData = await clickupApiRequest.call(this, 'DELETE', `/task/${taskId}`, {}); - } catch (err) { - throw new Error(`ClickUp Error: ${err}`); - } + responseData = await clickupApiRequest.call(this, 'DELETE', `/task/${taskId}`, {}); } } if (Array.isArray(responseData)) { diff --git a/packages/nodes-base/nodes/ClickUp/ClickUpTrigger.node.ts b/packages/nodes-base/nodes/ClickUp/ClickUpTrigger.node.ts index 4ee6d66bba..36d523313e 100644 --- a/packages/nodes-base/nodes/ClickUp/ClickUpTrigger.node.ts +++ b/packages/nodes-base/nodes/ClickUp/ClickUpTrigger.node.ts @@ -5,11 +5,11 @@ import { import { IDataObject, - INodeTypeDescription, - INodeType, - IWebhookResponseData, - INodePropertyOptions, ILoadOptionsFunctions, + INodePropertyOptions, + INodeType, + INodeTypeDescription, + IWebhookResponseData, } from 'n8n-workflow'; import { @@ -21,7 +21,7 @@ import { createHmac } from 'crypto'; export class ClickUpTrigger implements INodeType { description: INodeTypeDescription = { displayName: 'ClickUp Trigger', - name: 'clickupTrigger', + name: 'clickUpTrigger', icon: 'file:clickup.png', group: ['trigger'], version: 1, @@ -68,89 +68,17 @@ export class ClickUpTrigger implements INodeType { name: '*', value: '*', }, - { - name: 'task.created', - value: 'taskCreated', - }, - { - name: 'task.updated', - value: 'taskUpdated', - }, - { - name: 'task.deleted', - value: 'taskDeleted', - }, - { - name: 'task.status.updated', - value: 'taskStatusUpdated', - }, - { - name: 'task.assignee.updated', - value: 'taskAssigneeUpdated', - }, - { - name: 'task.dueDate.updated', - value: 'taskDueDateUpdated', - }, - { - name: 'task.tag.updated', - value: 'taskTagUpdated', - }, - { - name: 'task.moved', - value: 'taskMoved', - }, - { - name: 'task.comment.posted', - value: 'taskCommentPosted', - }, - { - name: 'task.comment.updated', - value: 'taskCommentUpdated', - }, - { - name: 'task.timeEstimate.updated', - value: 'taskTimeEstimateUpdated', - }, - { - name: 'task.timeTracked.updated', - value: 'taskTimeTrackedUpdated', - }, - { - name: 'list.created', - value: 'listCreated', - }, - { - name: 'list.updated', - value: 'listUpdated', - }, - { - name: 'list.deleted', - value: 'listDeleted', - }, { name: 'folder.created', value: 'folderCreated', }, - { - name: 'folder.updated', - value: 'folderUpdated', - }, { name: 'folder.deleted', value: 'folderDeleted', }, { - name: 'space.created', - value: 'spaceCreated', - }, - { - name: 'space.updated', - value: 'spaceUpdated', - }, - { - name: 'space.deleted', - value: 'spaceDeleted', + name: 'folder.updated', + value: 'folderUpdated', }, { name: 'goal.created', @@ -168,23 +96,88 @@ export class ClickUpTrigger implements INodeType { name: 'keyResult.created', value: 'keyResultCreated', }, + { + name: 'keyResult.deleted', + value: 'keyResultDelete', + }, { name: 'keyResult.updated', value: 'keyResultUpdated', }, { - name: 'keyResult.deleted', - value: 'keyResultDelete', + name: 'list.created', + value: 'listCreated', + }, + { + name: 'list.deleted', + value: 'listDeleted', + }, + { + name: 'list.updated', + value: 'listUpdated', + }, + { + name: 'space.created', + value: 'spaceCreated', + }, + { + name: 'space.deleted', + value: 'spaceDeleted', + }, + { + name: 'space.updated', + value: 'spaceUpdated', + }, + { + name: 'task.assignee.updated', + value: 'taskAssigneeUpdated', + }, + { + name: 'task.comment.posted', + value: 'taskCommentPosted', + }, + { + name: 'task.comment.updated', + value: 'taskCommentUpdated', + }, + { + name: 'task.created', + value: 'taskCreated', + }, + { + name: 'task.deleted', + value: 'taskDeleted', + }, + { + name: 'task.dueDate.updated', + value: 'taskDueDateUpdated', + }, + { + name: 'task.moved', + value: 'taskMoved', + }, + { + name: 'task.status.updated', + value: 'taskStatusUpdated', + }, + { + name: 'task.tag.updated', + value: 'taskTagUpdated', + }, + { + name: 'task.timeEstimate.updated', + value: 'taskTimeEstimateUpdated', + }, + { + name: 'task.timeTracked.updated', + value: 'taskTimeTrackedUpdated', + }, + { + name: 'task.updated', + value: 'taskUpdated', }, ], }, - { - displayName: 'RAW Data', - name: 'rawData', - type: 'boolean', - default: false, - description: 'If the data should be returned RAW instead of parsed.', - }, { displayName: 'Filters', name: 'filters', @@ -192,12 +185,6 @@ export class ClickUpTrigger implements INodeType { placeholder: 'Add Field', default: {}, options: [ - { - displayName: 'Space ID', - name: 'spaceId', - type: 'string', - default: '', - }, { displayName: 'Folder ID', name: 'folderId', @@ -210,6 +197,12 @@ export class ClickUpTrigger implements INodeType { type: 'string', default: '', }, + { + displayName: 'Space ID', + name: 'spaceId', + type: 'string', + default: '', + }, { displayName: 'Task ID', name: 'taskId', @@ -309,16 +302,12 @@ export class ClickUpTrigger implements INodeType { async webhook(this: IWebhookFunctions): Promise { const webhookData = this.getWorkflowStaticData('node'); const headerData = this.getHeaderData() as IDataObject; - const rawData = this.getNodeParameter('rawData') as boolean; const req = this.getRequestObject(); const computedSignature = createHmac('sha256', webhookData.secret as string).update(JSON.stringify(req.body)).digest('hex'); if (headerData['x-signature'] !== computedSignature) { // Signature is not valid so ignore call return {}; } - if (!rawData) { - delete req.body.history_items - } return { workflowData: [ this.helpers.returnJsonArray(req.body), diff --git a/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts b/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts index e29a0f99b6..08375c5317 100644 --- a/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts +++ b/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts @@ -14,7 +14,7 @@ export async function clickupApiRequest(this: IHookFunctions | IExecuteFunctions throw new Error('No credentials got returned!'); } - let options: OptionsWithUri = { + const options: OptionsWithUri = { headers: { Authorization: credentials.accessToken, 'Content-Type': 'application/json', @@ -30,8 +30,8 @@ export async function clickupApiRequest(this: IHookFunctions | IExecuteFunctions } catch (error) { let errorMessage = error; if (error.err) { - errorMessage = error.err + errorMessage = error.err; } - throw new Error('Click Up Error: ' + errorMessage); + throw new Error('ClickUp Error: ' + errorMessage); } } diff --git a/packages/nodes-base/nodes/ClickUp/TaskDescription.ts b/packages/nodes-base/nodes/ClickUp/TaskDescription.ts index 1b9b989c07..b3883040c3 100644 --- a/packages/nodes-base/nodes/ClickUp/TaskDescription.ts +++ b/packages/nodes-base/nodes/ClickUp/TaskDescription.ts @@ -19,9 +19,9 @@ export const taskOperations = [ description: 'Create a task', }, { - name: 'Update', - value: 'update', - description: 'Update a task', + name: 'Delete', + value: 'delete', + description: 'Delete a task', }, { name: 'Get', @@ -29,9 +29,9 @@ export const taskOperations = [ description: 'Get a task', }, { - name: 'Delete', - value: 'delete', - description: 'Delete a task', + name: 'Update', + value: 'update', + description: 'Update a task', }, ], default: 'create', @@ -168,6 +168,19 @@ export const taskFields = [ }, }, options: [ + { + displayName: 'Assignees', + name: 'assignees', + type: 'multiOptions', + loadOptionsDependsOn: [ + 'list', + ], + typeOptions: { + loadOptionsMethod: 'getAssignees', + }, + + default: [], + }, { displayName: 'Content', name: 'content', @@ -178,58 +191,22 @@ export const taskFields = [ default: '', }, { - displayName: 'Is Markdown Content', - name: 'markdownContent', + displayName: 'Due Date', + name: 'dueDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Due Date Time', + name: 'dueDateTime', type: 'boolean', default: false, }, { - displayName: 'Assignees', - name: 'assignees', - type: 'multiOptions', - loadOptionsDependsOn: [ - 'list', - ], - typeOptions: { - loadOptionsMethod: 'getAssignees' - }, - - default: [], - }, - { - displayName: 'Tags', - name: 'tags', - type: 'multiOptions', - loadOptionsDependsOn: [ - 'space', - ], - typeOptions: { - loadOptionsMethod: 'getTags', - }, - default: [], - description: 'The array of tags applied to this task', - }, - { - displayName: 'Status', - name: 'status', - type: 'options', - loadOptionsDependsOn: [ - 'list', - ], - typeOptions: { - loadOptionsMethod: 'getStatuses', - }, - default: '', - }, - { - displayName: 'Priority', - name: 'priority', - type: 'number', - typeOptions: { - maxValue: 4, - }, - description: 'Integer mapping as 1 : Urgent, 2 : High, 3 : Normal, 4 : Low', - default: 1, + displayName: 'Is Markdown Content', + name: 'markdownContent', + type: 'boolean', + default: false, }, { displayName: 'Notify All', @@ -243,6 +220,16 @@ export const taskFields = [ type: 'string', default: '', }, + { + displayName: 'Priority', + name: 'priority', + type: 'number', + typeOptions: { + maxValue: 4, + }, + description: 'Integer mapping as 1 : Urgent, 2 : High, 3 : Normal, 4 : Low', + default: 3, + }, { displayName: 'Start Date Time', name: 'startDateTime', @@ -250,16 +237,29 @@ export const taskFields = [ default: false, }, { - displayName: 'Due Date Time', - name: 'dueDateTime', - type: 'boolean', - default: false, + displayName: 'Status', + name: 'status', + type: 'options', + loadOptionsDependsOn: [ + 'list', + ], + typeOptions: { + loadOptionsMethod: 'getStatuses', + }, + default: '', }, { - displayName: 'Due Date', - name: 'dueDate', - type: 'dateTime', - default: '', + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + loadOptionsDependsOn: [ + 'space', + ], + typeOptions: { + loadOptionsMethod: 'getTags', + }, + default: [], + description: 'The array of tags applied to this task', }, { displayName: 'Time Estimate', @@ -317,6 +317,18 @@ export const taskFields = [ }, default: '', }, + { + displayName: 'Due Date', + name: 'dueDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Due Date Time', + name: 'dueDateTime', + type: 'boolean', + default: false, + }, { displayName: 'Is Markdown Content', name: 'markdownContent', @@ -324,14 +336,10 @@ export const taskFields = [ default: false, }, { - displayName: 'Priority', - name: 'priority', - type: 'number', - typeOptions: { - maxValue: 4, - }, - description: 'Integer mapping as 1 : Urgent, 2 : High, 3 : Normal, 4 : Low', - default: 1, + displayName: 'Name', + name: 'name', + type: 'string', + default: '', }, { displayName: 'Notify All', @@ -345,24 +353,22 @@ export const taskFields = [ type: 'string', default: '', }, + { + displayName: 'Priority', + name: 'priority', + type: 'number', + typeOptions: { + maxValue: 4, + }, + description: 'Integer mapping as 1 : Urgent, 2 : High, 3 : Normal, 4 : Low', + default: 3, + }, { displayName: 'Start Date Time', name: 'startDateTime', type: 'boolean', default: false, }, - { - displayName: 'Due Date Time', - name: 'dueDateTime', - type: 'boolean', - default: false, - }, - { - displayName: 'Due Date', - name: 'dueDate', - type: 'dateTime', - default: '', - }, { displayName: 'Time Estimate', name: 'timeEstimate', diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 4087568d57..3b2dea916b 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -34,8 +34,8 @@ "dist/credentials/Aws.credentials.js", "dist/credentials/BitbucketApi.credentials.js", "dist/credentials/ChargebeeApi.credentials.js", - "dist/credentials/CodaApi.credentials.js", "dist/credentials/ClickUpApi.credentials.js", + "dist/credentials/CodaApi.credentials.js", "dist/credentials/CopperApi.credentials.js", "dist/credentials/DropboxApi.credentials.js", "dist/credentials/EventbriteApi.credentials.js", @@ -98,9 +98,9 @@ "dist/nodes/Bitbucket/BitbucketTrigger.node.js", "dist/nodes/Chargebee/Chargebee.node.js", "dist/nodes/Chargebee/ChargebeeTrigger.node.js", - "dist/nodes/Coda/Coda.node.js", "dist/nodes/ClickUp/ClickUp.node.js", "dist/nodes/ClickUp/ClickUpTrigger.node.js", + "dist/nodes/Coda/Coda.node.js", "dist/nodes/Copper/CopperTrigger.node.js", "dist/nodes/Cron.node.js", "dist/nodes/Discord/Discord.node.js",