diff --git a/packages/nodes-base/nodes/Microsoft/Teams/ChannelDescription.ts b/packages/nodes-base/nodes/Microsoft/Teams/ChannelDescription.ts index 6b188f220b..36244b6452 100644 --- a/packages/nodes-base/nodes/Microsoft/Teams/ChannelDescription.ts +++ b/packages/nodes-base/nodes/Microsoft/Teams/ChannelDescription.ts @@ -7,6 +7,7 @@ export const channelOperations: INodeProperties[] = [ displayName: 'Operation', name: 'operation', type: 'options', + noDataExpression: true, displayOptions: { show: { resource: [ @@ -42,7 +43,6 @@ export const channelOperations: INodeProperties[] = [ }, ], default: 'create', - description: 'The operation to perform.', }, ]; @@ -87,7 +87,7 @@ export const channelFields: INodeProperties[] = [ }, }, default: '', - description: 'Channel name as it will appear to the user in Microsoft Teams.', + description: 'Channel name as it will appear to the user in Microsoft Teams', }, { displayName: 'Options', @@ -266,7 +266,7 @@ export const channelFields: INodeProperties[] = [ }, }, default: false, - description: 'If all results should be returned or only up to a given limit.', + description: 'Whether to return all results or only up to a given limit', }, { displayName: 'Limit', @@ -289,8 +289,8 @@ export const channelFields: INodeProperties[] = [ minValue: 1, maxValue: 500, }, - default: 100, - description: 'How many results to return.', + default: 50, + description: 'Max number of results to return', }, /* -------------------------------------------------------------------------- */ @@ -360,7 +360,7 @@ export const channelFields: INodeProperties[] = [ name: 'name', type: 'string', default: '', - description: 'Channel name as it will appear to the user in Microsoft Teams.', + description: 'Channel name as it will appear to the user in Microsoft Teams', }, { displayName: 'Description', diff --git a/packages/nodes-base/nodes/Microsoft/Teams/ChannelMessageDescription.ts b/packages/nodes-base/nodes/Microsoft/Teams/ChannelMessageDescription.ts index 0c31578e31..fdd2ec8410 100644 --- a/packages/nodes-base/nodes/Microsoft/Teams/ChannelMessageDescription.ts +++ b/packages/nodes-base/nodes/Microsoft/Teams/ChannelMessageDescription.ts @@ -7,6 +7,7 @@ export const channelMessageOperations: INodeProperties[] = [ displayName: 'Operation', name: 'operation', type: 'options', + noDataExpression: true, displayOptions: { show: { resource: [ @@ -27,7 +28,6 @@ export const channelMessageOperations: INodeProperties[] = [ }, ], default: 'create', - description: 'The operation to perform.', }, ]; @@ -103,7 +103,7 @@ export const channelMessageFields: INodeProperties[] = [ ], }, }, - default: '', + default: 'text', description: 'The type of the content', }, { @@ -125,7 +125,7 @@ export const channelMessageFields: INodeProperties[] = [ }, }, default: '', - description: 'The content of the item.', + description: 'The content of the item', }, { displayName: 'Options', @@ -149,7 +149,7 @@ export const channelMessageFields: INodeProperties[] = [ name: 'makeReply', type: 'string', default: '', - description: 'An optional ID of the message you want to reply to.', + description: 'An optional ID of the message you want to reply to', }, ], }, @@ -213,7 +213,7 @@ export const channelMessageFields: INodeProperties[] = [ }, }, default: false, - description: 'If all results should be returned or only up to a given limit.', + description: 'Whether to return all results or only up to a given limit', }, { displayName: 'Limit', @@ -236,7 +236,7 @@ export const channelMessageFields: INodeProperties[] = [ minValue: 1, maxValue: 500, }, - default: 100, - description: 'How many results to return.', + default: 50, + description: 'Max number of results to return', }, ]; diff --git a/packages/nodes-base/nodes/Microsoft/Teams/ChatMessageDescription.ts b/packages/nodes-base/nodes/Microsoft/Teams/ChatMessageDescription.ts index 0cb29f074c..1fea40182c 100644 --- a/packages/nodes-base/nodes/Microsoft/Teams/ChatMessageDescription.ts +++ b/packages/nodes-base/nodes/Microsoft/Teams/ChatMessageDescription.ts @@ -7,6 +7,7 @@ export const chatMessageOperations: INodeProperties[] = [ displayName: 'Operation', name: 'operation', type: 'options', + noDataExpression: true, displayOptions: { show: { resource: [ @@ -32,7 +33,6 @@ export const chatMessageOperations: INodeProperties[] = [ }, ], default: 'create', - description: 'The operation to perform.', }, ]; @@ -87,7 +87,7 @@ export const chatMessageFields: INodeProperties[] = [ ], }, }, - default: '', + default: 'text', description: 'The type of the content', }, { @@ -109,7 +109,7 @@ export const chatMessageFields: INodeProperties[] = [ }, }, default: '', - description: 'The content of the item.', + description: 'The content of the item', }, /* -------------------------------------------------------------------------- */ @@ -170,7 +170,7 @@ export const chatMessageFields: INodeProperties[] = [ }, }, default: false, - description: 'If all results should be returned or only up to a given limit.', + description: 'Whether to return all results or only up to a given limit', }, { displayName: 'Limit', @@ -193,7 +193,7 @@ export const chatMessageFields: INodeProperties[] = [ minValue: 1, maxValue: 500, }, - default: 100, - description: 'How many results to return.', + default: 50, + description: 'Max number of results to return', }, ]; diff --git a/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts b/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts index 9073158577..3e34844926 100644 --- a/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts +++ b/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts @@ -9,6 +9,7 @@ import { INodePropertyOptions, INodeType, INodeTypeDescription, + JsonObject, } from 'n8n-workflow'; import { @@ -61,6 +62,7 @@ export class MicrosoftTeams implements INodeType { displayName: 'Resource', name: 'resource', type: 'options', + noDataExpression: true, options: [ { name: 'Channel', @@ -80,7 +82,7 @@ export class MicrosoftTeams implements INodeType { }, ], default: 'channel', - description: 'The resource to operate on.', + description: 'The resource to operate on', }, // CHANNEL ...channelOperations, @@ -133,11 +135,17 @@ export class MicrosoftTeams implements INodeType { // select them easily async getGroups(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const { value } = await microsoftApiRequest.call(this, 'GET', '/v1.0/groups'); + const groupSource = this.getCurrentNodeParameter('groupSource') as string; + let requestUrl = '/v1.0/groups' as string; + if (groupSource === 'mine') { + requestUrl = '/v1.0/me/transitiveMemberOf'; + } + const { value } = await microsoftApiRequest.call(this, 'GET', requestUrl); for (const group of value) { returnData.push({ - name: group.mail, + name: group.displayName || group.mail || group.id, value: group.id, + description: group.mail, }); } return returnData; @@ -146,7 +154,12 @@ export class MicrosoftTeams implements INodeType { // select them easily async getPlans(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const groupId = this.getCurrentNodeParameter('groupId') as string; + let groupId = this.getCurrentNodeParameter('groupId') as string; + const operation = this.getNodeParameter('operation', 0) as string; + if (operation === 'update' && (groupId === undefined || groupId === null)) { + // groupId not found at base, check updateFields for the groupId + groupId = this.getCurrentNodeParameter('updateFields.groupId') as string; + } const { value } = await microsoftApiRequest.call(this, 'GET', `/v1.0/groups/${groupId}/planner/plans`); for (const plan of value) { returnData.push({ @@ -160,7 +173,12 @@ export class MicrosoftTeams implements INodeType { // select them easily async getBuckets(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const planId = this.getCurrentNodeParameter('planId') as string; + let planId = this.getCurrentNodeParameter('planId') as string; + const operation = this.getNodeParameter('operation', 0) as string; + if (operation === 'update' && (planId === undefined || planId === null)) { + // planId not found at base, check updateFields for the planId + planId = this.getCurrentNodeParameter('updateFields.planId') as string; + } const { value } = await microsoftApiRequest.call(this, 'GET', `/v1.0/planner/plans/${planId}/buckets`); for (const bucket of value) { returnData.push({ @@ -174,7 +192,12 @@ export class MicrosoftTeams implements INodeType { // select them easily async getMembers(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const groupId = this.getCurrentNodeParameter('groupId') as string; + let groupId = this.getCurrentNodeParameter('groupId') as string; + const operation = this.getNodeParameter('operation', 0) as string; + if (operation === 'update' && (groupId === undefined || groupId === null)) { + // groupId not found at base, check updateFields for the groupId + groupId = this.getCurrentNodeParameter('updateFields.groupId') as string; + } const { value } = await microsoftApiRequest.call(this, 'GET', `/v1.0/groups/${groupId}/members`); for (const member of value) { returnData.push({ @@ -188,7 +211,13 @@ export class MicrosoftTeams implements INodeType { // select them easily async getLabels(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const planId = this.getCurrentNodeParameter('planId') as string; + + let planId = this.getCurrentNodeParameter('planId') as string; + const operation = this.getNodeParameter('operation', 0) as string; + if (operation === 'update' && (planId === undefined || planId === null)) { + // planId not found at base, check updateFields for the planId + planId = this.getCurrentNodeParameter('updateFields.planId') as string; + } const { categoryDescriptions } = await microsoftApiRequest.call(this, 'GET', `/v1.0/planner/plans/${planId}/details`); for (const key of Object.keys(categoryDescriptions)) { if (categoryDescriptions[key] !== null) { @@ -407,16 +436,29 @@ export class MicrosoftTeams implements INodeType { const taskId = this.getNodeParameter('taskId', i) as string; responseData = await microsoftApiRequest.call(this, 'GET', `/v1.0/planner/tasks/${taskId}`); } - //https://docs.microsoft.com/en-us/graph/api/planneruser-list-tasks?view=graph-rest-1.0&tabs=http if (operation === 'getAll') { - const memberId = this.getNodeParameter('memberId', i) as string; + const tasksFor = this.getNodeParameter('tasksFor', i) as string; const returnAll = this.getNodeParameter('returnAll', i) as boolean; - if (returnAll) { - responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/users/${memberId}/planner/tasks`); + if (tasksFor === 'member') { + //https://docs.microsoft.com/en-us/graph/api/planneruser-list-tasks?view=graph-rest-1.0&tabs=http + const memberId = this.getNodeParameter('memberId', i) as string; + if (returnAll) { + responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/users/${memberId}/planner/tasks`); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/users/${memberId}/planner/tasks`, {}); + responseData = responseData.splice(0, qs.limit); + } } else { - qs.limit = this.getNodeParameter('limit', i) as number; - responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/users/${memberId}/planner/tasks`, {}); - responseData = responseData.splice(0, qs.limit); + //https://docs.microsoft.com/en-us/graph/api/plannerplan-list-tasks?view=graph-rest-1.0&tabs=http + const planId = this.getNodeParameter('planId', i) as string; + if (returnAll) { + responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/planner/plans/${planId}/tasks`); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/planner/plans/${planId}/tasks`, {}); + responseData = responseData.splice(0, qs.limit); + } } } //https://docs.microsoft.com/en-us/graph/api/plannertask-update?view=graph-rest-1.0&tabs=http @@ -436,6 +478,11 @@ export class MicrosoftTeams implements INodeType { delete body.assignedTo; } + if (body.groupId) { + // tasks are assigned to a plan and bucket, group is used for filtering + delete body.groupId; + } + if (Array.isArray(body.labels)) { body.appliedCategories = (body.labels as string[]).map((label) => ({ [label]: true })); } @@ -454,7 +501,7 @@ export class MicrosoftTeams implements INodeType { } } catch (error) { if (this.continueOnFail()) { - returnData.push({ error: error.message }); + returnData.push({ error: (error as JsonObject).message }); continue; } throw error; diff --git a/packages/nodes-base/nodes/Microsoft/Teams/TaskDescription.ts b/packages/nodes-base/nodes/Microsoft/Teams/TaskDescription.ts index 9cf53d0739..0523635d52 100644 --- a/packages/nodes-base/nodes/Microsoft/Teams/TaskDescription.ts +++ b/packages/nodes-base/nodes/Microsoft/Teams/TaskDescription.ts @@ -7,6 +7,7 @@ export const taskOperations: INodeProperties[] = [ displayName: 'Operation', name: 'operation', type: 'options', + noDataExpression: true, displayOptions: { show: { resource: [ @@ -42,15 +43,47 @@ export const taskOperations: INodeProperties[] = [ }, ], default: 'create', - description: 'The operation to perform.', }, ]; export const taskFields: INodeProperties[] = [ + { + displayName: 'Group Source', + name: 'groupSource', + required: true, + type: 'options', + default: 'all', + displayOptions: { + show: { + operation: [ + 'getAll', + 'create', + 'update', + ], + resource: [ + 'task', + ], + }, + }, + options: [ + { + name: 'All Groups', + value: 'all', + description: 'From all groups', + }, + { + name: 'My Groups', + value: 'mine', + description: 'Only load groups that account is member of', + }, + ], + }, + /* -------------------------------------------------------------------------- */ /* task:create */ /* -------------------------------------------------------------------------- */ + { displayName: 'Group ID', name: 'groupId', @@ -58,6 +91,9 @@ export const taskFields: INodeProperties[] = [ type: 'options', typeOptions: { loadOptionsMethod: 'getGroups', + loadOptionsDependsOn: [ + 'groupSource', + ], }, displayOptions: { show: { @@ -93,7 +129,7 @@ export const taskFields: INodeProperties[] = [ }, }, default: '', - description: 'The ID of the Plan.', + description: 'The plan for the task to belong to', }, { displayName: 'Bucket ID', @@ -117,7 +153,7 @@ export const taskFields: INodeProperties[] = [ }, }, default: '', - description: 'The ID of the Bucket.', + description: 'The bucket for the task to belong to', }, { displayName: 'Title', @@ -135,7 +171,7 @@ export const taskFields: INodeProperties[] = [ }, }, default: '', - description: 'Title of the task.', + description: 'Title of the task', }, { displayName: 'Additional Fields', @@ -165,14 +201,14 @@ export const taskFields: INodeProperties[] = [ ], }, default: '', - description: `Date and time at which the task is due. The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time.`, + description: 'Who the task should be assigned to', }, { displayName: 'Due Date Time', name: 'dueDateTime', type: 'dateTime', default: '', - description: `Date and time at which the task is due. The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time.`, + description: 'Date and time at which the task is due. The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time.', }, { displayName: 'Labels', @@ -185,7 +221,7 @@ export const taskFields: INodeProperties[] = [ ], }, default: [], - description: `Percentage of task completion. When set to 100, the task is considered completed.`, + description: 'Labels to assign to the task', }, { displayName: 'Percent Complete', @@ -196,7 +232,7 @@ export const taskFields: INodeProperties[] = [ maxValue: 100, }, default: 0, - description: `Percentage of task completion. When set to 100, the task is considered completed.`, + description: 'Percentage of task completion. When set to 100, the task is considered completed.', }, ], }, @@ -246,6 +282,36 @@ export const taskFields: INodeProperties[] = [ /* -------------------------------------------------------------------------- */ /* task:getAll */ /* -------------------------------------------------------------------------- */ + + { + displayName: 'Tasks For', + name: 'tasksFor', + default: 'member', + required: true, + type: 'options', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'task', + ], + }, + }, + options: [ + { + name: 'Group Member', + value: 'member', + description: 'Tasks assigned to group member', + }, + { + name: 'Plan', + value: 'plan', + description: 'Tasks in group plan', + }, + ], + }, { displayName: 'Group ID', name: 'groupId', @@ -253,6 +319,9 @@ export const taskFields: INodeProperties[] = [ type: 'options', typeOptions: { loadOptionsMethod: 'getGroups', + loadOptionsDependsOn: [ + 'groupSource', + ], }, displayOptions: { show: { @@ -285,6 +354,35 @@ export const taskFields: INodeProperties[] = [ resource: [ 'task', ], + tasksFor: [ + 'member', + ], + }, + }, + default: '', + }, + { + displayName: 'Plan ID', + name: 'planId', + required: false, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getPlans', + loadOptionsDependsOn: [ + 'groupId', + ], + }, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'task', + ], + tasksFor: [ + 'plan', + ], }, }, default: '', @@ -304,7 +402,7 @@ export const taskFields: INodeProperties[] = [ }, }, default: false, - description: 'If all results should be returned or only up to a given limit.', + description: 'Whether to return all results or only up to a given limit', }, { displayName: 'Limit', @@ -327,8 +425,8 @@ export const taskFields: INodeProperties[] = [ minValue: 1, maxValue: 500, }, - default: 100, - description: 'How many results to return.', + default: 50, + description: 'Max number of results to return', }, /* -------------------------------------------------------------------------- */ @@ -350,7 +448,7 @@ export const taskFields: INodeProperties[] = [ }, }, default: '', - description: 'The ID of the Task.', + description: 'The ID of the Task', }, { displayName: 'Update Fields', @@ -380,26 +478,38 @@ export const taskFields: INodeProperties[] = [ ], }, default: '', - description: `Date and time at which the task is due. The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time.`, + description: 'Who the task should be assigned to', }, { displayName: 'Bucket ID', name: 'bucketId', - type: 'string', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getBuckets', + loadOptionsDependsOn: [ + 'updateFields.planId', + ], + }, default: '', - description: 'Channel name as it will appear to the user in Microsoft Teams.', + description: 'The bucket for the task to belong to', }, { displayName: 'Due Date Time', name: 'dueDateTime', type: 'dateTime', default: '', - description: `Date and time at which the task is due. The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time.`, + description: 'Date and time at which the task is due. The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time.', }, { displayName: 'Group ID', name: 'groupId', - type: 'string', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getGroups', + loadOptionsDependsOn: [ + 'groupSource', + ], + }, default: '', }, { @@ -409,11 +519,11 @@ export const taskFields: INodeProperties[] = [ typeOptions: { loadOptionsMethod: 'getLabels', loadOptionsDependsOn: [ - 'planId', + 'updateFields.planId', ], }, default: [], - description: `Percentage of task completion. When set to 100, the task is considered completed.`, + description: 'Labels to assign to the task', }, { displayName: 'Percent Complete', @@ -424,21 +534,27 @@ export const taskFields: INodeProperties[] = [ maxValue: 100, }, default: 0, - description: `Percentage of task completion. When set to 100, the task is considered completed.`, + description: 'Percentage of task completion. When set to 100, the task is considered completed.', }, { displayName: 'Plan ID', name: 'planId', - type: 'string', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getPlans', + loadOptionsDependsOn: [ + 'groupId', + ], + }, default: '', - description: 'Channel name as it will appear to the user in Microsoft Teams.', + description: 'The plan for the task to belong to', }, { displayName: 'Title', name: 'title', type: 'string', default: '', - description: 'Title of the task.', + description: 'Title of the task', }, ], },