Add task:getAll and subtask:create to Asana (#1266)

*  Add task:getAll and subtask:create

*  Improvements
This commit is contained in:
Jan 2020-12-28 08:50:16 +01:00 committed by GitHub
commit 3c67ac5a52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 567 additions and 0 deletions

View file

@ -14,9 +14,16 @@ import {
import {
asanaApiRequest,
asanaApiRequestAllItems,
getTaskFields,
getWorkspaces,
} from './GenericFunctions';
import * as moment from 'moment-timezone';
import {
snakeCase,
} from 'change-case';
export class Asana implements INodeType {
description: INodeTypeDescription = {
displayName: 'Asana',
@ -83,6 +90,10 @@ export class Asana implements INodeType {
name: 'Project',
value: 'project',
},
{
name: 'Subtask',
value: 'subtask',
},
{
name: 'Task',
value: 'task',
@ -103,7 +114,273 @@ export class Asana implements INodeType {
default: 'task',
description: 'The resource to operate on.',
},
// ----------------------------------
// subtask
// ----------------------------------
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'subtask',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a subtask',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all substasks',
},
],
default: 'create',
description: 'The operation to perform.',
},
// ----------------------------------
// subtask:create
// ----------------------------------
{
displayName: 'Parent Task ID',
name: 'taskId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'subtask',
],
},
},
description: 'The task to operate on.',
},
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'subtask',
],
},
},
description: 'The name of the subtask to create',
},
{
displayName: 'Additional Fields',
name: 'otherProperties',
type: 'collection',
displayOptions: {
show: {
resource: [
'subtask',
],
operation: [
'create',
],
},
},
default: {},
placeholder: 'Add Field',
options: [
{
displayName: 'Assignee',
name: 'assignee',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: '',
description: 'Set Assignee on the subtask',
},
{
displayName: 'Assignee Status',
name: 'assignee_status',
type: 'options',
options: [
{
name: 'Inbox',
value: 'inbox',
},
{
name: 'Today',
value: 'today',
},
{
name: 'Upcoming',
value: 'upcoming',
},
{
name: 'Later',
value: 'later',
},
],
default: 'inbox',
description: 'Set Assignee status on the subtask (requires Assignee)',
},
{
displayName: 'Completed',
name: 'completed',
type: 'boolean',
default: false,
description: 'If the subtask should be marked completed.',
},
{
displayName: 'Due On',
name: 'due_on',
type: 'dateTime',
default: '',
description: 'Date on which the time is due.',
},
{
displayName: 'Liked',
name: 'liked',
type: 'boolean',
default: false,
description: 'If the task is liked by the authorized user.',
},
{
displayName: 'Notes',
name: 'notes',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
rows: 5,
},
default: '',
description: 'The task notes',
},
{
displayName: 'Workspace',
name: 'workspace',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getWorkspaces',
},
default: '',
description: 'The workspace to create the subtask in',
},
],
},
// ----------------------------------
// subtask:getAll
// ----------------------------------
{
displayName: 'Parent Task ID',
name: 'taskId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'subtask',
],
},
},
description: 'The task to operate on.',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'subtask',
],
},
},
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: [
'subtask',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 500,
},
default: 100,
description: 'How many results to return.',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'subtask',
],
},
},
default: {},
placeholder: 'Add Field',
options: [
{
displayName: 'Fields',
name: 'opt_fields',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getTaskFields',
},
default: [
'gid',
'name',
'resource_type',
],
description: 'Defines fields to return.',
},
{
displayName: 'Pretty',
name: 'opt_pretty',
type: 'boolean',
default: false,
description: 'Provides “pretty” output.',
},
],
},
// ----------------------------------
// task
// ----------------------------------
@ -134,6 +411,11 @@ export class Asana implements INodeType {
value: 'get',
description: 'Get a task',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all tasks',
},
{
name: 'Move',
value: 'move',
@ -241,6 +523,145 @@ export class Asana implements INodeType {
},
description: 'The ID of the task to get the data of.',
},
// ----------------------------------
// 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',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'task',
],
},
},
default: {},
description: 'Properties to search for',
placeholder: 'Add Filter',
options: [
{
displayName: 'Assignee',
name: 'assignee',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: '',
description: 'The assignee to filter tasks on. Note: If you specify assignee, you must also specify the workspace to filter on.',
},
{
displayName: 'Fields',
name: 'opt_fields',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getTaskFields',
},
default: [
'gid',
'name',
'resource_type',
],
description: 'Defines fields to return.',
},
{
displayName: 'Pretty',
name: 'opt_pretty',
type: 'boolean',
default: false,
description: 'Provides “pretty” output.',
},
{
displayName: 'Project',
name: 'project',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getProjects',
},
default: '',
description: 'The project to filter tasks on.',
},
{
displayName: 'Section',
name: 'section',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getSections',
},
default: '',
description: 'The section to filter tasks on.',
},
{
displayName: 'Workspace',
name: 'workspace',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getWorkspaces',
},
default: '',
description: 'The workspace to filter tasks on. Note: If you specify workspace, you must also specify the assignee to filter on.',
},
{
displayName: 'Completed Since',
name: 'completed_since',
type: 'dateTime',
default: '',
description: 'Only return tasks that are either incomplete or that have been completed since this time.',
},
{
displayName: 'Modified Since',
name: 'modified_since',
type: 'dateTime',
default: '',
description: 'Only return tasks that have been modified since the given time.',
},
],
},
// ----------------------------------
// task:move
@ -1219,12 +1640,25 @@ export class Asana implements INodeType {
return returnData;
},
async getTaskFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
for (const field of getTaskFields()) {
const value = snakeCase(field);
returnData.push({
name: field,
value: (value === '') ? '*' : value,
});
}
return returnData;
},
},
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const timezone = this.getTimezone();
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;
@ -1240,6 +1674,61 @@ export class Asana implements INodeType {
body = {};
qs = {};
if (resource === 'subtask') {
if (operation === 'create') {
// ----------------------------------
// subtask:create
// ----------------------------------
const taskId = this.getNodeParameter('taskId', i) as string;
requestMethod = 'POST';
endpoint = `/tasks/${taskId}/subtasks`;
body.name = this.getNodeParameter('name', i) as string;
const otherProperties = this.getNodeParameter('otherProperties', i) as IDataObject;
Object.assign(body, otherProperties);
responseData = await asanaApiRequest.call(this, requestMethod, endpoint, body, qs);
responseData = responseData.data;
}
if (operation === 'getAll') {
// ----------------------------------
// subtask:getAll
// ----------------------------------
const taskId = this.getNodeParameter('taskId', i) as string;
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
const options = this.getNodeParameter('options', i) as IDataObject;
requestMethod = 'GET';
endpoint = `/tasks/${taskId}/subtasks`;
Object.assign(qs, options);
if (qs.opt_fields) {
const fields = qs.opt_fields as string[];
if (fields.includes('*')) {
qs.opt_fields = getTaskFields().map((e) => snakeCase(e)).join(',');
} else {
qs.opt_fields = (qs.opt_fields as string[]).join(',');
}
}
responseData = await asanaApiRequest.call(this, requestMethod, endpoint, body, qs);
responseData = responseData.data;
if (returnAll === false) {
const limit = this.getNodeParameter('limit', i) as boolean;
responseData = responseData.splice(0, limit);
}
}
}
if (resource === 'task') {
if (operation === 'create') {
// ----------------------------------
@ -1286,6 +1775,47 @@ export class Asana implements INodeType {
responseData = responseData.data;
} else if (operation === 'getAll') {
// ----------------------------------
// task:getAll
// ----------------------------------
const filters = this.getNodeParameter('filters', i) as IDataObject;
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
requestMethod = 'GET';
endpoint = `/tasks`;
Object.assign(qs, filters);
if (qs.opt_fields) {
const fields = qs.opt_fields as string[];
if (fields.includes('*')) {
qs.opt_fields = getTaskFields().map((e) => snakeCase(e)).join(',');
} else {
qs.opt_fields = (qs.opt_fields as string[]).join(',');
}
}
if (qs.modified_since) {
qs.modified_since = moment.tz(qs.modified_since as string, timezone).format();
}
if (qs.completed_since) {
qs.completed_since = moment.tz(qs.completed_since as string, timezone).format();
}
if (returnAll) {
responseData = await asanaApiRequestAllItems.call(this, requestMethod, endpoint, body, qs);
} else {
qs.limit = this.getNodeParameter('limit', i) as boolean;
responseData = await asanaApiRequest.call(this, requestMethod, endpoint, body, qs);
responseData = responseData.data;
}
} else if (operation === 'move') {
// ----------------------------------
// task:move

View file

@ -121,3 +121,40 @@ export async function getWorkspaces(this: ILoadOptionsFunctions): Promise < INod
return returnData;
}
export function getTaskFields() {
return [
'*',
'GID',
'Resource Type',
'name',
'Approval Status',
'Assignee Status',
'Completed',
'Completed At',
'Completed By',
'Created At',
'Dependencies',
'Dependents',
'Due At',
'Due On',
'External',
'HTML Notes',
'Liked',
'Likes',
'Memberships',
'Modified At',
'Notes',
'Num Likes',
'Resource Subtype',
'Start On',
'Assignee',
'Custom Fields',
'Followers',
'Parent',
'Permalink URL',
'Projects',
'Tags',
'Workspace',
];
}