Add Clockify task resource (#2162)

*  Add Task resource to Clockify Node

* 🔨 Refactor Clockify expansion

* 🔥 Remove logging

*  Add defaults

*  Improvements

*  Minor improvements

Co-authored-by: Frank Silver <dasylva.f@gmail.com>
Co-authored-by: ricardo <ricardoespinoza105@gmail.com>
Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
Iván Ovejero 2021-09-03 19:03:15 +02:00 committed by GitHub
parent 7dcbaedea6
commit 1084e7d9b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 538 additions and 5 deletions

View file

@ -39,6 +39,11 @@ import {
tagOperations,
} from './TagDescription';
import {
taskFields,
taskOperations,
} from './TaskDescription';
import {
timeEntryFields,
timeEntryOperations,
@ -81,6 +86,10 @@ export class Clockify implements INodeType {
name: 'Tag',
value: 'tag',
},
{
name: 'Task',
value: 'task',
},
{
name: 'Time Entry',
value: 'timeEntry',
@ -91,6 +100,7 @@ export class Clockify implements INodeType {
},
...projectOperations,
...tagOperations,
...taskOperations,
...timeEntryOperations,
{
displayName: 'Workspace ID',
@ -104,6 +114,7 @@ export class Clockify implements INodeType {
},
...projectFields,
...tagFields,
...taskFields,
...timeEntryFields,
],
};
@ -457,6 +468,152 @@ export class Clockify implements INodeType {
}
}
if (resource === 'task') {
if (operation === 'create') {
const workspaceId = this.getNodeParameter(
'workspaceId',
i,
) as string;
const projectId = this.getNodeParameter('projectId', i) as string;
const name = this.getNodeParameter('name', i) as string;
const additionalFields = this.getNodeParameter(
'additionalFields',
i,
) as IDataObject;
const body: IDataObject = {
name,
};
Object.assign(body, additionalFields);
if (body.estimate) {
const [hour, minute] = (body.estimate as string).split(':');
body.estimate = `PT${hour}H${minute}M`;
}
responseData = await clockifyApiRequest.call(
this,
'POST',
`/workspaces/${workspaceId}/projects/${projectId}/tasks`,
body,
qs,
);
}
if (operation === 'delete') {
const workspaceId = this.getNodeParameter(
'workspaceId',
i,
) as string;
const projectId = this.getNodeParameter('projectId', i) as string;
const taskId = this.getNodeParameter('taskId', i) as string;
responseData = await clockifyApiRequest.call(
this,
'DELETE',
`/workspaces/${workspaceId}/projects/${projectId}/tasks/${taskId}`,
{},
qs,
);
}
if (operation === 'get') {
const workspaceId = this.getNodeParameter(
'workspaceId',
i,
) as string;
const projectId = this.getNodeParameter('projectId', i) as string;
const taskId = this.getNodeParameter('taskId', i) as string;
responseData = await clockifyApiRequest.call(
this,
'GET',
`/workspaces/${workspaceId}/projects/${projectId}/tasks/${taskId}`,
{},
qs,
);
}
if (operation === 'getAll') {
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
const workspaceId = this.getNodeParameter(
'workspaceId',
i,
) as string;
const projectId = this.getNodeParameter('projectId', i) as string;
const filters = this.getNodeParameter(
'filters',
i,
) as IDataObject;
Object.assign(qs, filters);
if (returnAll) {
responseData = await clockifyApiRequestAllItems.call(
this,
'GET',
`/workspaces/${workspaceId}/projects/${projectId}/tasks`,
{},
qs,
);
} else {
qs['page-size'] = this.getNodeParameter('limit', i) as number;
responseData = await clockifyApiRequest.call(
this,
'GET',
`/workspaces/${workspaceId}/projects/${projectId}/tasks`,
{},
qs,
);
}
}
if (operation === 'update') {
const workspaceId = this.getNodeParameter(
'workspaceId',
i,
) as string;
const projectId = this.getNodeParameter('projectId', i) as string;
const taskId = this.getNodeParameter('taskId', i) as string;
const updateFields = this.getNodeParameter(
'updateFields',
i,
) as IDataObject;
const body: IDataObject = {};
Object.assign(body, updateFields);
if (body.estimate) {
const [hour, minute] = (body.estimate as string).split(':');
body.estimate = `PT${hour}H${minute}M`;
}
responseData = await clockifyApiRequest.call(
this,
'PUT',
`/workspaces/${workspaceId}/projects/${projectId}/tasks/${taskId}`,
body,
qs,
);
}
}
if (resource === 'timeEntry') {
if (operation === 'create') {

View file

@ -24,7 +24,7 @@ export class ClockifyTrigger implements INodeType {
displayName: 'Clockify Trigger',
icon: 'file:clockify.svg',
name: 'clockifyTrigger',
group: [ 'trigger' ],
group: ['trigger'],
version: 1,
description: 'Listens to Clockify events',
defaults: {
@ -32,7 +32,7 @@ export class ClockifyTrigger implements INodeType {
color: '#000000',
},
inputs: [],
outputs: [ 'main' ],
outputs: ['main'],
credentials: [
{
name: 'clockifyApi',
@ -109,7 +109,7 @@ export class ClockifyTrigger implements INodeType {
qs.start = webhookData.lastTimeChecked;
qs.end = moment().tz(workflowTimezone).format('YYYY-MM-DDTHH:mm:ss') + 'Z';
qs.hydrated = true;
qs[ 'in-progress' ] = false;
qs['in-progress'] = false;
break;
}
@ -117,7 +117,7 @@ export class ClockifyTrigger implements INodeType {
webhookData.lastTimeChecked = qs.end;
if (Array.isArray(result) && result.length !== 0) {
return [ this.helpers.returnJsonArray(result) ];
return [this.helpers.returnJsonArray(result)];
}
return null;
}

View file

@ -10,6 +10,11 @@ enum MembershipStatusEnum {
INACTIVE = 'INACTIVE',
}
enum TaskStatusEnum {
ACTIVE = 'ACTIVE',
DONE = 'DONE',
}
export interface IMembershipDto {
hourlyRate: IHourlyRateDto;
membershipStatus: MembershipStatusEnum;
@ -25,6 +30,17 @@ export interface ITagDto {
archived: boolean;
}
export interface ITaskDto {
assigneeIds: object;
estimate: string;
id: string;
name: any; // tslint:disable-line:no-any
workspaceId: string;
projectId: string;
'is-active': boolean;
status: TaskStatusEnum;
}
export interface ITimeIntervalDto {
duration: string;
end: string;

View file

@ -1,4 +1,7 @@
import { IHourlyRateDto, IMembershipDto } from './CommonDtos';
import {
IHourlyRateDto,
IMembershipDto,
} from './CommonDtos';
enum EstimateEnum {
AUTO = 'AUTO',
@ -52,4 +55,5 @@ export interface ITaskDto {
name: string;
projectId: string;
status: TaskStatusEnum;
'is-active': boolean;
}

View file

@ -0,0 +1,356 @@
import {
INodeProperties,
} from 'n8n-workflow';
export const taskOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'task',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a task',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a task',
},
{
name: 'Get',
value: 'get',
description: 'Get a task',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all tasks',
},
{
name: 'Update',
value: 'update',
description: 'Update a task',
},
],
default: 'create',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const taskFields = [
{
displayName: 'Project ID',
name: 'projectId',
type: 'options',
typeOptions: {
loadOptionsDependsOn: [
'workspaceId',
],
loadOptionsMethod: 'loadProjectsForWorkspace',
},
displayOptions: {
show: {
resource: [
'task',
],
},
},
required: true,
default: '',
},
/* -------------------------------------------------------------------------- */
/* task:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Task Name',
name: 'name',
type: 'string',
required: true,
default: '',
description: 'Name of task to create',
displayOptions: {
show: {
resource: [
'task',
],
operation: [
'create',
],
},
},
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'task',
],
},
},
default: {},
options: [
{
displayName: 'Assignee IDs',
name: 'assigneeIds',
type: 'multiOptions',
default: [],
typeOptions: {
loadOptionsMethod: 'loadUsersForWorkspace',
},
},
{
displayName: 'Estimate',
name: 'estimate',
type: 'string',
default: '',
placeholder: '2:30',
description: 'Estimate time the task will take, e.x: 2:30 (2 hours and 30 minutes)',
},
],
},
/* -------------------------------------------------------------------------- */
/* task:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Task ID',
name: 'taskId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'task',
],
operation: [
'delete',
],
},
},
description: 'ID of task to delete',
},
/* -------------------------------------------------------------------------- */
/* task:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Task ID',
name: 'taskId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'task',
],
operation: [
'get',
],
},
},
description: 'ID of task to get',
},
/* -------------------------------------------------------------------------- */
/* task:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'task',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'task',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 500,
},
default: 100,
description: 'How many results to return.',
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Filter',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'task',
],
},
},
default: {},
options: [
{
displayName: 'Is Active',
name: 'is-active',
type: 'boolean',
default: false,
},
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'Text to match in the task name',
},
{
displayName: 'Sort Column',
name: 'sort-column',
type: 'options',
options: [
{
name: 'Name',
value: 'NAME',
},
],
default: 'NAME',
},
{
displayName: 'Sort Order',
name: 'sort-order',
type: 'options',
options: [
{
name: 'Ascending',
value: 'ASCENDING',
},
{
name: 'Descending',
value: 'DESCENDING',
},
],
default: 'ASCENDING',
},
],
},
/* -------------------------------------------------------------------------- */
/* task:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Task ID',
name: 'taskId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'task',
],
operation: [
'update',
],
},
},
description: 'ID of task to update',
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
operation: [
'update',
],
resource: [
'task',
],
},
},
default: {},
options: [
{
displayName: 'Assignee IDs',
name: 'assigneeIds',
type: 'multiOptions',
default: [],
typeOptions: {
loadOptionsMethod: 'loadUsersForWorkspace',
},
},
{
displayName: 'Estimate',
name: 'estimate',
type: 'string',
default: '',
placeholder: '2:30',
description: 'Estimate time the task will take, e.x: 2:30 (2 hours and 30 minutes)',
},
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
},
{
displayName: 'Status',
name: 'status',
type: 'options',
options: [
{
name: 'Active',
value: 'ACTIVE',
},
{
name: 'Done',
value: 'DONE',
},
],
default: 'ACTIVE',
},
],
},
] as INodeProperties[];