diff --git a/packages/nodes-base/credentials/TodoistApi.credentials.ts b/packages/nodes-base/credentials/TodoistApi.credentials.ts index bbcf09f2ff..45b98d6a59 100644 --- a/packages/nodes-base/credentials/TodoistApi.credentials.ts +++ b/packages/nodes-base/credentials/TodoistApi.credentials.ts @@ -1,4 +1,6 @@ import { + IAuthenticateBearer, + ICredentialTestRequest, ICredentialType, INodeProperties, } from 'n8n-workflow'; @@ -16,4 +18,18 @@ export class TodoistApi implements ICredentialType { default: '', }, ]; + + authenticate = { + type: 'bearer', + properties: { + tokenPropertyName: 'apiKey', + }, + } as IAuthenticateBearer; + + test: ICredentialTestRequest = { + request: { + baseURL: 'https://api.todoist.com/rest/v1', + url: '/labels', + }, + }; } diff --git a/packages/nodes-base/nodes/NextCloud/GenericFunctions.ts b/packages/nodes-base/nodes/NextCloud/GenericFunctions.ts index ff8226e375..443f67cb16 100644 --- a/packages/nodes-base/nodes/NextCloud/GenericFunctions.ts +++ b/packages/nodes-base/nodes/NextCloud/GenericFunctions.ts @@ -34,7 +34,7 @@ export async function nextCloudApiRequest(this: IHookFunctions | IExecuteFunctio credentials = await this.getCredentials('nextCloudOAuth2Api') as { webDavUrl: string }; } - let options: OptionsWithUri = { + const options: OptionsWithUri = { headers, method, body, diff --git a/packages/nodes-base/nodes/Todoist/GenericFunctions.ts b/packages/nodes-base/nodes/Todoist/GenericFunctions.ts index aba3afa25d..37644ca2ce 100644 --- a/packages/nodes-base/nodes/Todoist/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Todoist/GenericFunctions.ts @@ -28,12 +28,11 @@ export async function todoistApiRequest( body: any = {}, // tslint:disable-line:no-any qs: IDataObject = {}, ): Promise { // tslint:disable-line:no-any - const authentication = this.getNodeParameter('authentication', 0, 'apiKey'); + const authentication = this.getNodeParameter('authentication', 0) as string; const endpoint = 'api.todoist.com/rest/v1'; const options: OptionsWithUri = { - headers: {}, method, qs, uri: `https://${endpoint}${resource}`, @@ -45,16 +44,8 @@ export async function todoistApiRequest( } try { - if (authentication === 'apiKey') { - const credentials = await this.getCredentials('todoistApi'); - - //@ts-ignore - options.headers['Authorization'] = `Bearer ${credentials.apiKey}`; - return this.helpers.request!(options); - } else { - //@ts-ignore - return await this.helpers.requestOAuth2.call(this, 'todoistOAuth2Api', options); - } + const credentialType = authentication === 'apiKey' ? 'todoistApi' : 'todoistOAuth2Api'; + return await this.helpers.requestWithAuthentication.call(this, credentialType, options); } catch (error) { throw new NodeApiError(this.getNode(), error); diff --git a/packages/nodes-base/nodes/Todoist/Todoist.node.ts b/packages/nodes-base/nodes/Todoist/Todoist.node.ts index d7a5fcd55a..3f167d3bf9 100644 --- a/packages/nodes-base/nodes/Todoist/Todoist.node.ts +++ b/packages/nodes-base/nodes/Todoist/Todoist.node.ts @@ -21,7 +21,7 @@ interface IBodyCreateTask { description?: string; project_id?: number; section_id?: number; - parent?: number; + parent_id?: number; order?: number; label_ids?: number[]; priority?: number; @@ -116,16 +116,16 @@ export class Todoist implements INodeType { }, }, options: [ - { - name: 'Create', - value: 'create', - description: 'Create a new task', - }, { name: 'Close', value: 'close', description: 'Close a task', }, + { + name: 'Create', + value: 'create', + description: 'Create a new task', + }, { name: 'Delete', value: 'delete', @@ -192,6 +192,7 @@ export class Todoist implements INodeType { }, }, default: [], + description: 'Optional labels that will be assigned to a created task', }, { displayName: 'Content', @@ -266,6 +267,13 @@ export class Todoist implements INodeType { default: '', description: 'Specific date and time in RFC3339 format in UTC', }, + { + displayName: 'Due String Locale', + name: 'dueLang', + type: 'string', + default: '', + description: '2-letter code specifying language in case due_string is not written in English', + }, { displayName: 'Due String', name: 'dueString', @@ -274,11 +282,18 @@ export class Todoist implements INodeType { description: 'Human defined task due date (ex.: “next Monday”, “Tomorrow”). Value is set using local (not UTC) time.', }, { - displayName: 'Due String Locale', - name: 'dueLang', - type: 'string', - default: '', - description: '2-letter code specifying language in case due_string is not written in English', + displayName: 'Parent ID', + name: 'parentId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getItems', + loadOptionsDependsOn: [ + 'project', + 'options.section', + ], + }, + default: {}, + description: 'The parent task you want to operate on', }, { displayName: 'Priority', @@ -292,7 +307,7 @@ export class Todoist implements INodeType { description: 'Task priority from 1 (normal) to 4 (urgent)', }, { - displayName: 'Section', + displayName: 'Section ID', name: 'section', type: 'options', typeOptions: { @@ -344,7 +359,7 @@ export class Todoist implements INodeType { minValue: 1, maxValue: 500, }, - default: 100, + default: 50, description: 'Max number of results to return', }, { @@ -395,6 +410,20 @@ export class Todoist implements INodeType { default: '', description: 'IETF language tag defining what language filter is written in, if differs from default English', }, + { + displayName: 'Parent ID', + name: 'parentId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getItems', + loadOptionsDependsOn: [ + 'filters.projectId', + 'filters.sectionId', + ], + }, + default: '', + description: 'Filter tasks by parent task ID', + }, { displayName: 'Project ID', name: 'projectId', @@ -405,6 +434,19 @@ export class Todoist implements INodeType { default: '', description: 'Filter tasks by project ID', }, + { + displayName: 'Section ID', + name: 'sectionId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getSections', + loadOptionsDependsOn: [ + 'filters.projectId', + ], + }, + default: '', + description: 'Filter tasks by section ID', + }, ], }, { @@ -445,13 +487,6 @@ export class Todoist implements INodeType { default: '', description: 'Specific date and time in RFC3339 format in UTC', }, - { - displayName: 'Due String', - name: 'dueString', - type: 'string', - default: '', - description: 'Human defined task due date (ex.: “next Monday”, “Tomorrow”). Value is set using local (not UTC) time.', - }, { displayName: 'Due String Locale', name: 'dueLang', @@ -459,6 +494,13 @@ export class Todoist implements INodeType { default: '', description: '2-letter code specifying language in case due_string is not written in English', }, + { + displayName: 'Due String', + name: 'dueString', + type: 'string', + default: '', + description: 'Human defined task due date (ex.: “next Monday”, “Tomorrow”). Value is set using local (not UTC) time.', + }, { displayName: 'Labels', name: 'labels', @@ -509,7 +551,13 @@ export class Todoist implements INodeType { async getSections(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const projectId = this.getCurrentNodeParameter('project') as number; + const options = Object.assign({}, + this.getNodeParameter('options', {}), + this.getNodeParameter('filters', {}), + ) as IDataObject; + + const projectId = options.projectId as number ?? + this.getCurrentNodeParameter('project') as number; if (projectId) { const qs: IDataObject = {project_id: projectId}; const sections = await todoistApiRequest.call(this, 'GET', '/sections', {}, qs); @@ -527,6 +575,41 @@ export class Todoist implements INodeType { return returnData; }, + // Get all the available parents in the selected project and section, + // to display them to user so that they can select one easily + async getItems(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + + const options = Object.assign({}, + this.getNodeParameter('options', {}), + this.getNodeParameter('filters', {}), + ) as IDataObject; + + const projectId = options.projectId as number ?? + this.getCurrentNodeParameter('project') as number; + + const sectionId = options.sectionId as number || options.section as number || + this.getCurrentNodeParameter('sectionId') as number; + + if (projectId) { + const qs: IDataObject = sectionId ? + {project_id: projectId, section_id: sectionId} : {project_id: projectId}; + + const items = await todoistApiRequest.call(this, 'GET', '/tasks', {}, qs); + for (const item of items) { + const itemContent = item.content; + const itemId = item.id; + + returnData.push({ + name: itemContent, + value: itemId, + }); + } + } + + return returnData; + }, + // Get all the available labels to display them to user so that he can // select them easily async getLabels(this: ILoadOptionsFunctions): Promise { @@ -592,13 +675,16 @@ export class Todoist implements INodeType { } if (labels !== undefined && labels.length !== 0) { - body.label_ids = labels; + body.label_ids = labels as number[]; } if (options.section) { body.section_id = options.section as number; } + if (options.parentId) { + body.parent_id = options.parentId as number; + } responseData = await todoistApiRequest.call(this, 'POST', '/tasks', body); } if (operation === 'close') { @@ -628,10 +714,16 @@ export class Todoist implements INodeType { if (operation === 'getAll') { //https://developer.todoist.com/rest/v1/#get-active-tasks const returnAll = this.getNodeParameter('returnAll', i) as boolean; - const filters = this.getNodeParameter('filters', i) as IDataObject; + const filters = this.getNodeParameter('filters', i, {}) as IDataObject; if (filters.projectId) { qs.project_id = filters.projectId as string; } + if (filters.sectionId) { + qs.section_id = filters.sectionId as string; + } + if (filters.parentId) { + qs.parent_id = filters.parentId as string; + } if (filters.labelId) { qs.label_id = filters.labelId as string; }