@blickwerk/n8n-nodes-microsoft-planner 1.1.1 → 1.2.1
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
CHANGED
|
@@ -84,6 +84,13 @@ Configure the Microsoft Planner OAuth2 API credentials in n8n:
|
|
|
84
84
|
4. Click **Connect my account**
|
|
85
85
|
5. Complete the OAuth flow
|
|
86
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
|
+
|
|
87
94
|
## Operations
|
|
88
95
|
|
|
89
96
|
### Task
|
|
@@ -98,10 +105,10 @@ Configure the Microsoft Planner OAuth2 API credentials in n8n:
|
|
|
98
105
|
|
|
99
106
|
### Creating a Task
|
|
100
107
|
|
|
101
|
-
To create a task
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
|
105
112
|
|
|
106
113
|
Optional fields:
|
|
107
114
|
- **Description**: Detailed task description
|
|
@@ -117,14 +124,19 @@ Optional fields:
|
|
|
117
124
|
|
|
118
125
|
### Getting Tasks
|
|
119
126
|
|
|
120
|
-
|
|
121
|
-
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
|
124
134
|
|
|
125
135
|
### Updating a Task
|
|
126
136
|
|
|
127
|
-
To update a task
|
|
137
|
+
To update a task:
|
|
138
|
+
1. Select the **Task** from the dropdown
|
|
139
|
+
2. Update any of these fields:
|
|
128
140
|
- Title
|
|
129
141
|
- Description
|
|
130
142
|
- Priority (via dropdown)
|
|
@@ -134,15 +146,14 @@ To update a task, provide the Task ID and the fields you want to update. You can
|
|
|
134
146
|
- Percent Complete
|
|
135
147
|
- Move to different bucket
|
|
136
148
|
|
|
137
|
-
###
|
|
149
|
+
### How Dynamic Dropdowns Work
|
|
138
150
|
|
|
139
|
-
|
|
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
|
|
140
155
|
|
|
141
|
-
|
|
142
|
-
2. Sign in with your Microsoft account
|
|
143
|
-
3. Use these queries:
|
|
144
|
-
- Get all plans: `GET https://graph.microsoft.com/v1.0/me/planner/plans`
|
|
145
|
-
- 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.
|
|
146
157
|
|
|
147
158
|
## Compatibility
|
|
148
159
|
|
|
@@ -156,6 +167,27 @@ Tested with n8n version 1.0.0 and above.
|
|
|
156
167
|
|
|
157
168
|
## Version History
|
|
158
169
|
|
|
170
|
+
### 1.2.1
|
|
171
|
+
- Fixed Plans not loading (improved API endpoint to fetch from groups)
|
|
172
|
+
- Allow manual ID input in all dropdown fields
|
|
173
|
+
- You can now choose from dropdown OR paste IDs manually
|
|
174
|
+
- Added placeholders and better descriptions for all ID fields
|
|
175
|
+
- Improved error handling for plans loading
|
|
176
|
+
|
|
177
|
+
### 1.2.0
|
|
178
|
+
- **Major UX Improvement**: Added dynamic dropdowns for Plans, Buckets, and Tasks
|
|
179
|
+
- No more manual ID lookup - select from dropdown menus
|
|
180
|
+
- Plans load automatically from your Microsoft 365 account
|
|
181
|
+
- Buckets load dynamically based on selected Plan
|
|
182
|
+
- Tasks load from selected Plan or Bucket
|
|
183
|
+
- Improved user experience with clearer field labels
|
|
184
|
+
|
|
185
|
+
### 1.1.1
|
|
186
|
+
- Fixed startDateTime not being set (was returning null)
|
|
187
|
+
- Fixed user assignment by adding User.Read.All permission requirement
|
|
188
|
+
- Added error logging for failed user lookups
|
|
189
|
+
- Improved README documentation with all required permissions
|
|
190
|
+
|
|
159
191
|
### 1.1.0
|
|
160
192
|
- Added priority dropdown (Urgent, Important, Medium, Low) instead of number input
|
|
161
193
|
- Added user assignment functionality (assign tasks by email)
|
|
@@ -1,5 +1,15 @@
|
|
|
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<{
|
|
7
|
+
name: any;
|
|
8
|
+
value: any;
|
|
9
|
+
}[]>;
|
|
10
|
+
getBuckets(this: ILoadOptionsFunctions): Promise<any>;
|
|
11
|
+
getTasks(this: ILoadOptionsFunctions): Promise<any>;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
4
14
|
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
15
|
}
|
|
@@ -43,6 +43,67 @@ class MicrosoftPlanner {
|
|
|
43
43
|
...TaskDescription_1.taskFields,
|
|
44
44
|
],
|
|
45
45
|
};
|
|
46
|
+
this.methods = {
|
|
47
|
+
loadOptions: {
|
|
48
|
+
async getPlans() {
|
|
49
|
+
try {
|
|
50
|
+
// Try to get plans from groups the user is member of
|
|
51
|
+
const groups = await GenericFunctions_1.microsoftApiRequestAllItems.call(this, 'value', 'GET', '/me/memberOf/microsoft.graph.group?$filter=groupTypes/any(c:c+eq+%27Unified%27)');
|
|
52
|
+
const plans = [];
|
|
53
|
+
for (const group of groups) {
|
|
54
|
+
try {
|
|
55
|
+
const groupPlans = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/groups/${group.id}/planner/plans`);
|
|
56
|
+
if (groupPlans.value && groupPlans.value.length > 0) {
|
|
57
|
+
plans.push(...groupPlans.value);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
// Skip groups without plans
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return plans.map((plan) => ({
|
|
66
|
+
name: plan.title || plan.id,
|
|
67
|
+
value: plan.id,
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.error('Error loading plans:', error);
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
async getBuckets() {
|
|
76
|
+
const planId = this.getNodeParameter('planId', 0);
|
|
77
|
+
if (!planId) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
const buckets = await GenericFunctions_1.microsoftApiRequestAllItems.call(this, 'value', 'GET', `/planner/plans/${planId}/buckets`);
|
|
81
|
+
return buckets.map((bucket) => ({
|
|
82
|
+
name: bucket.name,
|
|
83
|
+
value: bucket.id,
|
|
84
|
+
}));
|
|
85
|
+
},
|
|
86
|
+
async getTasks() {
|
|
87
|
+
const planId = this.getNodeParameter('planId', 0);
|
|
88
|
+
const bucketId = this.getNodeParameter('bucketId', 0);
|
|
89
|
+
let endpoint = '';
|
|
90
|
+
if (bucketId) {
|
|
91
|
+
endpoint = `/planner/buckets/${bucketId}/tasks`;
|
|
92
|
+
}
|
|
93
|
+
else if (planId) {
|
|
94
|
+
endpoint = `/planner/plans/${planId}/tasks`;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
const tasks = await GenericFunctions_1.microsoftApiRequestAllItems.call(this, 'value', 'GET', endpoint);
|
|
100
|
+
return tasks.map((task) => ({
|
|
101
|
+
name: task.title,
|
|
102
|
+
value: task.id,
|
|
103
|
+
}));
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
};
|
|
46
107
|
}
|
|
47
108
|
async execute() {
|
|
48
109
|
const items = this.getInputData();
|
|
@@ -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
57
|
type: 'string',
|
|
58
|
+
typeOptions: {
|
|
59
|
+
loadOptionsMethod: 'getPlans',
|
|
60
|
+
},
|
|
58
61
|
required: true,
|
|
59
62
|
displayOptions: {
|
|
60
63
|
show: {
|
|
@@ -63,12 +66,16 @@ exports.taskFields = [
|
|
|
63
66
|
},
|
|
64
67
|
},
|
|
65
68
|
default: '',
|
|
66
|
-
description: 'The
|
|
69
|
+
description: 'The plan to which the task belongs. Choose from the dropdown or enter a Plan ID manually.',
|
|
70
|
+
placeholder: 'Select from dropdown or paste Plan ID',
|
|
67
71
|
},
|
|
68
72
|
{
|
|
69
|
-
displayName: 'Bucket
|
|
73
|
+
displayName: 'Bucket',
|
|
70
74
|
name: 'bucketId',
|
|
71
75
|
type: 'string',
|
|
76
|
+
typeOptions: {
|
|
77
|
+
loadOptionsMethod: 'getBuckets',
|
|
78
|
+
},
|
|
72
79
|
required: true,
|
|
73
80
|
displayOptions: {
|
|
74
81
|
show: {
|
|
@@ -77,7 +84,8 @@ exports.taskFields = [
|
|
|
77
84
|
},
|
|
78
85
|
},
|
|
79
86
|
default: '',
|
|
80
|
-
description: 'The
|
|
87
|
+
description: 'The bucket to which the task belongs. Choose from the dropdown or enter a Bucket ID manually.',
|
|
88
|
+
placeholder: 'Select from dropdown or paste Bucket ID',
|
|
81
89
|
},
|
|
82
90
|
{
|
|
83
91
|
displayName: 'Title',
|
|
@@ -176,9 +184,12 @@ exports.taskFields = [
|
|
|
176
184
|
// task:get
|
|
177
185
|
// ----------------------------------
|
|
178
186
|
{
|
|
179
|
-
displayName: 'Task
|
|
187
|
+
displayName: 'Task',
|
|
180
188
|
name: 'taskId',
|
|
181
189
|
type: 'string',
|
|
190
|
+
typeOptions: {
|
|
191
|
+
loadOptionsMethod: 'getTasks',
|
|
192
|
+
},
|
|
182
193
|
required: true,
|
|
183
194
|
displayOptions: {
|
|
184
195
|
show: {
|
|
@@ -187,7 +198,8 @@ exports.taskFields = [
|
|
|
187
198
|
},
|
|
188
199
|
},
|
|
189
200
|
default: '',
|
|
190
|
-
description: 'The
|
|
201
|
+
description: 'The task to operate on. Choose from the dropdown or enter a Task ID manually.',
|
|
202
|
+
placeholder: 'Select from dropdown or paste Task ID',
|
|
191
203
|
},
|
|
192
204
|
{
|
|
193
205
|
displayName: 'Additional Fields',
|
|
@@ -259,18 +271,26 @@ exports.taskFields = [
|
|
|
259
271
|
},
|
|
260
272
|
options: [
|
|
261
273
|
{
|
|
262
|
-
displayName: 'Plan
|
|
274
|
+
displayName: 'Plan',
|
|
263
275
|
name: 'planId',
|
|
264
276
|
type: 'string',
|
|
277
|
+
typeOptions: {
|
|
278
|
+
loadOptionsMethod: 'getPlans',
|
|
279
|
+
},
|
|
265
280
|
default: '',
|
|
266
|
-
description: 'Filter tasks by plan ID',
|
|
281
|
+
description: 'Filter tasks by plan. Choose from the dropdown or enter a Plan ID manually.',
|
|
282
|
+
placeholder: 'Select from dropdown or paste Plan ID',
|
|
267
283
|
},
|
|
268
284
|
{
|
|
269
|
-
displayName: 'Bucket
|
|
285
|
+
displayName: 'Bucket',
|
|
270
286
|
name: 'bucketId',
|
|
271
287
|
type: 'string',
|
|
288
|
+
typeOptions: {
|
|
289
|
+
loadOptionsMethod: 'getBuckets',
|
|
290
|
+
},
|
|
272
291
|
default: '',
|
|
273
|
-
description: 'Filter tasks by bucket ID',
|
|
292
|
+
description: 'Filter tasks by bucket. Choose from the dropdown or enter a Bucket ID manually.',
|
|
293
|
+
placeholder: 'Select from dropdown or paste Bucket ID',
|
|
274
294
|
},
|
|
275
295
|
],
|
|
276
296
|
},
|