@blickwerk/n8n-nodes-microsoft-planner 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -26
- package/dist/nodes/MicrosoftPlanner/GenericFunctions.js +2 -0
- package/dist/nodes/MicrosoftPlanner/MicrosoftPlanner.node.d.ts +8 -1
- package/dist/nodes/MicrosoftPlanner/MicrosoftPlanner.node.js +65 -8
- package/dist/nodes/MicrosoftPlanner/TaskDescription.js +30 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes
|
|
|
14
14
|
|
|
15
15
|
1. Go to **Settings > Community Nodes** in your n8n instance
|
|
16
16
|
2. Select **Install**
|
|
17
|
-
3. Enter
|
|
17
|
+
3. Enter `@blickwerk/n8n-nodes-microsoft-planner` in **Enter npm package name**
|
|
18
18
|
4. Agree to the [risks](https://docs.n8n.io/integrations/community-nodes/risks/) of using community nodes
|
|
19
19
|
5. Select **Install**
|
|
20
20
|
|
|
@@ -22,10 +22,10 @@ After installing the node, you can use it like any other node in your workflows.
|
|
|
22
22
|
|
|
23
23
|
### Manual Installation
|
|
24
24
|
|
|
25
|
-
To install manually
|
|
25
|
+
To install manually:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
npm install n8n-nodes-microsoft-planner
|
|
28
|
+
npm install @blickwerk/n8n-nodes-microsoft-planner
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
## Prerequisites
|
|
@@ -36,6 +36,7 @@ You need to have:
|
|
|
36
36
|
- Azure AD App Registration with the following API permissions:
|
|
37
37
|
- `Tasks.ReadWrite` - Read and write tasks
|
|
38
38
|
- `Group.ReadWrite.All` - Read and write all groups (required for Planner)
|
|
39
|
+
- `User.Read.All` - Read all users (required for user assignment by email)
|
|
39
40
|
|
|
40
41
|
## Setting up Azure AD App
|
|
41
42
|
|
|
@@ -54,10 +55,13 @@ You need to have:
|
|
|
54
55
|
3. Select **Microsoft Graph**
|
|
55
56
|
4. Select **Delegated permissions**
|
|
56
57
|
5. Add the following permissions:
|
|
57
|
-
- `Tasks.ReadWrite`
|
|
58
|
-
- `Group.ReadWrite.All`
|
|
58
|
+
- `Tasks.ReadWrite` - Read and write tasks
|
|
59
|
+
- `Group.ReadWrite.All` - Read and write all groups (required for Planner)
|
|
60
|
+
- `User.Read.All` - Read all users' basic profiles (required for user assignment)
|
|
59
61
|
6. Click **Grant admin consent**
|
|
60
62
|
|
|
63
|
+
**Note**: The `User.Read.All` permission is required if you want to assign tasks to users by email address.
|
|
64
|
+
|
|
61
65
|
### Create Client Secret
|
|
62
66
|
|
|
63
67
|
1. Go to **Certificates & secrets**
|
|
@@ -80,6 +84,13 @@ Configure the Microsoft Planner OAuth2 API credentials in n8n:
|
|
|
80
84
|
4. Click **Connect my account**
|
|
81
85
|
5. Complete the OAuth flow
|
|
82
86
|
|
|
87
|
+
## Features
|
|
88
|
+
|
|
89
|
+
- **Dynamic Dropdowns**: Automatically loads and displays your Plans, Buckets, and Tasks - no need to manually copy IDs!
|
|
90
|
+
- **User Assignment**: Assign tasks to users by email address
|
|
91
|
+
- **Priority Management**: Easy-to-use priority dropdown (Urgent, Important, Medium, Low)
|
|
92
|
+
- **Full CRUD Operations**: Create, Read, Update, and Delete tasks
|
|
93
|
+
|
|
83
94
|
## Operations
|
|
84
95
|
|
|
85
96
|
### Task
|
|
@@ -94,38 +105,55 @@ Configure the Microsoft Planner OAuth2 API credentials in n8n:
|
|
|
94
105
|
|
|
95
106
|
### Creating a Task
|
|
96
107
|
|
|
97
|
-
To create a task
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
108
|
+
To create a task:
|
|
109
|
+
1. Select a **Plan** from the dropdown (automatically loaded from your Microsoft 365)
|
|
110
|
+
2. Select a **Bucket** from the dropdown (loads buckets from the selected plan)
|
|
111
|
+
3. Enter the **Title** of the task
|
|
101
112
|
|
|
102
113
|
Optional fields:
|
|
103
|
-
- Description
|
|
104
|
-
- Priority
|
|
105
|
-
-
|
|
106
|
-
-
|
|
107
|
-
-
|
|
114
|
+
- **Description**: Detailed task description
|
|
115
|
+
- **Priority**: Select from dropdown
|
|
116
|
+
- Urgent (highest priority)
|
|
117
|
+
- Important
|
|
118
|
+
- Medium (default)
|
|
119
|
+
- Low
|
|
120
|
+
- **Assigned To**: Comma-separated list of user emails (e.g., `user1@domain.com, user2@domain.com`)
|
|
121
|
+
- **Due Date Time**: When the task should be completed
|
|
122
|
+
- **Start Date Time**: When work on the task should begin
|
|
123
|
+
- **Percent Complete**: Task completion percentage (0-100)
|
|
108
124
|
|
|
109
125
|
### Getting Tasks
|
|
110
126
|
|
|
111
|
-
|
|
112
|
-
-
|
|
113
|
-
|
|
114
|
-
|
|
127
|
+
**Get a single task:**
|
|
128
|
+
- Select the task from the dropdown (automatically loaded from your plans)
|
|
129
|
+
|
|
130
|
+
**Get many tasks:**
|
|
131
|
+
- Filter by **Plan** (select from dropdown)
|
|
132
|
+
- Or filter by **Bucket** (select from dropdown)
|
|
133
|
+
- Set a limit or return all tasks
|
|
115
134
|
|
|
116
135
|
### Updating a Task
|
|
117
136
|
|
|
118
|
-
To update a task
|
|
137
|
+
To update a task:
|
|
138
|
+
1. Select the **Task** from the dropdown
|
|
139
|
+
2. Update any of these fields:
|
|
140
|
+
- Title
|
|
141
|
+
- Description
|
|
142
|
+
- Priority (via dropdown)
|
|
143
|
+
- Assigned users (via email list)
|
|
144
|
+
- Due Date Time
|
|
145
|
+
- Start Date Time
|
|
146
|
+
- Percent Complete
|
|
147
|
+
- Move to different bucket
|
|
119
148
|
|
|
120
|
-
###
|
|
149
|
+
### How Dynamic Dropdowns Work
|
|
121
150
|
|
|
122
|
-
|
|
151
|
+
The node automatically fetches your data from Microsoft 365:
|
|
152
|
+
- **Plans**: Loaded from your account when you open the Plan dropdown
|
|
153
|
+
- **Buckets**: Dynamically loaded based on the selected Plan
|
|
154
|
+
- **Tasks**: Loaded from the selected Plan or Bucket
|
|
123
155
|
|
|
124
|
-
|
|
125
|
-
2. Sign in with your Microsoft account
|
|
126
|
-
3. Use these queries:
|
|
127
|
-
- Get all plans: `GET https://graph.microsoft.com/v1.0/me/planner/plans`
|
|
128
|
-
- Get buckets in a plan: `GET https://graph.microsoft.com/v1.0/planner/plans/{plan-id}/buckets`
|
|
156
|
+
No need to manually look up IDs anymore! Just select from the dropdown menus.
|
|
129
157
|
|
|
130
158
|
## Compatibility
|
|
131
159
|
|
|
@@ -139,10 +167,34 @@ Tested with n8n version 1.0.0 and above.
|
|
|
139
167
|
|
|
140
168
|
## Version History
|
|
141
169
|
|
|
170
|
+
### 1.2.0
|
|
171
|
+
- **Major UX Improvement**: Added dynamic dropdowns for Plans, Buckets, and Tasks
|
|
172
|
+
- No more manual ID lookup - select from dropdown menus
|
|
173
|
+
- Plans load automatically from your Microsoft 365 account
|
|
174
|
+
- Buckets load dynamically based on selected Plan
|
|
175
|
+
- Tasks load from selected Plan or Bucket
|
|
176
|
+
- Improved user experience with clearer field labels
|
|
177
|
+
|
|
178
|
+
### 1.1.1
|
|
179
|
+
- Fixed startDateTime not being set (was returning null)
|
|
180
|
+
- Fixed user assignment by adding User.Read.All permission requirement
|
|
181
|
+
- Added error logging for failed user lookups
|
|
182
|
+
- Improved README documentation with all required permissions
|
|
183
|
+
|
|
184
|
+
### 1.1.0
|
|
185
|
+
- Added priority dropdown (Urgent, Important, Medium, Low) instead of number input
|
|
186
|
+
- Added user assignment functionality (assign tasks by email)
|
|
187
|
+
- Improved user experience with better field descriptions
|
|
188
|
+
|
|
189
|
+
### 1.0.1
|
|
190
|
+
- Fixed DateTime format handling (automatic conversion to ISO 8601)
|
|
191
|
+
- Improved error handling for invalid date formats
|
|
192
|
+
|
|
142
193
|
### 1.0.0
|
|
143
194
|
- Initial release
|
|
144
195
|
- Support for creating and retrieving Planner tasks
|
|
145
196
|
- OAuth2 authentication
|
|
197
|
+
- CRUD operations (Create, Read, Update, Delete)
|
|
146
198
|
|
|
147
199
|
## License
|
|
148
200
|
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
1
|
+
import { IExecuteFunctions, ILoadOptionsFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
2
|
export declare class MicrosoftPlanner implements INodeType {
|
|
3
3
|
description: INodeTypeDescription;
|
|
4
|
+
methods: {
|
|
5
|
+
loadOptions: {
|
|
6
|
+
getPlans(this: ILoadOptionsFunctions): Promise<any>;
|
|
7
|
+
getBuckets(this: ILoadOptionsFunctions): Promise<any>;
|
|
8
|
+
getTasks(this: ILoadOptionsFunctions): Promise<any>;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
4
11
|
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
12
|
}
|
|
@@ -43,6 +43,47 @@ class MicrosoftPlanner {
|
|
|
43
43
|
...TaskDescription_1.taskFields,
|
|
44
44
|
],
|
|
45
45
|
};
|
|
46
|
+
this.methods = {
|
|
47
|
+
loadOptions: {
|
|
48
|
+
async getPlans() {
|
|
49
|
+
const plans = await GenericFunctions_1.microsoftApiRequestAllItems.call(this, 'value', 'GET', '/me/planner/plans');
|
|
50
|
+
return plans.map((plan) => ({
|
|
51
|
+
name: plan.title,
|
|
52
|
+
value: plan.id,
|
|
53
|
+
}));
|
|
54
|
+
},
|
|
55
|
+
async getBuckets() {
|
|
56
|
+
const planId = this.getNodeParameter('planId', 0);
|
|
57
|
+
if (!planId) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
const buckets = await GenericFunctions_1.microsoftApiRequestAllItems.call(this, 'value', 'GET', `/planner/plans/${planId}/buckets`);
|
|
61
|
+
return buckets.map((bucket) => ({
|
|
62
|
+
name: bucket.name,
|
|
63
|
+
value: bucket.id,
|
|
64
|
+
}));
|
|
65
|
+
},
|
|
66
|
+
async getTasks() {
|
|
67
|
+
const planId = this.getNodeParameter('planId', 0);
|
|
68
|
+
const bucketId = this.getNodeParameter('bucketId', 0);
|
|
69
|
+
let endpoint = '';
|
|
70
|
+
if (bucketId) {
|
|
71
|
+
endpoint = `/planner/buckets/${bucketId}/tasks`;
|
|
72
|
+
}
|
|
73
|
+
else if (planId) {
|
|
74
|
+
endpoint = `/planner/plans/${planId}/tasks`;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
const tasks = await GenericFunctions_1.microsoftApiRequestAllItems.call(this, 'value', 'GET', endpoint);
|
|
80
|
+
return tasks.map((task) => ({
|
|
81
|
+
name: task.title,
|
|
82
|
+
value: task.id,
|
|
83
|
+
}));
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
46
87
|
}
|
|
47
88
|
async execute() {
|
|
48
89
|
const items = this.getInputData();
|
|
@@ -68,11 +109,13 @@ class MicrosoftPlanner {
|
|
|
68
109
|
if (additionalFields.priority !== undefined) {
|
|
69
110
|
body.priority = additionalFields.priority;
|
|
70
111
|
}
|
|
71
|
-
|
|
72
|
-
|
|
112
|
+
const formattedDueDateTime = (0, GenericFunctions_1.formatDateTime)(additionalFields.dueDateTime);
|
|
113
|
+
if (formattedDueDateTime) {
|
|
114
|
+
body.dueDateTime = formattedDueDateTime;
|
|
73
115
|
}
|
|
74
|
-
|
|
75
|
-
|
|
116
|
+
const formattedStartDateTime = (0, GenericFunctions_1.formatDateTime)(additionalFields.startDateTime);
|
|
117
|
+
if (formattedStartDateTime) {
|
|
118
|
+
body.startDateTime = formattedStartDateTime;
|
|
76
119
|
}
|
|
77
120
|
if (additionalFields.percentComplete !== undefined) {
|
|
78
121
|
body.percentComplete = additionalFields.percentComplete;
|
|
@@ -86,10 +129,16 @@ class MicrosoftPlanner {
|
|
|
86
129
|
if (userId) {
|
|
87
130
|
userIds.push(userId);
|
|
88
131
|
}
|
|
132
|
+
else {
|
|
133
|
+
console.warn(`Could not find user ID for email: ${email}`);
|
|
134
|
+
}
|
|
89
135
|
}
|
|
90
136
|
if (userIds.length > 0) {
|
|
91
137
|
body.assignments = (0, GenericFunctions_1.createAssignmentsObject)(userIds);
|
|
92
138
|
}
|
|
139
|
+
else if (emails.length > 0) {
|
|
140
|
+
console.warn('No valid user IDs found for assignment. Check if User.Read.All permission is granted.');
|
|
141
|
+
}
|
|
93
142
|
}
|
|
94
143
|
const responseData = await GenericFunctions_1.microsoftApiRequest.call(this, 'POST', '/planner/tasks', body);
|
|
95
144
|
// Add description if provided
|
|
@@ -162,11 +211,13 @@ class MicrosoftPlanner {
|
|
|
162
211
|
if (updateFields.priority !== undefined) {
|
|
163
212
|
body.priority = updateFields.priority;
|
|
164
213
|
}
|
|
165
|
-
|
|
166
|
-
|
|
214
|
+
const formattedDueDateTime = (0, GenericFunctions_1.formatDateTime)(updateFields.dueDateTime);
|
|
215
|
+
if (formattedDueDateTime) {
|
|
216
|
+
body.dueDateTime = formattedDueDateTime;
|
|
167
217
|
}
|
|
168
|
-
|
|
169
|
-
|
|
218
|
+
const formattedStartDateTime = (0, GenericFunctions_1.formatDateTime)(updateFields.startDateTime);
|
|
219
|
+
if (formattedStartDateTime) {
|
|
220
|
+
body.startDateTime = formattedStartDateTime;
|
|
170
221
|
}
|
|
171
222
|
if (updateFields.percentComplete !== undefined) {
|
|
172
223
|
body.percentComplete = updateFields.percentComplete;
|
|
@@ -183,10 +234,16 @@ class MicrosoftPlanner {
|
|
|
183
234
|
if (userId) {
|
|
184
235
|
userIds.push(userId);
|
|
185
236
|
}
|
|
237
|
+
else {
|
|
238
|
+
console.warn(`Could not find user ID for email: ${email}`);
|
|
239
|
+
}
|
|
186
240
|
}
|
|
187
241
|
if (userIds.length > 0) {
|
|
188
242
|
body.assignments = (0, GenericFunctions_1.createAssignmentsObject)(userIds);
|
|
189
243
|
}
|
|
244
|
+
else if (emails.length > 0) {
|
|
245
|
+
console.warn('No valid user IDs found for assignment. Check if User.Read.All permission is granted.');
|
|
246
|
+
}
|
|
190
247
|
}
|
|
191
248
|
const responseData = await GenericFunctions_1.microsoftApiRequest.call(this, 'PATCH', `/planner/tasks/${taskId}`, body, {}, undefined, {
|
|
192
249
|
'If-Match': eTag,
|
|
@@ -52,9 +52,12 @@ exports.taskFields = [
|
|
|
52
52
|
// task:create
|
|
53
53
|
// ----------------------------------
|
|
54
54
|
{
|
|
55
|
-
displayName: 'Plan
|
|
55
|
+
displayName: 'Plan',
|
|
56
56
|
name: 'planId',
|
|
57
|
-
type: '
|
|
57
|
+
type: 'options',
|
|
58
|
+
typeOptions: {
|
|
59
|
+
loadOptionsMethod: 'getPlans',
|
|
60
|
+
},
|
|
58
61
|
required: true,
|
|
59
62
|
displayOptions: {
|
|
60
63
|
show: {
|
|
@@ -63,12 +66,15 @@ exports.taskFields = [
|
|
|
63
66
|
},
|
|
64
67
|
},
|
|
65
68
|
default: '',
|
|
66
|
-
description: 'The
|
|
69
|
+
description: 'The plan to which the task belongs',
|
|
67
70
|
},
|
|
68
71
|
{
|
|
69
|
-
displayName: 'Bucket
|
|
72
|
+
displayName: 'Bucket',
|
|
70
73
|
name: 'bucketId',
|
|
71
|
-
type: '
|
|
74
|
+
type: 'options',
|
|
75
|
+
typeOptions: {
|
|
76
|
+
loadOptionsMethod: 'getBuckets',
|
|
77
|
+
},
|
|
72
78
|
required: true,
|
|
73
79
|
displayOptions: {
|
|
74
80
|
show: {
|
|
@@ -77,7 +83,7 @@ exports.taskFields = [
|
|
|
77
83
|
},
|
|
78
84
|
},
|
|
79
85
|
default: '',
|
|
80
|
-
description: 'The
|
|
86
|
+
description: 'The bucket to which the task belongs',
|
|
81
87
|
},
|
|
82
88
|
{
|
|
83
89
|
displayName: 'Title',
|
|
@@ -176,9 +182,12 @@ exports.taskFields = [
|
|
|
176
182
|
// task:get
|
|
177
183
|
// ----------------------------------
|
|
178
184
|
{
|
|
179
|
-
displayName: 'Task
|
|
185
|
+
displayName: 'Task',
|
|
180
186
|
name: 'taskId',
|
|
181
|
-
type: '
|
|
187
|
+
type: 'options',
|
|
188
|
+
typeOptions: {
|
|
189
|
+
loadOptionsMethod: 'getTasks',
|
|
190
|
+
},
|
|
182
191
|
required: true,
|
|
183
192
|
displayOptions: {
|
|
184
193
|
show: {
|
|
@@ -187,7 +196,7 @@ exports.taskFields = [
|
|
|
187
196
|
},
|
|
188
197
|
},
|
|
189
198
|
default: '',
|
|
190
|
-
description: 'The
|
|
199
|
+
description: 'The task to operate on',
|
|
191
200
|
},
|
|
192
201
|
{
|
|
193
202
|
displayName: 'Additional Fields',
|
|
@@ -259,18 +268,24 @@ exports.taskFields = [
|
|
|
259
268
|
},
|
|
260
269
|
options: [
|
|
261
270
|
{
|
|
262
|
-
displayName: 'Plan
|
|
271
|
+
displayName: 'Plan',
|
|
263
272
|
name: 'planId',
|
|
264
|
-
type: '
|
|
273
|
+
type: 'options',
|
|
274
|
+
typeOptions: {
|
|
275
|
+
loadOptionsMethod: 'getPlans',
|
|
276
|
+
},
|
|
265
277
|
default: '',
|
|
266
|
-
description: 'Filter tasks by plan
|
|
278
|
+
description: 'Filter tasks by plan',
|
|
267
279
|
},
|
|
268
280
|
{
|
|
269
|
-
displayName: 'Bucket
|
|
281
|
+
displayName: 'Bucket',
|
|
270
282
|
name: 'bucketId',
|
|
271
|
-
type: '
|
|
283
|
+
type: 'options',
|
|
284
|
+
typeOptions: {
|
|
285
|
+
loadOptionsMethod: 'getBuckets',
|
|
286
|
+
},
|
|
272
287
|
default: '',
|
|
273
|
-
description: 'Filter tasks by bucket
|
|
288
|
+
description: 'Filter tasks by bucket',
|
|
274
289
|
},
|
|
275
290
|
],
|
|
276
291
|
},
|