diff --git a/packages/nodes-base/nodes/ClickUp/ChecklistDescription.ts b/packages/nodes-base/nodes/ClickUp/ChecklistDescription.ts new file mode 100644 index 0000000000..517d2946b5 --- /dev/null +++ b/packages/nodes-base/nodes/ClickUp/ChecklistDescription.ts @@ -0,0 +1,152 @@ +import { + INodeProperties, + } from 'n8n-workflow'; + +export const checklistOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'checklist', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a checklist', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a checklist', + }, + { + name: 'Update', + value: 'update', + description: 'Update a checklist', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const checklistFields = [ + +/* -------------------------------------------------------------------------- */ +/* checklist:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Task ID', + name: 'task', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'checklist', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'checklist', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* checklist:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Checklist ID', + name: 'checklist', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'checklist', + ], + operation: [ + 'delete', + ], + }, + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* checklist:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Checklist ID', + name: 'checklist', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'checklist', + ], + operation: [ + 'update', + ], + }, + }, + required: true, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'checklist', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Position', + name: 'position', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ClickUp/ChecklistItemDescription.ts b/packages/nodes-base/nodes/ClickUp/ChecklistItemDescription.ts new file mode 100644 index 0000000000..e15fb52919 --- /dev/null +++ b/packages/nodes-base/nodes/ClickUp/ChecklistItemDescription.ts @@ -0,0 +1,221 @@ +import { + INodeProperties, + } from 'n8n-workflow'; + +export const checklistItemOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'checklistItem', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a checklist item', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a checklist item', + }, + { + name: 'Update', + value: 'update', + description: 'Update a checklist item', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const checklistItemFields = [ + +/* -------------------------------------------------------------------------- */ +/* checklistItem:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Checklist ID', + name: 'checklist', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'checklistItem', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'checklistItem', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'checklistItem', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Assignee ID', + name: 'assignee', + type: 'string', + default: '', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* checklistItem:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Checklist ID', + name: 'checklist', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'checklistItem', + ], + operation: [ + 'delete', + ], + }, + }, + required: true, + }, + { + displayName: 'Checklist Item ID', + name: 'checklistItem', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'checklistItem', + ], + operation: [ + 'delete', + ], + }, + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* checklistItem:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Checklist ID', + name: 'checklist', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'checklistItem', + ], + operation: [ + 'update', + ], + }, + }, + required: true, + }, + { + displayName: 'Checklist Item ID', + name: 'checklistItem', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'checklistItem', + ], + operation: [ + 'update', + ], + }, + }, + required: true, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'checklistItem', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Assignee ID', + name: 'assignee', + type: 'string', + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Parent Checklist Item ID', + name: 'parent', + type: 'string', + default: '', + description: 'Checklist item that you want to nest the target checklist item underneath.', + }, + { + displayName: 'Resolved', + name: 'resolved', + type: 'boolean', + default: false, + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts b/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts index 887f9033ae..09d9d183b1 100644 --- a/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts +++ b/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts @@ -1,6 +1,7 @@ import { IExecuteFunctions, } from 'n8n-core'; + import { IDataObject, ILoadOptionsFunctions, @@ -9,23 +10,76 @@ import { INodeType, INodeTypeDescription, } from 'n8n-workflow'; + import { clickupApiRequest, clickupApiRequestAllItems, validateJSON, } from './GenericFunctions'; + +import { + checklistFields, + checklistOperations, +} from './ChecklistDescription'; + +import { + checklistItemFields, + checklistItemOperations, +} from './ChecklistItemDescription'; + +import { + commentFields, + commentOperations, +} from './CommentDescription'; + +import { + folderFields, + folderOperations, +} from './FolderDescription'; + +import { + goalFields, + goalOperations, +} from './GoalDescription'; + +import { + goalKeyResultFields, + goalKeyResultOperations, +} from './GoalKeyResultDescription'; + +// import { +// guestFields, +// guestOperations, +// } from './guestDescription'; + import { taskFields, taskOperations, } from './TaskDescription'; + +import { + taskDependencyFields, + taskDependencyOperations, +} from './TaskDependencyDescription'; + +import { + timeTrackingFields, + timeTrackingOperations, +} from './TimeTrackingDescription'; + import { listFields, listOperations, } from './ListDescription'; + import { ITask, } from './TaskInterface'; + import { + IList, + } from './ListInterface'; + export class ClickUp implements INodeType { description: INodeTypeDescription = { displayName: 'ClickUp', @@ -53,6 +107,34 @@ export class ClickUp implements INodeType { name: 'resource', type: 'options', options: [ + { + name: 'Checklist', + value: 'checklist', + }, + { + name: 'Checklist Item', + value: 'checklistItem', + }, + { + name: 'Comment', + value: 'comment', + }, + { + name: 'Folder', + value: 'folder', + }, + { + name: 'Goal', + value: 'goal', + }, + { + name: 'Goal Key Result', + value: 'goalKeyResult', + }, + // { + // name: 'Guest', + // value: 'guest', + // }, { name: 'List', value: 'list', @@ -61,12 +143,49 @@ export class ClickUp implements INodeType { name: 'Task', value: 'task', }, + { + name: 'Task Dependency', + value: 'taskDependency', + }, + { + name: 'Time Traking', + value: 'timeTracking', + }, ], default: 'task', description: 'Resource to consume.', }, + // CHECKLIST + ...checklistOperations, + ...checklistFields, + // CHECKLIST ITEM + ...checklistItemOperations, + ...checklistItemFields, + // COMMENT + ...commentOperations, + ...commentFields, + // FOLDER + ...folderOperations, + ...folderFields, + // GOAL + ...goalOperations, + ...goalFields, + // GOAL kEY RESULT + ...goalKeyResultOperations, + ...goalKeyResultFields, + // GUEST + // ...guestOperations, + // ...guestFields, + // TASK ...taskOperations, ...taskFields, + // TASK DEPENDENCY + ...taskDependencyOperations, + ...taskDependencyFields, + // TIME TRACKING + ...timeTrackingOperations, + ...timeTrackingFields, + // LIST ...listOperations, ...listFields, ], @@ -215,6 +334,347 @@ export class ClickUp implements INodeType { const operation = this.getNodeParameter('operation', 0) as string; for (let i = 0; i < length; i++) { + if (resource === 'checklist') { + if (operation === 'create') { + const taskId = this.getNodeParameter('task', i) as string; + const name = this.getNodeParameter('name', i) as string; + const body: IDataObject = { + name, + }; + responseData = await clickupApiRequest.call(this, 'POST', `/task/${taskId}/checklist`, body); + responseData = responseData.checklist; + } + if (operation === 'delete') { + const checklistId = this.getNodeParameter('checklist', i) as string; + responseData = await clickupApiRequest.call(this, 'DELETE', `/checklist/${checklistId}`); + responseData = { success: true }; + } + if (operation === 'update') { + const checklistId = this.getNodeParameter('checklist', i) as string; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + const body: IDataObject = {}; + if (updateFields.name) { + body.name = updateFields.name as string; + } + if (updateFields.position) { + body.position = updateFields.position as number; + } + responseData = await clickupApiRequest.call(this, 'PUT', `/checklist/${checklistId}`, body); + responseData = responseData.checklist; + } + } + if (resource === 'checklistItem') { + if (operation === 'create') { + const checklistId = this.getNodeParameter('checklist', i) as string; + const name = this.getNodeParameter('name', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IDataObject = { + name, + }; + if (additionalFields.assigneeId) { + body.assignee = parseInt(additionalFields.assigneeId as string, 10); + } + responseData = await clickupApiRequest.call(this, 'POST', `/checklist/${checklistId}/checklist_item`, body); + responseData = responseData.checklist; + } + if (operation === 'delete') { + const checklistId = this.getNodeParameter('checklist', i) as string; + const checklistItemId = this.getNodeParameter('checklistItem', i) as string; + responseData = await clickupApiRequest.call(this, 'DELETE', `/checklist/${checklistId}/checklist_item/${checklistItemId}`); + responseData = { success: true }; + } + if (operation === 'update') { + const checklistId = this.getNodeParameter('checklist', i) as string; + const checklistItemId = this.getNodeParameter('checklistItem', i) as string; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + const body: IDataObject = {}; + if (updateFields.name) { + body.name = updateFields.name as string; + } + if (updateFields.parent) { + body.parent = updateFields.parent as string; + } + if (updateFields.assignee) { + body.assignee = parseInt(updateFields.assignee as string, 10); + } + if (updateFields.resolved) { + body.resolved = updateFields.resolved as boolean; + } + responseData = await clickupApiRequest.call(this, 'PUT', `/checklist/${checklistId}/checklist_item/${checklistItemId}`, body); + responseData = responseData.checklist; + } + } + if (resource === 'comment') { + if (operation === 'create') { + const resource = this.getNodeParameter('commentOn', i) as string; + const id = this.getNodeParameter('id', i) as string; + const commentText = this.getNodeParameter('commentText', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IDataObject = { + comment_text: commentText, + }; + if (additionalFields.assignee) { + body.assigneeId = additionalFields.assignee as string; + } + if (additionalFields.notifyAll) { + body.notify_all = additionalFields.notifyAll as boolean; + } + responseData = await clickupApiRequest.call(this, 'POST', `/${resource}/${id}/comment`, body); + } + if (operation === 'delete') { + const commentId = this.getNodeParameter('comment', i) as string; + responseData = await clickupApiRequest.call(this, 'DELETE', `/comment/${commentId}`); + responseData = { success: true }; + } + if (operation === 'getAll') { + const resource = this.getNodeParameter('commentsOn', i) as string; + const id = this.getNodeParameter('id', i) as string; + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await clickupApiRequest.call(this, 'GET', `/${resource}/${id}/comment`, {}, qs); + responseData = responseData.comments; + responseData = responseData.splice(0, qs.limit); + } + if (operation === 'update') { + const commentId = this.getNodeParameter('comment', i) as string; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + const body: IDataObject = {}; + if (updateFields.commentText) { + body.comment_text = updateFields.commentText as string; + } + if (updateFields.assignee) { + body.assignee = parseInt(updateFields.assignee as string, 10); + } + if (updateFields.resolved) { + body.resolved = updateFields.resolved as boolean; + } + responseData = await clickupApiRequest.call(this, 'PUT', `/comment/${commentId}`, body); + responseData = { success: true }; + } + } + if (resource === 'folder') { + if (operation === 'create') { + const spaceId = this.getNodeParameter('space', i) as string; + const name = this.getNodeParameter('name', i) as string; + const body: IDataObject = { + name, + }; + responseData = await clickupApiRequest.call(this, 'POST', `/space/${spaceId}/folder`, body); + } + if (operation === 'delete') { + const folderId = this.getNodeParameter('folder', i) as string; + responseData = await clickupApiRequest.call(this, 'DELETE', `/folder/${folderId}`); + responseData = { success: true }; + } + if (operation === 'get') { + const folderId = this.getNodeParameter('folder', i) as string; + responseData = await clickupApiRequest.call(this, 'GET', `/folder/${folderId}`); + } + if (operation === 'getAll') { + const filters = this.getNodeParameter('filters', i) as IDataObject; + const spaceId = this.getNodeParameter('space', i) as string; + if (filters.archived) { + qs.archived = filters.archived as boolean; + } + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await clickupApiRequest.call(this, 'GET', `/space/${spaceId}/folder`, {}, qs); + responseData = responseData.folders; + responseData = responseData.splice(0, qs.limit); + } + if (operation === 'update') { + const folderId = this.getNodeParameter('folder', i) as string; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + const body: IDataObject = {}; + if (updateFields.name) { + body.name = updateFields.name as string; + } + responseData = await clickupApiRequest.call(this, 'PUT', `/folder/${folderId}`, body); + } + } + if (resource === 'goal') { + if (operation === 'create') { + const teamId = this.getNodeParameter('team', i) as string; + const name = this.getNodeParameter('name', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IDataObject = { + name, + }; + if (additionalFields.dueDate) { + body.due_date = new Date(additionalFields.dueDate as string).getTime(); + } + if (additionalFields.description) { + body.description = additionalFields.description as string; + } + if (additionalFields.multipleOwners) { + body.multiple_owners = additionalFields.multipleOwners as boolean; + } + if (additionalFields.color) { + body.color = additionalFields.color as string; + } + if (additionalFields.owners) { + body.owners = ((additionalFields.owners as string).split(',') as string[]).map((e: string) => parseInt(e, 10)); + } + responseData = await clickupApiRequest.call(this, 'POST', `/team/${teamId}/goal`, body); + responseData = responseData.goal; + } + if (operation === 'delete') { + const goalId = this.getNodeParameter('goal', i) as string; + responseData = await clickupApiRequest.call(this, 'DELETE', `/goal/${goalId}`); + responseData = { success: true }; + } + if (operation === 'get') { + const goalId = this.getNodeParameter('goal', i) as string; + responseData = await clickupApiRequest.call(this, 'GET', `/goal/${goalId}`); + responseData = responseData.goal; + } + if (operation === 'getAll') { + const teamId = this.getNodeParameter('team', i) as string; + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await clickupApiRequest.call(this, 'GET', `/team/${teamId}/goal`, {}, qs); + responseData = responseData.goals; + responseData = responseData.splice(0, qs.limit); + + } + if (operation === 'update') { + const goalId = this.getNodeParameter('goal', i) as string; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + const body: IDataObject = {}; + if (updateFields.name) { + body.name = updateFields.name as string; + } + if (updateFields.dueDate) { + body.due_date = new Date(updateFields.dueDate as string).getTime(); + } + if (updateFields.description) { + body.description = updateFields.description as string; + } + if (updateFields.color) { + body.color = updateFields.color as string; + } + if (updateFields.addOwners) { + body.add_owners = ((updateFields.addOwners as string).split(',') as string[]).map((e: string) => parseInt(e, 10)) as number[]; + } + if (updateFields.removeOwners) { + body.rem_owners = ((updateFields.removeOwners as string).split(',') as string[]).map((e: string) => parseInt(e, 10)) as number[]; + } + responseData = await clickupApiRequest.call(this, 'PUT', `/goal/${goalId}`, body); + responseData = responseData.goal; + } + } + if (resource === 'goalKeyResult') { + if (operation === 'create') { + const goalId = this.getNodeParameter('goal', i) as string; + const name = this.getNodeParameter('name', i) as string; + const type = this.getNodeParameter('type', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IDataObject = { + name, + type, + }; + if (additionalFields.unit) { + body.unit = additionalFields.unit as string; + } + if (additionalFields.stepsStart) { + body.steps_start = additionalFields.stepsStart as number; + } + if (additionalFields.stepsEnd) { + body.steps_end = additionalFields.stepsEnd as number; + } + if (additionalFields.taskIds) { + body.task_ids = (additionalFields.taskIds as string).split(','); + } + if (additionalFields.listIds) { + body.list_ids = (additionalFields.listIds as string).split(','); + } + if (additionalFields.owners) { + body.owners = ((additionalFields.owners as string).split(',') as string[]).map((e: string) => parseInt(e, 10)); + } + responseData = await clickupApiRequest.call(this, 'POST', `/goal/${goalId}/key_result`, body); + responseData = responseData.key_result; + } + if (operation === 'delete') { + const keyResultId = this.getNodeParameter('keyResult', i) as string; + responseData = await clickupApiRequest.call(this, 'DELETE', `/key_result/${keyResultId}`); + responseData = { success: true }; + } + if (operation === 'update') { + const keyResultId = this.getNodeParameter('keyResult', i) as string; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + const body: IDataObject = {}; + if (updateFields.name) { + body.name = updateFields.name as string; + } + if (updateFields.note) { + body.note = updateFields.note as string; + } + if (updateFields.stepsCurrent) { + body.steps_current = updateFields.stepsCurrent as number; + } + if (updateFields.stepsStart) { + body.steps_start = updateFields.stepsStart as number; + } + if (updateFields.stepsEnd) { + body.steps_end = updateFields.stepsEnd as number; + } + if (updateFields.unit) { + body.unit = updateFields.unit as string; + } + responseData = await clickupApiRequest.call(this, 'PUT', `/key_result/${keyResultId}`, body); + responseData = responseData.key_result; + } + } + if (resource === 'guest') { + if (operation === 'create') { + const teamId = this.getNodeParameter('team', i) as string; + const email = this.getNodeParameter('email', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IDataObject = { + email, + }; + if (additionalFields.canEditTags) { + body.can_edit_tags = additionalFields.canEditTags as boolean; + } + if (additionalFields.canSeeTimeSpend) { + body.can_see_time_spend = additionalFields.canSeeTimeSpend as boolean; + } + if (additionalFields.canSeeTimeEstimated) { + body.can_see_time_estimated = additionalFields.canSeeTimeEstimated as boolean; + } + responseData = await clickupApiRequest.call(this, 'POST', `/team/${teamId}/guest`, body); + responseData = responseData.team; + } + if (operation === 'delete') { + const teamId = this.getNodeParameter('team', i) as string; + const guestId = this.getNodeParameter('guest', i) as string; + responseData = await clickupApiRequest.call(this, 'DELETE', `/team/${teamId}/guest/${guestId}`); + responseData = { success: true }; + } + if (operation === 'get') { + const teamId = this.getNodeParameter('team', i) as string; + const guestId = this.getNodeParameter('guest', i) as string; + responseData = await clickupApiRequest.call(this, 'GET', `/team/${teamId}/guest/${guestId}`); + responseData = responseData.team; + } + if (operation === 'update') { + const teamId = this.getNodeParameter('team', i) as string; + const guestId = this.getNodeParameter('guest', i) as string; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + const body: IDataObject = {}; + if (updateFields.username) { + body.username = updateFields.username as string; + } + if (updateFields.canEditTags) { + body.can_edit_tags = updateFields.canEditTags as boolean; + } + if (updateFields.canSeeTimeSpend) { + body.can_see_time_spend = updateFields.canSeeTimeSpend as boolean; + } + if (updateFields.canSeeTimeEstimated) { + body.can_see_time_estimated = updateFields.canSeeTimeEstimated as boolean; + } + responseData = await clickupApiRequest.call(this, 'PUT', `/team/${teamId}/guest/${guestId}`, body); + responseData = responseData.team; + } + } if (resource === 'task') { if (operation === 'create') { const listId = this.getNodeParameter('list', i) as string; @@ -275,7 +735,12 @@ export class ClickUp implements INodeType { if (operation === 'update') { const taskId = this.getNodeParameter('id', i) as string; const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; - const body: ITask = {}; + const body: ITask = { + assignees: { + add: [], + rem: [], + }, + }; if (updateFields.content) { body.content = updateFields.content as string; } @@ -306,6 +771,17 @@ export class ClickUp implements INodeType { if (updateFields.parentId) { body.parent = updateFields.parentId as string; } + if (updateFields.addAssignees) { + //@ts-ignore + body.assignees.add = ((updateFields.addAssignees as string).split(',') as string[]).map((e: string) => parseInt(e, 10)); + } + if (updateFields.removeAssignees) { + //@ts-ignore + body.assignees.rem = ((updateFields.removeAssignees as string).split(',') as string[]).map((e: string) => parseInt(e, 10)); + } + if (updateFields.status) { + body.status = updateFields.status as string; + } if (updateFields.markdownContent) { delete body.content; body.markdown_content = updateFields.content as string; @@ -317,8 +793,7 @@ export class ClickUp implements INodeType { responseData = await clickupApiRequest.call(this, 'GET', `/task/${taskId}`); } if (operation === 'getAll') { - const returnAll = true; - //const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; const filters = this.getNodeParameter('filters', i) as IDataObject; if (filters.archived) { qs.archived = filters.archived as boolean; @@ -363,9 +838,9 @@ export class ClickUp implements INodeType { if (returnAll === true) { responseData = await clickupApiRequestAllItems.call(this, 'tasks', 'GET', `/list/${listId}/task`, {}, qs); } else { - // qs.limit = this.getNodeParameter('limit', i) as number; - // responseData = await clickupApiRequest.call(this, 'GET', `/list/${listId}/task`, {}, qs); - // responseData = responseData.tasks; + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await clickupApiRequestAllItems.call(this, 'tasks', 'GET', `/list/${listId}/task`, {}, qs); + responseData = responseData.splice(0, qs.limit); } } if (operation === 'setCustomField') { @@ -392,14 +867,184 @@ export class ClickUp implements INodeType { if (operation === 'delete') { const taskId = this.getNodeParameter('id', i) as string; responseData = await clickupApiRequest.call(this, 'DELETE', `/task/${taskId}`, {}); + responseData = { success: true }; + } + } + if (resource === 'taskDependency') { + if (operation === 'create') { + const taskId = this.getNodeParameter('task', i) as string; + const dependsOn = this.getNodeParameter('dependsOn', i) as string; + const dependencyOf = this.getNodeParameter('dependencyOf', i) as string; + if (dependencyOf !== '' && dependsOn !== '' ) { + throw new Error('Both can not be passed in the same request.'); + } + const body: IDataObject = {}; + if (dependsOn) { + body.depends_on = dependsOn; + } + if (dependencyOf) { + body.dependency_of = dependencyOf; + } + responseData = await clickupApiRequest.call(this, 'POST', `/task/${taskId}/dependency`, body); + responseData = { success: true }; + } + if (operation === 'delete') { + const taskId = this.getNodeParameter('task', i) as string; + const dependsOn = this.getNodeParameter('dependsOn', i) as string; + const dependencyOf = this.getNodeParameter('dependencyOf', i) as string; + if (dependencyOf !== '' && dependsOn !== '' ) { + throw new Error('Both can not be passed in the same request.'); + } + if (dependsOn) { + qs.depends_on = dependsOn; + } + if (dependencyOf) { + qs.dependency_of = dependencyOf; + } + responseData = await clickupApiRequest.call(this, 'DELETE', `/task/${taskId}/dependency`, {}, qs); + responseData = { success: true }; + } + } + if (resource === 'timeTracking') { + if (operation === 'log') { + const taskId = this.getNodeParameter('task', i) as string; + const type = this.getNodeParameter('type', i) as string; + const body: IDataObject = {}; + if (type === 'fromTo') { + const from = new Date(this.getNodeParameter('from', i) as string).getTime(); + const to = new Date(this.getNodeParameter('to', i) as string).getTime(); + body.from = from; + body.to = to; + } else { + const minutes = this.getNodeParameter('minutes', i) as number; + body.time = minutes * 60000; + } + responseData = await clickupApiRequest.call(this, 'POST', `/task/${taskId}/time`, body); + } + if (operation === 'delete') { + const taskId = this.getNodeParameter('task', i) as string; + const intervalId = this.getNodeParameter('interval', i) as string; + responseData = await clickupApiRequest.call(this, 'DELETE', `/task/${taskId}/time/${intervalId}`); + responseData = { success: true }; + } + if (operation === 'getAll') { + const taskId = this.getNodeParameter('task', i) as string; + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await clickupApiRequest.call(this, 'GET', `/task/${taskId}/time`, {}, qs); + responseData = responseData.data; + responseData = responseData.splice(0, qs.limit); + } + if (operation === 'update') { + const taskId = this.getNodeParameter('task', i) as string; + const intervalId = this.getNodeParameter('interval', i) as string; + const type = this.getNodeParameter('type', i) as string; + const body: IDataObject = {}; + if (type === 'fromTo') { + const from = new Date(this.getNodeParameter('from', i) as string).getTime(); + const to = new Date(this.getNodeParameter('to', i) as string).getTime(); + body.from = from; + body.to = to; + } else { + const minutes = this.getNodeParameter('minutes', i) as number; + body.time = minutes * 60000; + } + responseData = await clickupApiRequest.call(this, 'PUT', `/task/${taskId}/time/${intervalId}`, body); + responseData = { success: true }; } } if (resource === 'list') { + if (operation === 'create') { + const spaceId = this.getNodeParameter('space', i) as string; + const folderless = this.getNodeParameter('folderless', i) as string; + const name = this.getNodeParameter('name', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IList = { + name, + }; + if (additionalFields.content) { + body.content = additionalFields.content as string; + } + 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.priority) { + body.priority = additionalFields.priority as number; + } + if (additionalFields.assignee) { + body.assignee = parseInt(additionalFields.assignee as string, 10); + } + if (additionalFields.status) { + body.status = additionalFields.status as string; + } + if (folderless) { + responseData = await clickupApiRequest.call(this, 'POST', `/space/${spaceId}/list`, body); + } else { + const folderId = this.getNodeParameter('folder', i) as string; + responseData = await clickupApiRequest.call(this, 'POST', `/folder/${folderId}/list`, body); + } + } if (operation === 'customFields') { const listId = this.getNodeParameter('list', i) as string; responseData = await clickupApiRequest.call(this, 'GET', `/list/${listId}/field`); responseData = responseData.fields; } + if (operation === 'delete') { + const listId = this.getNodeParameter('list', i) as string; + responseData = await clickupApiRequest.call(this, 'DELETE', `/list/${listId}`); + responseData = { success: true }; + } + if (operation === 'get') { + const listId = this.getNodeParameter('list', i) as string; + responseData = await clickupApiRequest.call(this, 'GET', `/list/${listId}`); + } + if (operation === 'getAll') { + const filters = this.getNodeParameter('filters', i) as IDataObject; + const spaceId = this.getNodeParameter('space', i) as string; + const folderless = this.getNodeParameter('folderless', i) as boolean; + if (filters.archived) { + qs.archived = filters.archived as boolean; + } + let endpoint = `/space/${spaceId}/list`; + if (!folderless) { + const folderId = this.getNodeParameter('folder', i) as string; + endpoint = `/folder/${folderId}/list`; + } + + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await clickupApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.lists; + responseData = responseData.splice(0, qs.limit); + } + if (operation === 'update') { + const listId = this.getNodeParameter('list', i) as string; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + const body: IList = {}; + if (updateFields.name) { + body.name = updateFields.name as string; + } + if (updateFields.content) { + body.content = updateFields.content as string; + } + 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.priority) { + body.priority = updateFields.priority as number; + } + if (updateFields.assignee) { + body.assignee = parseInt(updateFields.assignee as string, 10); + } + if (updateFields.unsetStatus) { + body.unset_status = updateFields.unsetStatus as boolean; + } + responseData = await clickupApiRequest.call(this, 'PUT', `/list/${listId}`, body); + } } if (Array.isArray(responseData)) { returnData.push.apply(returnData, responseData as IDataObject[]); diff --git a/packages/nodes-base/nodes/ClickUp/CommentDescription.ts b/packages/nodes-base/nodes/ClickUp/CommentDescription.ts new file mode 100644 index 0000000000..a8ef00d2f4 --- /dev/null +++ b/packages/nodes-base/nodes/ClickUp/CommentDescription.ts @@ -0,0 +1,292 @@ +import { + INodeProperties, + } from 'n8n-workflow'; + +export const commentOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'comment', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a comment', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a comment', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all comments', + }, + { + name: 'Update', + value: 'update', + description: 'Update a comment', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const commentFields = [ + +/* -------------------------------------------------------------------------- */ +/* comment:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Comment On', + name: 'commentOn', + type: 'options', + options: [ + { + name: 'List', + value: 'list', + }, + { + name: 'Task', + value: 'task', + }, + { + name: 'View', + value: 'view', + }, + ], + default: '', + displayOptions: { + show: { + resource: [ + 'comment', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'comment', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + }, + { + displayName: 'Comment Text', + name: 'commentText', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'comment', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'comment', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Assignee ID', + name: 'assignee', + type: 'string', + default: '', + }, + { + displayName: 'Notify All', + name: 'notifyAll', + type: 'boolean', + default: false, + description: 'If true, creation notifications will be sent to everyone including the creator of the comment.', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* comment:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Comment ID', + name: 'comment', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'comment', + ], + operation: [ + 'delete', + ], + }, + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* comment:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Comments On', + name: 'commentsOn', + type: 'options', + options: [ + { + name: 'List', + value: 'list', + }, + { + name: 'Task', + value: 'task', + }, + { + name: 'View', + value: 'view', + }, + ], + default: '', + displayOptions: { + show: { + resource: [ + 'comment', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'comment', + ], + operation: [ + 'getAll', + ], + }, + }, + required: true, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'comment', + ], + operation: [ + 'getAll', + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, +/* -------------------------------------------------------------------------- */ +/* comment:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Comment ID', + name: 'comment', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'comment', + ], + operation: [ + 'update', + ], + }, + }, + required: true, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'comment', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Assignee ID', + name: 'assignee', + type: 'string', + default: '', + }, + { + displayName: 'Comment Text', + name: 'commentText', + type: 'string', + default: '', + }, + { + displayName: 'Resolved', + name: 'resolved', + type: 'boolean', + default: false, + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ClickUp/FolderDescription.ts b/packages/nodes-base/nodes/ClickUp/FolderDescription.ts new file mode 100644 index 0000000000..af9803c13e --- /dev/null +++ b/packages/nodes-base/nodes/ClickUp/FolderDescription.ts @@ -0,0 +1,438 @@ +import { + INodeProperties, + } from 'n8n-workflow'; + +export const folderOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'folder', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a folder', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a folder', + }, + { + name: 'Get', + value: 'get', + description: 'Get a folder', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all folders', + }, + { + name: 'Update', + value: 'update', + description: 'Update a folder', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const folderFields = [ + +/* -------------------------------------------------------------------------- */ +/* folder:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'create', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Space ID', + name: 'space', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'create', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getSpaces', + loadOptionsDependsOn: [ + 'team', + ] + }, + required: true, + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* folder:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'delete', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Space ID', + name: 'space', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'delete', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getSpaces', + loadOptionsDependsOn: [ + 'team', + ] + }, + required: true, + }, + { + displayName: 'Folder ID', + name: 'folder', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'delete', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getFolders', + loadOptionsDependsOn: [ + 'space', + ], + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* folder:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'get', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Space ID', + name: 'space', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'get', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getSpaces', + loadOptionsDependsOn: [ + 'team', + ] + }, + required: true, + }, + { + displayName: 'Folder ID', + name: 'folder', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'get', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getFolders', + loadOptionsDependsOn: [ + 'space', + ], + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* folder:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'getAll', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Space ID', + name: 'space', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'getAll', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getSpaces', + loadOptionsDependsOn: [ + 'team', + ] + }, + required: true, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'getAll', + ], + }, + }, + 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: { + resource: [ + 'folder', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Archived', + name: 'archived', + type: 'boolean', + default: false, + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* folder:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'update', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Space ID', + name: 'space', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'update', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getSpaces', + loadOptionsDependsOn: [ + 'team', + ] + }, + required: true, + }, + { + displayName: 'Folder ID', + name: 'folder', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'update', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getFolders', + loadOptionsDependsOn: [ + 'space', + ], + }, + required: true, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'folder', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts b/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts index 5ef1238b84..d836f7798e 100644 --- a/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts +++ b/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts @@ -1,4 +1,7 @@ -import { OptionsWithUri } from 'request'; +import { + OptionsWithUri, + } from 'request'; + import { IExecuteFunctions, IExecuteSingleFunctions, @@ -6,7 +9,10 @@ import { ILoadOptionsFunctions, IWebhookFunctions, } from 'n8n-core'; -import { IDataObject } from 'n8n-workflow'; + +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'); @@ -42,12 +48,14 @@ export async function clickupApiRequestAllItems(this: IHookFunctions | IExecuteF let responseData; query.page = 0; - query.limit = 100; do { responseData = await clickupApiRequest.call(this, method, resource, body, query); returnData.push.apply(returnData, responseData[propertyName]); query.page++; + if (query.limit && query.limit <= returnData.length) { + return returnData; + } } while ( responseData[propertyName] && responseData[propertyName].length !== 0 diff --git a/packages/nodes-base/nodes/ClickUp/GoalDescription.ts b/packages/nodes-base/nodes/ClickUp/GoalDescription.ts new file mode 100644 index 0000000000..06e0463f6c --- /dev/null +++ b/packages/nodes-base/nodes/ClickUp/GoalDescription.ts @@ -0,0 +1,305 @@ +import { + INodeProperties, + } from 'n8n-workflow'; + +export const goalOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'goal', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a goal', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a goal', + }, + { + name: 'Get', + value: 'get', + description: 'Get a goal', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all goals', + }, + { + name: 'Update', + value: 'update', + description: 'Update a goal', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const goalFields = [ + +/* -------------------------------------------------------------------------- */ +/* goal:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'goal', + ], + operation: [ + 'create', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'goal', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'goal', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Color', + name: 'color', + type: 'color', + default: '', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Due Date', + name: 'dueDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Multiple Owners', + name: 'multipleOwners', + type: 'boolean', + default: false, + }, + { + displayName: 'Owners', + name: 'owners', + type: 'string', + default: '', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* goal:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Goal ID', + name: 'goal', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'goal', + ], + operation: [ + 'delete', + ], + }, + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* goal:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Goal ID', + name: 'goal', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'goal', + ], + operation: [ + 'get', + ], + }, + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* goal:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'goal', + ], + operation: [ + 'getAll', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'goal', + ], + operation: [ + 'getAll', + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, +/* -------------------------------------------------------------------------- */ +/* goal:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Goal ID', + name: 'goal', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'goal', + ], + operation: [ + 'update', + ], + }, + }, + required: true, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'goal', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Add Owners', + name: 'addOwners', + type: 'string', + default: '', + }, + { + displayName: 'Color', + name: 'color', + type: 'color', + default: '', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Due Date', + name: 'dueDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Remove Owners', + name: 'removeOwners', + type: 'string', + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ClickUp/GoalKeyResultDescription.ts b/packages/nodes-base/nodes/ClickUp/GoalKeyResultDescription.ts new file mode 100644 index 0000000000..41cb29cb17 --- /dev/null +++ b/packages/nodes-base/nodes/ClickUp/GoalKeyResultDescription.ts @@ -0,0 +1,289 @@ +import { + INodeProperties, + } from 'n8n-workflow'; + +export const goalKeyResultOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'goalKeyResult', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a key result', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a key result', + }, + { + name: 'Update', + value: 'update', + description: 'Update a key result', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const goalKeyResultFields = [ + +/* -------------------------------------------------------------------------- */ +/* goalKeyResult:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Goal ID', + name: 'goal', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'goalKeyResult', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'goalKeyResult', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Automatic', + value: 'automatic', + }, + { + name: 'Boolean', + value: 'boolean', + }, + { + name: 'Currency', + value: 'currency', + }, + { + name: 'Number', + value: 'number', + }, + { + name: 'Percentage', + value: 'percentage', + }, + ], + default: '', + displayOptions: { + show: { + resource: [ + 'goalKeyResult', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'goalKeyResult', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'List IDs', + name: 'listIds', + type: 'string', + default: '', + }, + { + displayName: 'Owners', + name: 'owners', + type: 'string', + default: '', + }, + { + displayName: 'Steps Start', + name: 'stepsStart', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + description: 'Required for Percentage, Automatic (when Task IDs or List IDs are filled), Number and Currency', + }, + { + displayName: 'Steps End', + name: 'stepsEnd', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + description: 'Required for Percentage, Automatic (when Task IDs or List IDs are filled), Number and Currency', + }, + { + displayName: 'Task IDs', + name: 'taskIds', + type: 'string', + default: '', + }, + { + displayName: 'Unit', + name: 'unit', + type: 'string', + default: '', + description: `Only matters for type Number and Currency. For Currency the unit must be a valid currency code.`, + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* goalKeyResult:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Key Result ID', + name: 'keyResult', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'goalKeyResult', + ], + operation: [ + 'delete', + ], + }, + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* goalKeyResult:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Key Result ID', + name: 'keyResult', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'goalKeyResult', + ], + operation: [ + 'update', + ], + }, + }, + required: true, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'goalKeyResult', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Note', + name: 'note', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Steps Current', + name: 'stepsCurrent', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Steps End', + name: 'stepsEnd', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Steps Start', + name: 'stepsStart', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Unit', + name: 'unit', + type: 'string', + default: '', + description: `Only matters for type Number and Currency. For Currency the unit must be a valid currency code.`, + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ClickUp/GuestDescription.ts b/packages/nodes-base/nodes/ClickUp/GuestDescription.ts new file mode 100644 index 0000000000..2f6ebd6da1 --- /dev/null +++ b/packages/nodes-base/nodes/ClickUp/GuestDescription.ts @@ -0,0 +1,285 @@ +import { + INodeProperties, + } from 'n8n-workflow'; + +export const guestOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'guest', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a guest', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a guest', + }, + { + name: 'Get', + value: 'get', + description: 'Get a guest', + }, + { + name: 'Update', + value: 'update', + description: 'Update a guest', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const guestFields = [ + +/* -------------------------------------------------------------------------- */ +/* guest:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'guest', + ], + operation: [ + 'create', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'guest', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'guest', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Can Edit Tags', + name: 'can_edit_tags', + type: 'boolean', + default: false, + }, + { + displayName: 'Can See Time Spend', + name: 'can_see_time_spend', + type: 'boolean', + default: false, + }, + { + displayName: 'Can See Time estimated', + name: 'can_see_time_estimated', + type: 'boolean', + default: false, + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* guest:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'guest', + ], + operation: [ + 'delete', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Guest ID', + name: 'guest', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'guest', + ], + operation: [ + 'delete', + ], + }, + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* guest:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'guest', + ], + operation: [ + 'get', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Guest ID', + name: 'guest', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'guest', + ], + operation: [ + 'get', + ], + }, + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* guest:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'guest', + ], + operation: [ + 'update', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Guest ID', + name: 'guest', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'guest', + ], + operation: [ + 'update', + ], + }, + }, + required: true, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'guest', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Can Edit Tags', + name: 'can_edit_tags', + type: 'boolean', + default: false, + }, + { + displayName: 'Can See Time Spend', + name: 'can_see_time_spend', + type: 'boolean', + default: false, + }, + { + displayName: 'Can See Time estimated', + name: 'can_see_time_estimated', + type: 'boolean', + default: false, + }, + { + displayName: 'Username', + name: 'username', + type: 'string', + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ClickUp/ListDescription.ts b/packages/nodes-base/nodes/ClickUp/ListDescription.ts index 517d8497f2..8fd4a8a13f 100644 --- a/packages/nodes-base/nodes/ClickUp/ListDescription.ts +++ b/packages/nodes-base/nodes/ClickUp/ListDescription.ts @@ -1,4 +1,6 @@ -import { INodeProperties } from 'n8n-workflow'; +import { + INodeProperties, + } from 'n8n-workflow'; export const listOperations = [ { @@ -13,11 +15,36 @@ export const listOperations = [ }, }, options: [ + { + name: 'Create', + value: 'create', + description: 'Create a list', + }, { name: 'Custom Fields', value: 'customFields', description: `Retrieve list's custom fields`, }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a list', + }, + { + name: 'Get', + value: 'get', + description: 'Get a list', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all lists', + }, + { + name: 'Update', + value: 'update', + description: 'Update a list', + }, ], default: 'customFields', description: 'The operation to perform.', @@ -26,6 +53,181 @@ export const listOperations = [ export const listFields = [ +/* -------------------------------------------------------------------------- */ +/* list:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'create', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Space ID', + name: 'space', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'create', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getSpaces', + loadOptionsDependsOn: [ + 'team', + ] + }, + required: true, + }, + { + displayName: 'Folderless List', + name: 'folderless', + type: 'boolean', + default: false, + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + }, + { + displayName: 'Folder ID', + name: 'folder', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'create', + ], + folderless: [ + false, + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getFolders', + loadOptionsDependsOn: [ + 'space', + ], + }, + required: true, + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Assignee', + name: 'assignee', + type: 'string', + default: '', + }, + { + displayName: 'Content', + name: 'content', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Due Date', + name: 'dueDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Due Date Time', + name: 'dueDateTime', + type: 'boolean', + default: false, + }, + { + displayName: 'Priority', + name: 'priority', + type: 'number', + typeOptions: { + minValue: 1, + maxValue: 4, + }, + description: 'Integer mapping as 1 : Urgent, 2 : High, 3 : Normal, 4 : Low', + default: 3, + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + loadOptionsDependsOn: [ + 'list', + ], + typeOptions: { + loadOptionsMethod: 'getStatuses', + }, + default: '', + }, + ], + }, /* -------------------------------------------------------------------------- */ /* list:customFields */ /* -------------------------------------------------------------------------- */ @@ -67,7 +269,7 @@ export const listFields = [ typeOptions: { loadOptionsMethod: 'getSpaces', loadOptionsDependsOn: [ - 'team', + 'teamId', ] }, required: true, @@ -167,4 +369,533 @@ export const listFields = [ }, required: true, }, +/* -------------------------------------------------------------------------- */ +/* list:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'delete', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Space ID', + name: 'space', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'delete', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getSpaces', + loadOptionsDependsOn: [ + 'team', + ] + }, + required: true, + }, + { + displayName: 'Folderless List', + name: 'folderless', + type: 'boolean', + default: false, + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'delete', + ], + }, + }, + required: true, + }, + { + displayName: 'Folder ID', + name: 'folder', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'delete', + ], + folderless: [ + false, + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getFolders', + loadOptionsDependsOn: [ + 'space', + ], + }, + required: true, + }, + { + displayName: 'List ID', + name: 'list', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'delete', + ], + }, + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* list:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'get', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Space ID', + name: 'space', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'get', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getSpaces', + loadOptionsDependsOn: [ + 'team', + ] + }, + required: true, + }, + { + displayName: 'Folderless List', + name: 'folderless', + type: 'boolean', + default: false, + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'get', + ], + }, + }, + required: true, + }, + { + displayName: 'Folder ID', + name: 'folder', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'get', + ], + folderless: [ + false, + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getFolders', + loadOptionsDependsOn: [ + 'space', + ], + }, + required: true, + }, + { + displayName: 'List ID', + name: 'list', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'get', + ], + }, + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* list:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'getAll', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Space ID', + name: 'space', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'getAll', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getSpaces', + loadOptionsDependsOn: [ + 'team', + ] + }, + required: true, + }, + { + displayName: 'Folderless List', + name: 'folderless', + type: 'boolean', + default: false, + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'getAll', + ], + }, + }, + required: true, + }, + { + displayName: 'Folder ID', + name: 'folder', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'getAll', + ], + folderless: [ + false, + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getFolders', + loadOptionsDependsOn: [ + 'space', + ], + }, + required: true, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'getAll', + ], + }, + }, + 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: { + resource: [ + 'list', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Archived', + name: 'archived', + type: 'boolean', + default: false, + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* list:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'team', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'update', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + required: true, + }, + { + displayName: 'Space ID', + name: 'space', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'update', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getSpaces', + loadOptionsDependsOn: [ + 'team', + ] + }, + required: true, + }, + { + displayName: 'Folderless List', + name: 'folderless', + type: 'boolean', + default: false, + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'update', + ], + }, + }, + required: true, + }, + { + displayName: 'Folder ID', + name: 'folder', + type: 'options', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'update', + ], + folderless: [ + false, + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getFolders', + loadOptionsDependsOn: [ + 'space', + ], + }, + required: true, + }, + { + displayName: 'List ID', + name: 'list', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'update', + ], + }, + }, + required: true, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'list', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Assignee', + name: 'assignee', + type: 'options', + loadOptionsDependsOn: [ + 'list', + ], + typeOptions: { + loadOptionsMethod: 'getAssignees', + }, + + default: '', + }, + { + displayName: 'Content', + name: 'content', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Due Date', + name: 'dueDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Due Date Time', + name: 'dueDateTime', + type: 'boolean', + default: false, + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Priority', + name: 'priority', + type: 'number', + typeOptions: { + minValue: 1, + maxValue: 4, + }, + description: 'Integer mapping as 1 : Urgent, 2 : High, 3 : Normal, 4 : Low', + default: 3, + }, + { + displayName: 'Unset Status', + name: 'unsetStatus', + type: 'boolean', + default: false, + }, + ], + }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ClickUp/ListInterface.ts b/packages/nodes-base/nodes/ClickUp/ListInterface.ts new file mode 100644 index 0000000000..d8a6db8157 --- /dev/null +++ b/packages/nodes-base/nodes/ClickUp/ListInterface.ts @@ -0,0 +1,11 @@ + +export interface IList { + name?: string; + content?: string; + assignee?: number; + status?: string; + priority?: number; + due_date?: number; + due_date_time?: boolean; + unset_status?: boolean; +} diff --git a/packages/nodes-base/nodes/ClickUp/TaskDependencyDescription.ts b/packages/nodes-base/nodes/ClickUp/TaskDependencyDescription.ts new file mode 100644 index 0000000000..8150394f8c --- /dev/null +++ b/packages/nodes-base/nodes/ClickUp/TaskDependencyDescription.ts @@ -0,0 +1,140 @@ +import { + INodeProperties, + } from 'n8n-workflow'; + +export const taskDependencyOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'taskDependency', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a task dependency', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a task dependency', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const taskDependencyFields = [ + +/* -------------------------------------------------------------------------- */ +/* taskDependency:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Task ID', + name: 'task', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'taskDependency', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + }, + { + displayName: 'Depends On', + name: 'dependsOn', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'taskDependency', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Dependency Of', + name: 'dependencyOf', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'taskDependency', + ], + operation: [ + 'create', + ], + }, + }, + }, +/* -------------------------------------------------------------------------- */ +/* taskDependency:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Task ID', + name: 'task', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'taskDependency', + ], + operation: [ + 'delete', + ], + }, + }, + required: true, + }, + { + displayName: 'Depends On', + name: 'dependsOn', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'taskDependency', + ], + operation: [ + 'delete', + ], + }, + }, + }, + { + displayName: 'Dependency Of', + name: 'dependencyOf', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'taskDependency', + ], + operation: [ + 'delete', + ], + }, + }, + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ClickUp/TaskDescription.ts b/packages/nodes-base/nodes/ClickUp/TaskDescription.ts index 77a12ed07a..5d53390c1c 100644 --- a/packages/nodes-base/nodes/ClickUp/TaskDescription.ts +++ b/packages/nodes-base/nodes/ClickUp/TaskDescription.ts @@ -1,4 +1,6 @@ -import { INodeProperties } from 'n8n-workflow'; +import { + INodeProperties, + } from 'n8n-workflow'; export const taskOperations = [ { @@ -55,7 +57,7 @@ export const taskFields = [ /* task:create */ /* -------------------------------------------------------------------------- */ { - displayName: 'Team', + displayName: 'Team ID', name: 'team', type: 'options', default: '', @@ -75,7 +77,7 @@ export const taskFields = [ required: true, }, { - displayName: 'Space', + displayName: 'Space ID', name: 'space', type: 'options', default: '', @@ -115,7 +117,7 @@ export const taskFields = [ required: true, }, { - displayName: 'Folder', + displayName: 'Folder ID', name: 'folder', type: 'options', default: '', @@ -141,7 +143,7 @@ export const taskFields = [ required: true, }, { - displayName: 'List', + displayName: 'List ID', name: 'list', type: 'options', default: '', @@ -167,7 +169,7 @@ export const taskFields = [ required: true, }, { - displayName: 'List', + displayName: 'List ID', name: 'list', type: 'options', default: '', @@ -378,6 +380,13 @@ export const taskFields = [ }, }, options: [ + { + displayName: 'Add Assignees', + name: 'addAssignees', + type: 'string', + default: '', + description: 'Assignees IDs. Multiple ca be added separated by comma' + }, { displayName: 'Content', name: 'content', @@ -434,6 +443,20 @@ export const taskFields = [ description: 'Integer mapping as 1 : Urgent, 2 : High, 3 : Normal, 4 : Low', default: 3, }, + { + displayName: 'Remove Assignees', + name: 'removeAssignees', + type: 'string', + default: '', + description: 'Assignees IDs. Multiple ca be added separated by comma' + }, + { + displayName: 'Status', + name: 'status', + type: 'string', + default: '', + description: 'status' + }, { displayName: 'Start Date Time', name: 'startDateTime', @@ -475,7 +498,7 @@ export const taskFields = [ /* task:getAll */ /* -------------------------------------------------------------------------- */ { - displayName: 'Team', + displayName: 'Team ID', name: 'team', type: 'options', default: '', @@ -495,7 +518,7 @@ export const taskFields = [ required: true, }, { - displayName: 'Space', + displayName: 'Space ID', name: 'space', type: 'options', default: '', @@ -535,7 +558,7 @@ export const taskFields = [ required: true, }, { - displayName: 'Folder', + displayName: 'Folder ID', name: 'folder', type: 'options', default: '', @@ -561,7 +584,7 @@ export const taskFields = [ required: true, }, { - displayName: 'List', + displayName: 'List ID', name: 'list', type: 'options', default: '', @@ -587,7 +610,7 @@ export const taskFields = [ required: true, }, { - displayName: 'List', + displayName: 'List ID', name: 'list', type: 'options', default: '', @@ -612,47 +635,47 @@ export const taskFields = [ }, required: true, }, - // { - // displayName: 'Return All', - // name: 'returnAll', - // type: 'boolean', - // displayOptions: { - // show: { - // resource: [ - // 'task', - // ], - // operation: [ - // 'getAll', - // ], - // }, - // }, - // default: true, - // description: 'If all results should be returned or only up to a given limit.', - // }, - // { - // displayName: 'Limit', - // name: 'limit', - // type: 'number', - // displayOptions: { - // show: { - // resource: [ - // 'task', - // ], - // operation: [ - // 'getAll', - // ], - // returnAll: [ - // false, - // ], - // }, - // }, - // typeOptions: { - // minValue: 1, - // maxValue: 100, - // }, - // default: 50, - // description: 'How many results to return.', - // }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'getAll', + ], + }, + }, + default: true, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, { displayName: 'Filters', name: 'filters', @@ -800,7 +823,7 @@ export const taskFields = [ /* task:delete */ /* -------------------------------------------------------------------------- */ { - displayName: 'ID', + displayName: 'Task ID', name: 'id', type: 'string', default: '', diff --git a/packages/nodes-base/nodes/ClickUp/TaskInterface.ts b/packages/nodes-base/nodes/ClickUp/TaskInterface.ts index 44a61805f5..a24f9b2851 100644 --- a/packages/nodes-base/nodes/ClickUp/TaskInterface.ts +++ b/packages/nodes-base/nodes/ClickUp/TaskInterface.ts @@ -1,9 +1,11 @@ -import { IDataObject } from "n8n-workflow"; +import { + IDataObject + } from 'n8n-workflow'; export interface ITask { name?: string; content?: string; - assignees?: string[]; + assignees?: string[] | IDataObject; tags?: string[]; status?: string; priority?: number; diff --git a/packages/nodes-base/nodes/ClickUp/TimeTrackingDescription.ts b/packages/nodes-base/nodes/ClickUp/TimeTrackingDescription.ts new file mode 100644 index 0000000000..8f76b77a26 --- /dev/null +++ b/packages/nodes-base/nodes/ClickUp/TimeTrackingDescription.ts @@ -0,0 +1,359 @@ +import { + INodeProperties, + } from 'n8n-workflow'; + +export const timeTrackingOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + }, + }, + options: [ + { + name: 'Log', + value: 'log', + description: 'Log time on task', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a logged time', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all loggin times on task', + }, + { + name: 'Update', + value: 'update', + description: 'Update a logged time', + }, + ], + default: 'log', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const timeTrackingFields = [ + +/* -------------------------------------------------------------------------- */ +/* timeTracking:log */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Task ID', + name: 'task', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + operation: [ + 'log', + ], + }, + }, + required: true, + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + default: '', + options: [ + { + name: 'Duration', + value: 'duration', + }, + { + name: 'From/To', + value: 'fromTo', + }, + ], + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + operation: [ + 'log', + ], + }, + }, + }, + { + displayName: 'Minutes', + name: 'minutes', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + operation: [ + 'log', + ], + type: [ + 'duration', + ], + }, + }, + required: true, + }, + { + displayName: 'From', + name: 'from', + type: 'dateTime', + default: '', + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + operation: [ + 'log', + ], + type: [ + 'fromTo', + ], + }, + }, + required: true, + }, + { + displayName: 'To', + name: 'to', + type: 'dateTime', + default: '', + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + operation: [ + 'log', + ], + type: [ + 'fromTo', + ], + }, + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* timeTracking:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Task ID', + name: 'task', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + operation: [ + 'delete', + ], + }, + }, + required: true, + }, + { + displayName: 'Interval ID', + name: 'interval', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + operation: [ + 'delete', + ], + }, + }, + required: true, + }, +/* -------------------------------------------------------------------------- */ +/* timeTracking:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Task ID', + name: 'task', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + operation: [ + 'getAll', + ], + }, + }, + required: true, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + operation: [ + 'getAll', + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, +/* -------------------------------------------------------------------------- */ +/* timeTracking:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Task ID', + name: 'task', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + operation: [ + 'update', + ], + }, + }, + required: true, + }, + { + displayName: 'Interval ID', + name: 'interval', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + operation: [ + 'update', + ], + }, + }, + required: true, + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + default: '', + options: [ + { + name: 'Duration', + value: 'duration', + }, + { + name: 'From/To', + value: 'fromTo', + }, + ], + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Minutes', + name: 'minutes', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + operation: [ + 'update', + ], + type: [ + 'duration', + ], + }, + }, + required: true, + }, + { + displayName: 'From', + name: 'from', + type: 'dateTime', + default: '', + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + operation: [ + 'update', + ], + type: [ + 'fromTo', + ], + }, + }, + required: true, + }, + { + displayName: 'To', + name: 'to', + type: 'dateTime', + default: '', + displayOptions: { + show: { + resource: [ + 'timeTracking', + ], + operation: [ + 'update', + ], + type: [ + 'fromTo', + ], + }, + }, + required: true, + }, +] as INodeProperties[];