@blickwerk/n8n-nodes-microsoft-planner 1.5.5 → 1.6.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/LICENSE.md +21 -21
- package/README.md +527 -459
- package/dist/credentials/MicrosoftPlannerOAuth2Api.credentials.d.ts +2 -2
- package/dist/credentials/MicrosoftPlannerOAuth2Api.credentials.js +9 -28
- package/dist/credentials/MicrosoftPlannerOAuth2Api.credentials.js.map +1 -0
- package/dist/nodes/MicrosoftPlanner/BucketDescription.js +30 -23
- package/dist/nodes/MicrosoftPlanner/BucketDescription.js.map +1 -0
- package/dist/nodes/MicrosoftPlanner/CommentDescription.js +10 -14
- package/dist/nodes/MicrosoftPlanner/CommentDescription.js.map +1 -0
- package/dist/nodes/MicrosoftPlanner/GenericFunctions.d.ts +15 -18
- package/dist/nodes/MicrosoftPlanner/GenericFunctions.js +222 -70
- package/dist/nodes/MicrosoftPlanner/GenericFunctions.js.map +1 -0
- package/dist/nodes/MicrosoftPlanner/MicrosoftPlanner.node.d.ts +6 -10
- package/dist/nodes/MicrosoftPlanner/MicrosoftPlanner.node.js +100 -826
- package/dist/nodes/MicrosoftPlanner/MicrosoftPlanner.node.js.map +1 -0
- package/dist/nodes/MicrosoftPlanner/MicrosoftPlanner.node.json +18 -0
- package/dist/nodes/MicrosoftPlanner/PlanDescription.js +207 -57
- package/dist/nodes/MicrosoftPlanner/PlanDescription.js.map +1 -0
- package/dist/nodes/MicrosoftPlanner/SharedFields.d.ts +10 -0
- package/dist/nodes/MicrosoftPlanner/SharedFields.js +316 -0
- package/dist/nodes/MicrosoftPlanner/SharedFields.js.map +1 -0
- package/dist/nodes/MicrosoftPlanner/TaskDescription.d.ts +1 -1
- package/dist/nodes/MicrosoftPlanner/TaskDescription.js +226 -583
- package/dist/nodes/MicrosoftPlanner/TaskDescription.js.map +1 -0
- package/dist/nodes/MicrosoftPlanner/actions/bucket.d.ts +2 -0
- package/dist/nodes/MicrosoftPlanner/actions/bucket.js +72 -0
- package/dist/nodes/MicrosoftPlanner/actions/bucket.js.map +1 -0
- package/dist/nodes/MicrosoftPlanner/actions/comment.d.ts +2 -0
- package/dist/nodes/MicrosoftPlanner/actions/comment.js +100 -0
- package/dist/nodes/MicrosoftPlanner/actions/comment.js.map +1 -0
- package/dist/nodes/MicrosoftPlanner/actions/plan.d.ts +4 -0
- package/dist/nodes/MicrosoftPlanner/actions/plan.js +116 -0
- package/dist/nodes/MicrosoftPlanner/actions/plan.js.map +1 -0
- package/dist/nodes/MicrosoftPlanner/actions/task.d.ts +4 -0
- package/dist/nodes/MicrosoftPlanner/actions/task.js +392 -0
- package/dist/nodes/MicrosoftPlanner/actions/task.js.map +1 -0
- package/dist/nodes/MicrosoftPlanner/planner.svg +51 -51
- package/dist/package.json +60 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +60 -63
|
@@ -7,21 +7,29 @@ const TaskDescription_1 = require("./TaskDescription");
|
|
|
7
7
|
const PlanDescription_1 = require("./PlanDescription");
|
|
8
8
|
const BucketDescription_1 = require("./BucketDescription");
|
|
9
9
|
const CommentDescription_1 = require("./CommentDescription");
|
|
10
|
+
const task_1 = require("./actions/task");
|
|
11
|
+
const plan_1 = require("./actions/plan");
|
|
12
|
+
const bucket_1 = require("./actions/bucket");
|
|
13
|
+
const comment_1 = require("./actions/comment");
|
|
14
|
+
function matchesFilter(name, filter) {
|
|
15
|
+
return !filter || name.toLowerCase().includes(filter.toLowerCase());
|
|
16
|
+
}
|
|
10
17
|
class MicrosoftPlanner {
|
|
11
18
|
constructor() {
|
|
12
19
|
this.description = {
|
|
13
20
|
displayName: 'Microsoft Planner',
|
|
14
21
|
name: 'microsoftPlanner',
|
|
15
|
-
icon: 'file:planner.svg',
|
|
22
|
+
icon: { light: 'file:planner.svg', dark: 'file:planner.svg' },
|
|
16
23
|
group: ['transform'],
|
|
17
24
|
version: 1,
|
|
18
25
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
19
|
-
description: '
|
|
26
|
+
description: 'Manage plans, buckets, tasks and comments in Microsoft Planner',
|
|
20
27
|
defaults: {
|
|
21
28
|
name: 'Microsoft Planner',
|
|
22
29
|
},
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
usableAsTool: true,
|
|
31
|
+
inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
32
|
+
outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
25
33
|
credentials: [
|
|
26
34
|
{
|
|
27
35
|
name: 'microsoftPlannerOAuth2Api',
|
|
@@ -35,14 +43,6 @@ class MicrosoftPlanner {
|
|
|
35
43
|
type: 'options',
|
|
36
44
|
noDataExpression: true,
|
|
37
45
|
options: [
|
|
38
|
-
{
|
|
39
|
-
name: 'Task',
|
|
40
|
-
value: 'task',
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
name: 'Plan',
|
|
44
|
-
value: 'plan',
|
|
45
|
-
},
|
|
46
46
|
{
|
|
47
47
|
name: 'Bucket',
|
|
48
48
|
value: 'bucket',
|
|
@@ -51,10 +51,17 @@ class MicrosoftPlanner {
|
|
|
51
51
|
name: 'Comment',
|
|
52
52
|
value: 'comment',
|
|
53
53
|
},
|
|
54
|
+
{
|
|
55
|
+
name: 'Plan',
|
|
56
|
+
value: 'plan',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'Task',
|
|
60
|
+
value: 'task',
|
|
61
|
+
},
|
|
54
62
|
],
|
|
55
63
|
default: 'task',
|
|
56
64
|
},
|
|
57
|
-
// Grouped by resource so they appear nicely in the n8n UI
|
|
58
65
|
...TaskDescription_1.taskOperations,
|
|
59
66
|
...TaskDescription_1.taskFields,
|
|
60
67
|
...PlanDescription_1.planOperations,
|
|
@@ -67,88 +74,73 @@ class MicrosoftPlanner {
|
|
|
67
74
|
};
|
|
68
75
|
this.methods = {
|
|
69
76
|
listSearch: {
|
|
70
|
-
async getBuckets() {
|
|
77
|
+
async getBuckets(filter) {
|
|
78
|
+
const planId = this.getCurrentNodeParameter('planId');
|
|
79
|
+
if (!planId) {
|
|
80
|
+
return { results: [] };
|
|
81
|
+
}
|
|
71
82
|
try {
|
|
72
|
-
const planId = this.getNodeParameter('planId', 0);
|
|
73
|
-
if (!planId) {
|
|
74
|
-
return { results: [] };
|
|
75
|
-
}
|
|
76
83
|
const buckets = await GenericFunctions_1.microsoftApiRequestAllItems.call(this, 'value', 'GET', `/planner/plans/${planId}/buckets`);
|
|
77
|
-
if (!buckets || buckets.length === 0) {
|
|
78
|
-
return { results: [] };
|
|
79
|
-
}
|
|
80
84
|
return {
|
|
81
|
-
results: buckets
|
|
85
|
+
results: buckets
|
|
86
|
+
.map((bucket) => ({
|
|
82
87
|
name: bucket.name || bucket.id,
|
|
83
88
|
value: bucket.id,
|
|
84
|
-
}))
|
|
89
|
+
}))
|
|
90
|
+
.filter((bucket) => matchesFilter(bucket.name, filter)),
|
|
85
91
|
};
|
|
86
92
|
}
|
|
87
|
-
catch
|
|
93
|
+
catch {
|
|
88
94
|
return { results: [] };
|
|
89
95
|
}
|
|
90
96
|
},
|
|
91
|
-
async
|
|
97
|
+
async getGroups(filter) {
|
|
92
98
|
try {
|
|
93
|
-
const
|
|
94
|
-
// Try to get bucketId - might be undefined or an object
|
|
95
|
-
let bucketIdValue = '';
|
|
96
|
-
try {
|
|
97
|
-
const bucketId = this.getNodeParameter('bucketId', 0);
|
|
98
|
-
if (typeof bucketId === 'string') {
|
|
99
|
-
bucketIdValue = bucketId;
|
|
100
|
-
}
|
|
101
|
-
else if (bucketId && typeof bucketId === 'object' && 'value' in bucketId) {
|
|
102
|
-
bucketIdValue = bucketId.value;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
// bucketId might not exist yet, that's ok
|
|
107
|
-
}
|
|
108
|
-
let endpoint = '';
|
|
109
|
-
if (bucketIdValue) {
|
|
110
|
-
endpoint = `/planner/buckets/${bucketIdValue}/tasks`;
|
|
111
|
-
}
|
|
112
|
-
else if (planId) {
|
|
113
|
-
endpoint = `/planner/plans/${planId}/tasks`;
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
return { results: [] };
|
|
117
|
-
}
|
|
118
|
-
const tasks = await GenericFunctions_1.microsoftApiRequestAllItems.call(this, 'value', 'GET', endpoint);
|
|
119
|
-
if (!tasks || tasks.length === 0) {
|
|
120
|
-
return { results: [] };
|
|
121
|
-
}
|
|
99
|
+
const groups = await GenericFunctions_1.microsoftApiRequestAllItems.call(this, 'value', 'GET', '/me/memberOf/$/microsoft.graph.group', {}, { $select: 'id,displayName,groupTypes' });
|
|
122
100
|
return {
|
|
123
|
-
results:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
101
|
+
results: groups
|
|
102
|
+
.filter((group) => { var _a; return (_a = group.groupTypes) === null || _a === void 0 ? void 0 : _a.includes('Unified'); })
|
|
103
|
+
.map((group) => ({
|
|
104
|
+
name: group.displayName || group.id,
|
|
105
|
+
value: group.id,
|
|
106
|
+
}))
|
|
107
|
+
.filter((group) => matchesFilter(group.name, filter))
|
|
108
|
+
.sort((a, b) => a.name.localeCompare(b.name)),
|
|
127
109
|
};
|
|
128
110
|
}
|
|
129
|
-
catch
|
|
111
|
+
catch {
|
|
130
112
|
return { results: [] };
|
|
131
113
|
}
|
|
132
114
|
},
|
|
133
|
-
|
|
115
|
+
},
|
|
116
|
+
loadOptions: {
|
|
117
|
+
async getCategories() {
|
|
118
|
+
var _a;
|
|
119
|
+
let descriptions = {};
|
|
134
120
|
try {
|
|
135
|
-
|
|
136
|
-
if (!
|
|
137
|
-
|
|
121
|
+
let planId = this.getCurrentNodeParameter('planId');
|
|
122
|
+
if (!planId) {
|
|
123
|
+
const taskId = this.getCurrentNodeParameter('taskId', { extractValue: true });
|
|
124
|
+
if (taskId) {
|
|
125
|
+
const task = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}`, {}, { $select: 'planId' });
|
|
126
|
+
planId = task.planId;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (planId) {
|
|
130
|
+
const details = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/plans/${planId}/details`);
|
|
131
|
+
descriptions = (_a = details.categoryDescriptions) !== null && _a !== void 0 ? _a : {};
|
|
138
132
|
}
|
|
139
|
-
// Filter for Microsoft 365 Groups (Unified Groups)
|
|
140
|
-
// Security groups are not supported for Planner plans
|
|
141
|
-
const unifiedGroups = groups.filter((group) => { var _a; return (_a = group.groupTypes) === null || _a === void 0 ? void 0 : _a.includes('Unified'); });
|
|
142
|
-
return {
|
|
143
|
-
results: unifiedGroups.map((group) => ({
|
|
144
|
-
name: group.displayName || group.id,
|
|
145
|
-
value: group.id,
|
|
146
|
-
})),
|
|
147
|
-
};
|
|
148
133
|
}
|
|
149
|
-
catch
|
|
150
|
-
return { results: [] };
|
|
134
|
+
catch {
|
|
151
135
|
}
|
|
136
|
+
return Object.keys(GenericFunctions_1.CATEGORY_COLORS).map((category) => {
|
|
137
|
+
const label = descriptions[category];
|
|
138
|
+
const color = GenericFunctions_1.CATEGORY_COLORS[category];
|
|
139
|
+
return {
|
|
140
|
+
name: label ? `${label} (${color})` : `${color} (${category})`,
|
|
141
|
+
value: category,
|
|
142
|
+
};
|
|
143
|
+
});
|
|
152
144
|
},
|
|
153
145
|
},
|
|
154
146
|
};
|
|
@@ -158,769 +150,51 @@ class MicrosoftPlanner {
|
|
|
158
150
|
const returnData = [];
|
|
159
151
|
const resource = this.getNodeParameter('resource', 0);
|
|
160
152
|
const operation = this.getNodeParameter('operation', 0);
|
|
153
|
+
const userCache = new Map();
|
|
161
154
|
for (let i = 0; i < items.length; i++) {
|
|
162
155
|
try {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (additionalFields.priority !== undefined) {
|
|
181
|
-
body.priority = additionalFields.priority;
|
|
182
|
-
}
|
|
183
|
-
const formattedDueDateTime = (0, GenericFunctions_1.formatDateTime)(additionalFields.dueDateTime);
|
|
184
|
-
if (formattedDueDateTime) {
|
|
185
|
-
body.dueDateTime = formattedDueDateTime;
|
|
186
|
-
}
|
|
187
|
-
const formattedStartDateTime = (0, GenericFunctions_1.formatDateTime)(additionalFields.startDateTime);
|
|
188
|
-
if (formattedStartDateTime) {
|
|
189
|
-
body.startDateTime = formattedStartDateTime;
|
|
190
|
-
}
|
|
191
|
-
if (additionalFields.percentComplete !== undefined) {
|
|
192
|
-
body.percentComplete = additionalFields.percentComplete;
|
|
193
|
-
}
|
|
194
|
-
// Handle assignments
|
|
195
|
-
if (additionalFields.assignments) {
|
|
196
|
-
const emails = (0, GenericFunctions_1.parseAssignments)(additionalFields.assignments);
|
|
197
|
-
const userIds = [];
|
|
198
|
-
for (const email of emails) {
|
|
199
|
-
const userId = await GenericFunctions_1.getUserIdByEmail.call(this, email);
|
|
200
|
-
userIds.push(userId);
|
|
201
|
-
}
|
|
202
|
-
if (userIds.length > 0) {
|
|
203
|
-
body.assignments = (0, GenericFunctions_1.createAssignmentsObject)(userIds);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
const responseData = await GenericFunctions_1.microsoftApiRequest.call(this, 'POST', '/planner/tasks', body);
|
|
207
|
-
// Add description, references, or checklist if provided
|
|
208
|
-
if (additionalFields.description ||
|
|
209
|
-
additionalFields.attachmentsUi ||
|
|
210
|
-
additionalFields.checklistUi) {
|
|
211
|
-
const details = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${responseData.id}/details`);
|
|
212
|
-
const eTag = (0, GenericFunctions_1.cleanETag)(details['@odata.etag']);
|
|
213
|
-
const detailsBody = {};
|
|
214
|
-
if (additionalFields.description) {
|
|
215
|
-
detailsBody.description = additionalFields.description;
|
|
216
|
-
responseData.description = additionalFields.description;
|
|
217
|
-
}
|
|
218
|
-
// Handle Attachments (Manual or JSON)
|
|
219
|
-
const referencesBody = {};
|
|
220
|
-
const attachmentsUi = additionalFields.attachmentsUi;
|
|
221
|
-
if (attachmentsUi && attachmentsUi.attachments) {
|
|
222
|
-
const attachments = attachmentsUi.attachments;
|
|
223
|
-
const mode = attachments.mode;
|
|
224
|
-
if (mode === 'manual' && attachments.items) {
|
|
225
|
-
const items = attachments.items;
|
|
226
|
-
if (items && items.reference) {
|
|
227
|
-
const referenceList = items.reference;
|
|
228
|
-
for (const reference of referenceList) {
|
|
229
|
-
const url = reference.url;
|
|
230
|
-
const alias = reference.alias;
|
|
231
|
-
const type = reference.type;
|
|
232
|
-
const encodedUrl = (0, GenericFunctions_1.encodeReferenceKey)(url);
|
|
233
|
-
referencesBody[encodedUrl] = {
|
|
234
|
-
'@odata.type': '#microsoft.graph.plannerExternalReference',
|
|
235
|
-
alias,
|
|
236
|
-
type,
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
else if (mode === 'json' && attachments.json) {
|
|
242
|
-
const referencesJson = attachments.json;
|
|
243
|
-
let referenceList;
|
|
244
|
-
try {
|
|
245
|
-
referenceList = typeof referencesJson === 'string'
|
|
246
|
-
? JSON.parse(referencesJson)
|
|
247
|
-
: referencesJson;
|
|
248
|
-
if (!Array.isArray(referenceList))
|
|
249
|
-
throw new Error('Not an array');
|
|
250
|
-
}
|
|
251
|
-
catch (error) {
|
|
252
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid JSON in Attachments (JSON) field. It must be an array of objects.', { itemIndex: i });
|
|
253
|
-
}
|
|
254
|
-
for (const reference of referenceList) {
|
|
255
|
-
const url = reference.url;
|
|
256
|
-
if (!url)
|
|
257
|
-
continue;
|
|
258
|
-
const encodedUrl = (0, GenericFunctions_1.encodeReferenceKey)(url);
|
|
259
|
-
referencesBody[encodedUrl] = {
|
|
260
|
-
'@odata.type': '#microsoft.graph.plannerExternalReference',
|
|
261
|
-
alias: reference.alias || '',
|
|
262
|
-
type: reference.type || 'Other',
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
if (Object.keys(referencesBody).length > 0) {
|
|
268
|
-
detailsBody.references = referencesBody;
|
|
269
|
-
}
|
|
270
|
-
// Handle Checklist (Manual or JSON)
|
|
271
|
-
const checklistBody = {};
|
|
272
|
-
const checklistUi = additionalFields.checklistUi;
|
|
273
|
-
if (checklistUi && checklistUi.checklist) {
|
|
274
|
-
const checklist = checklistUi.checklist;
|
|
275
|
-
const mode = checklist.mode;
|
|
276
|
-
if (mode === 'manual' && checklist.items) {
|
|
277
|
-
const items = checklist.items;
|
|
278
|
-
if (items && items.item) {
|
|
279
|
-
const checklistList = items.item;
|
|
280
|
-
for (const item of checklistList) {
|
|
281
|
-
const id = item.id || (0, GenericFunctions_1.generateGuid)();
|
|
282
|
-
checklistBody[id] = {
|
|
283
|
-
'@odata.type': '#microsoft.graph.plannerChecklistItem',
|
|
284
|
-
title: item.title,
|
|
285
|
-
isChecked: item.isChecked,
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
else if (mode === 'json' && checklist.json) {
|
|
291
|
-
const checklistJson = checklist.json;
|
|
292
|
-
let checklistList;
|
|
293
|
-
try {
|
|
294
|
-
checklistList = typeof checklistJson === 'string'
|
|
295
|
-
? JSON.parse(checklistJson)
|
|
296
|
-
: checklistJson;
|
|
297
|
-
if (!Array.isArray(checklistList))
|
|
298
|
-
throw new Error('Not an array');
|
|
299
|
-
}
|
|
300
|
-
catch (error) {
|
|
301
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid JSON in Checklist (JSON) field. It must be an array of objects.', { itemIndex: i });
|
|
302
|
-
}
|
|
303
|
-
for (const item of checklistList) {
|
|
304
|
-
const title = item.title;
|
|
305
|
-
if (!title)
|
|
306
|
-
continue;
|
|
307
|
-
const id = item.id || (0, GenericFunctions_1.generateGuid)();
|
|
308
|
-
checklistBody[id] = {
|
|
309
|
-
'@odata.type': '#microsoft.graph.plannerChecklistItem',
|
|
310
|
-
title,
|
|
311
|
-
isChecked: !!item.isChecked,
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
if (Object.keys(checklistBody).length > 0) {
|
|
317
|
-
detailsBody.checklist = checklistBody;
|
|
318
|
-
}
|
|
319
|
-
await GenericFunctions_1.microsoftApiRequest.call(this, 'PATCH', `/planner/tasks/${responseData.id}/details`, detailsBody, {}, undefined, {
|
|
320
|
-
'If-Match': eTag,
|
|
321
|
-
});
|
|
322
|
-
// Fetch latest details to return in response
|
|
323
|
-
const finalDetails = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${responseData.id}/details`);
|
|
324
|
-
responseData.details = finalDetails;
|
|
325
|
-
}
|
|
326
|
-
returnData.push(responseData);
|
|
327
|
-
}
|
|
328
|
-
// ----------------------------------
|
|
329
|
-
// task:get
|
|
330
|
-
// ----------------------------------
|
|
331
|
-
if (operation === 'get') {
|
|
332
|
-
const taskIdParam = this.getNodeParameter('taskId', i);
|
|
333
|
-
const taskId = typeof taskIdParam === 'string' ? taskIdParam : taskIdParam.value;
|
|
334
|
-
const additionalFields = this.getNodeParameter('additionalFields', i);
|
|
335
|
-
const responseData = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}`);
|
|
336
|
-
if (additionalFields.includeDetails) {
|
|
337
|
-
const details = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}/details`);
|
|
338
|
-
responseData.details = details;
|
|
339
|
-
}
|
|
340
|
-
returnData.push(responseData);
|
|
341
|
-
}
|
|
342
|
-
// ----------------------------------
|
|
343
|
-
// task:getAll
|
|
344
|
-
// ----------------------------------
|
|
345
|
-
// Always return all tasks for the given scope; Graph does not honor $top/$limit
|
|
346
|
-
if (operation === 'getAll') {
|
|
347
|
-
const filterBy = this.getNodeParameter('filterBy', i);
|
|
348
|
-
const planId = this.getNodeParameter('planId', i);
|
|
349
|
-
let endpoint = '';
|
|
350
|
-
if (filterBy === 'plan') {
|
|
351
|
-
endpoint = `/planner/plans/${planId}/tasks`;
|
|
352
|
-
}
|
|
353
|
-
else if (filterBy === 'bucket') {
|
|
354
|
-
const bucketIdParam = this.getNodeParameter('bucketId', i);
|
|
355
|
-
const bucketIdValue = typeof bucketIdParam === 'string'
|
|
356
|
-
? bucketIdParam
|
|
357
|
-
: bucketIdParam.value;
|
|
358
|
-
endpoint = `/planner/buckets/${bucketIdValue}/tasks`;
|
|
359
|
-
}
|
|
360
|
-
else {
|
|
361
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'You must specify either a Plan ID or Bucket ID to retrieve tasks', { itemIndex: i });
|
|
362
|
-
}
|
|
363
|
-
const additionalFields = this.getNodeParameter('additionalFields', i);
|
|
364
|
-
const qs = {};
|
|
365
|
-
if (additionalFields === null || additionalFields === void 0 ? void 0 : additionalFields.select) {
|
|
366
|
-
qs.$select = additionalFields.select;
|
|
367
|
-
}
|
|
368
|
-
const responseData = await GenericFunctions_1.microsoftApiRequestAllItems.call(this, 'value', 'GET', endpoint, {}, qs);
|
|
369
|
-
returnData.push(...responseData);
|
|
370
|
-
}
|
|
371
|
-
// ----------------------------------
|
|
372
|
-
// task:update
|
|
373
|
-
// ----------------------------------
|
|
374
|
-
if (operation === 'update') {
|
|
375
|
-
const taskIdParam = this.getNodeParameter('taskId', i);
|
|
376
|
-
const taskId = typeof taskIdParam === 'string' ? taskIdParam : taskIdParam.value;
|
|
377
|
-
const updateFields = this.getNodeParameter('updateFields', i);
|
|
378
|
-
// Get current task to retrieve eTag
|
|
379
|
-
const currentTask = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}`);
|
|
380
|
-
const eTag = (0, GenericFunctions_1.cleanETag)(currentTask['@odata.etag']);
|
|
381
|
-
const body = {};
|
|
382
|
-
if (updateFields.title) {
|
|
383
|
-
body.title = updateFields.title;
|
|
384
|
-
}
|
|
385
|
-
if (updateFields.priority !== undefined) {
|
|
386
|
-
body.priority = updateFields.priority;
|
|
387
|
-
}
|
|
388
|
-
const formattedDueDateTime = (0, GenericFunctions_1.formatDateTime)(updateFields.dueDateTime);
|
|
389
|
-
if (formattedDueDateTime) {
|
|
390
|
-
body.dueDateTime = formattedDueDateTime;
|
|
391
|
-
}
|
|
392
|
-
const formattedStartDateTime = (0, GenericFunctions_1.formatDateTime)(updateFields.startDateTime);
|
|
393
|
-
if (formattedStartDateTime) {
|
|
394
|
-
body.startDateTime = formattedStartDateTime;
|
|
395
|
-
}
|
|
396
|
-
if (updateFields.percentComplete !== undefined) {
|
|
397
|
-
body.percentComplete = updateFields.percentComplete;
|
|
398
|
-
}
|
|
399
|
-
if (updateFields.bucketId) {
|
|
400
|
-
body.bucketId = updateFields.bucketId;
|
|
401
|
-
}
|
|
402
|
-
// Handle assignments
|
|
403
|
-
if (updateFields.assignments) {
|
|
404
|
-
const emails = (0, GenericFunctions_1.parseAssignments)(updateFields.assignments);
|
|
405
|
-
const userIds = [];
|
|
406
|
-
for (const email of emails) {
|
|
407
|
-
const userId = await GenericFunctions_1.getUserIdByEmail.call(this, email);
|
|
408
|
-
userIds.push(userId);
|
|
409
|
-
}
|
|
410
|
-
if (userIds.length > 0) {
|
|
411
|
-
body.assignments = (0, GenericFunctions_1.createAssignmentsObject)(userIds);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
// Only send PATCH request if there are fields to update (excluding description)
|
|
415
|
-
if (Object.keys(body).length > 0) {
|
|
416
|
-
await GenericFunctions_1.microsoftApiRequest.call(this, 'PATCH', `/planner/tasks/${taskId}`, body, {}, undefined, {
|
|
417
|
-
'If-Match': eTag,
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
// Update description, references, or checklist if provided
|
|
421
|
-
if (updateFields.description ||
|
|
422
|
-
updateFields.attachmentsUi ||
|
|
423
|
-
updateFields.checklistUi) {
|
|
424
|
-
const details = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}/details`);
|
|
425
|
-
const detailsETag = (0, GenericFunctions_1.cleanETag)(details['@odata.etag']);
|
|
426
|
-
const detailsBody = {};
|
|
427
|
-
if (updateFields.description) {
|
|
428
|
-
detailsBody.description = updateFields.description;
|
|
429
|
-
}
|
|
430
|
-
const referencesBody = {};
|
|
431
|
-
// 1. If operationMode is 'replace', set all existing references to null
|
|
432
|
-
const attachmentsUiForReplace = updateFields.attachmentsUi;
|
|
433
|
-
const attachmentsForReplace = attachmentsUiForReplace === null || attachmentsUiForReplace === void 0 ? void 0 : attachmentsUiForReplace.attachments;
|
|
434
|
-
const attachmentsOperationMode = attachmentsForReplace === null || attachmentsForReplace === void 0 ? void 0 : attachmentsForReplace.operationMode;
|
|
435
|
-
if (attachmentsOperationMode === 'replace' && details.references) {
|
|
436
|
-
for (const key of Object.keys(details.references)) {
|
|
437
|
-
referencesBody[key] = null;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
// 2. Add new/updated references (Manual or JSON)
|
|
441
|
-
const attachmentsUi = updateFields.attachmentsUi;
|
|
442
|
-
if (attachmentsUi && attachmentsUi.attachments) {
|
|
443
|
-
const attachments = attachmentsUi.attachments;
|
|
444
|
-
const mode = attachments.mode;
|
|
445
|
-
if (mode === 'manual' && attachments.items) {
|
|
446
|
-
const items = attachments.items;
|
|
447
|
-
if (items && items.reference) {
|
|
448
|
-
const referenceList = items.reference;
|
|
449
|
-
for (const reference of referenceList) {
|
|
450
|
-
const url = reference.url;
|
|
451
|
-
const alias = reference.alias;
|
|
452
|
-
const type = reference.type;
|
|
453
|
-
const encodedUrl = (0, GenericFunctions_1.encodeReferenceKey)(url);
|
|
454
|
-
referencesBody[encodedUrl] = {
|
|
455
|
-
'@odata.type': '#microsoft.graph.plannerExternalReference',
|
|
456
|
-
alias,
|
|
457
|
-
type,
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
else if (mode === 'json' && attachments.json) {
|
|
463
|
-
const referencesJson = attachments.json;
|
|
464
|
-
let referenceList;
|
|
465
|
-
try {
|
|
466
|
-
referenceList = typeof referencesJson === 'string'
|
|
467
|
-
? JSON.parse(referencesJson)
|
|
468
|
-
: referencesJson;
|
|
469
|
-
if (!Array.isArray(referenceList))
|
|
470
|
-
throw new Error('Not an array');
|
|
471
|
-
}
|
|
472
|
-
catch (error) {
|
|
473
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid JSON in Attachments (JSON) field. It must be an array of objects.', { itemIndex: i });
|
|
474
|
-
}
|
|
475
|
-
for (const reference of referenceList) {
|
|
476
|
-
const url = reference.url;
|
|
477
|
-
if (!url)
|
|
478
|
-
continue;
|
|
479
|
-
const encodedUrl = (0, GenericFunctions_1.encodeReferenceKey)(url);
|
|
480
|
-
referencesBody[encodedUrl] = {
|
|
481
|
-
'@odata.type': '#microsoft.graph.plannerExternalReference',
|
|
482
|
-
alias: reference.alias || '',
|
|
483
|
-
type: reference.type || 'Other',
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
if (Object.keys(referencesBody).length > 0) {
|
|
489
|
-
detailsBody.references = referencesBody;
|
|
490
|
-
}
|
|
491
|
-
const checklistBody = {};
|
|
492
|
-
// 1. If operationMode is 'replace', set all existing checklist items to null
|
|
493
|
-
const checklistUiForReplace = updateFields.checklistUi;
|
|
494
|
-
const checklistForReplace = checklistUiForReplace === null || checklistUiForReplace === void 0 ? void 0 : checklistUiForReplace.checklist;
|
|
495
|
-
const checklistOperationMode = checklistForReplace === null || checklistForReplace === void 0 ? void 0 : checklistForReplace.operationMode;
|
|
496
|
-
if (checklistOperationMode === 'replace' && details.checklist) {
|
|
497
|
-
for (const key of Object.keys(details.checklist)) {
|
|
498
|
-
checklistBody[key] = null;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
// 2. Add new/updated checklist items (Manual or JSON)
|
|
502
|
-
const checklistUi = updateFields.checklistUi;
|
|
503
|
-
if (checklistUi && checklistUi.checklist) {
|
|
504
|
-
const checklist = checklistUi.checklist;
|
|
505
|
-
const mode = checklist.mode;
|
|
506
|
-
if (mode === 'manual' && checklist.items) {
|
|
507
|
-
const items = checklist.items;
|
|
508
|
-
if (items && items.item) {
|
|
509
|
-
const checklistList = items.item;
|
|
510
|
-
for (const item of checklistList) {
|
|
511
|
-
const id = item.id || (0, GenericFunctions_1.generateGuid)();
|
|
512
|
-
checklistBody[id] = {
|
|
513
|
-
'@odata.type': '#microsoft.graph.plannerChecklistItem',
|
|
514
|
-
title: item.title,
|
|
515
|
-
isChecked: item.isChecked,
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
else if (mode === 'json' && checklist.json) {
|
|
521
|
-
const checklistJson = checklist.json;
|
|
522
|
-
let checklistList;
|
|
523
|
-
try {
|
|
524
|
-
checklistList = typeof checklistJson === 'string'
|
|
525
|
-
? JSON.parse(checklistJson)
|
|
526
|
-
: checklistJson;
|
|
527
|
-
if (!Array.isArray(checklistList))
|
|
528
|
-
throw new Error('Not an array');
|
|
529
|
-
}
|
|
530
|
-
catch (error) {
|
|
531
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid JSON in Checklist (JSON) field. It must be an array of objects.', { itemIndex: i });
|
|
532
|
-
}
|
|
533
|
-
for (const item of checklistList) {
|
|
534
|
-
const title = item.title;
|
|
535
|
-
if (!title)
|
|
536
|
-
continue;
|
|
537
|
-
const id = item.id || (0, GenericFunctions_1.generateGuid)();
|
|
538
|
-
checklistBody[id] = {
|
|
539
|
-
'@odata.type': '#microsoft.graph.plannerChecklistItem',
|
|
540
|
-
title,
|
|
541
|
-
isChecked: !!item.isChecked,
|
|
542
|
-
};
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
if (Object.keys(checklistBody).length > 0) {
|
|
547
|
-
detailsBody.checklist = checklistBody;
|
|
548
|
-
}
|
|
549
|
-
await GenericFunctions_1.microsoftApiRequest.call(this, 'PATCH', `/planner/tasks/${taskId}/details`, detailsBody, {}, undefined, {
|
|
550
|
-
'If-Match': detailsETag,
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
// Fetch updated task to return complete data
|
|
554
|
-
const responseData = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}`);
|
|
555
|
-
// If description (task details) was updated, also return latest details
|
|
556
|
-
if (Object.prototype.hasOwnProperty.call(updateFields, 'description') ||
|
|
557
|
-
Object.prototype.hasOwnProperty.call(updateFields, 'attachmentsUi') ||
|
|
558
|
-
Object.prototype.hasOwnProperty.call(updateFields, 'checklistUi')) {
|
|
559
|
-
const details = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}/details`);
|
|
560
|
-
responseData.details = details;
|
|
561
|
-
}
|
|
562
|
-
returnData.push(responseData);
|
|
563
|
-
}
|
|
564
|
-
// ----------------------------------
|
|
565
|
-
// task:delete
|
|
566
|
-
// ----------------------------------
|
|
567
|
-
if (operation === 'delete') {
|
|
568
|
-
const taskIdParam = this.getNodeParameter('taskId', i);
|
|
569
|
-
const taskId = typeof taskIdParam === 'string' ? taskIdParam : taskIdParam.value;
|
|
570
|
-
// Get current task to retrieve eTag
|
|
571
|
-
const currentTask = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}`);
|
|
572
|
-
const eTag = (0, GenericFunctions_1.cleanETag)(currentTask['@odata.etag']);
|
|
573
|
-
await GenericFunctions_1.microsoftApiRequest.call(this, 'DELETE', `/planner/tasks/${taskId}`, {}, {}, undefined, {
|
|
574
|
-
'If-Match': eTag,
|
|
575
|
-
});
|
|
576
|
-
returnData.push({ success: true, taskId });
|
|
577
|
-
}
|
|
578
|
-
// ----------------------------------
|
|
579
|
-
// task:getFiles
|
|
580
|
-
// ----------------------------------
|
|
581
|
-
if (operation === 'getFiles') {
|
|
582
|
-
const taskIdParam = this.getNodeParameter('taskId', i);
|
|
583
|
-
const taskId = typeof taskIdParam === 'string' ? taskIdParam : taskIdParam.value;
|
|
584
|
-
// Get task details
|
|
585
|
-
const details = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}/details`);
|
|
586
|
-
const references = details.references || {};
|
|
587
|
-
const files = Object.keys(references).map((encodedUrl) => {
|
|
588
|
-
// Decode the URL
|
|
589
|
-
const url = decodeURIComponent(encodedUrl);
|
|
590
|
-
return {
|
|
591
|
-
url,
|
|
592
|
-
alias: references[encodedUrl].alias,
|
|
593
|
-
type: references[encodedUrl].type,
|
|
594
|
-
previewPriority: references[encodedUrl].previewPriority,
|
|
595
|
-
lastModifiedDateTime: references[encodedUrl].lastModifiedDateTime,
|
|
596
|
-
lastModifiedBy: references[encodedUrl].lastModifiedBy,
|
|
597
|
-
};
|
|
598
|
-
});
|
|
599
|
-
returnData.push({
|
|
600
|
-
taskId,
|
|
601
|
-
fileCount: files.length,
|
|
602
|
-
files,
|
|
603
|
-
});
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
// ----------------------------------
|
|
607
|
-
// plan resource
|
|
608
|
-
// ----------------------------------
|
|
609
|
-
if (resource === 'plan') {
|
|
610
|
-
// plan:create -> POST /planner/plans
|
|
611
|
-
if (operation === 'create') {
|
|
612
|
-
const ownerParam = this.getNodeParameter('owner', i);
|
|
613
|
-
const owner = typeof ownerParam === 'string'
|
|
614
|
-
? ownerParam
|
|
615
|
-
: ownerParam.value;
|
|
616
|
-
const title = this.getNodeParameter('title', i);
|
|
617
|
-
const body = { owner, title };
|
|
618
|
-
const responseData = await GenericFunctions_1.microsoftApiRequest.call(this, 'POST', '/planner/plans', body);
|
|
619
|
-
returnData.push(responseData);
|
|
620
|
-
}
|
|
621
|
-
// plan:get -> GET /planner/plans/{planId}
|
|
622
|
-
if (operation === 'get') {
|
|
623
|
-
const planId = this.getNodeParameter('planId', i);
|
|
624
|
-
const additionalFields = this.getNodeParameter('additionalFields', i, {});
|
|
625
|
-
const responseData = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/plans/${planId}`);
|
|
626
|
-
if (additionalFields.includeDetails) {
|
|
627
|
-
const details = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/plans/${planId}/details`);
|
|
628
|
-
responseData.details = details;
|
|
629
|
-
}
|
|
630
|
-
returnData.push(responseData);
|
|
631
|
-
}
|
|
632
|
-
// plan:getAll -> list plans scoped to current user or a specific group
|
|
633
|
-
// Always return all plans; Graph does not honor $top/$limit for Planner.
|
|
634
|
-
if (operation === 'getAll') {
|
|
635
|
-
const scope = this.getNodeParameter('scope', i);
|
|
636
|
-
let endpoint = '';
|
|
637
|
-
if (scope === 'my') {
|
|
638
|
-
// List plans for the current user
|
|
639
|
-
endpoint = '/me/planner/plans';
|
|
640
|
-
}
|
|
641
|
-
else if (scope === 'group') {
|
|
642
|
-
// List plans owned by a specific Microsoft 365 group
|
|
643
|
-
const groupId = this.getNodeParameter('groupId', i);
|
|
644
|
-
endpoint = `/groups/${groupId}/planner/plans`;
|
|
645
|
-
}
|
|
646
|
-
else {
|
|
647
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid scope for plan getAll operation', {
|
|
648
|
-
itemIndex: i,
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
const additionalFields = this.getNodeParameter('additionalFields', i, {});
|
|
652
|
-
const qs = {};
|
|
653
|
-
if (additionalFields === null || additionalFields === void 0 ? void 0 : additionalFields.select) {
|
|
654
|
-
qs.$select = additionalFields.select;
|
|
655
|
-
}
|
|
656
|
-
const responseData = await GenericFunctions_1.microsoftApiRequestAllItems.call(this, 'value', 'GET', endpoint, {}, qs);
|
|
657
|
-
returnData.push(...responseData);
|
|
658
|
-
}
|
|
659
|
-
// plan:update -> PATCH /planner/plans/{planId} with ETag, and optionally /details
|
|
660
|
-
if (operation === 'update') {
|
|
661
|
-
const planId = this.getNodeParameter('planId', i);
|
|
662
|
-
const updateFields = this.getNodeParameter('updateFields', i);
|
|
663
|
-
// Get current plan to obtain ETag
|
|
664
|
-
const currentPlan = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/plans/${planId}`);
|
|
665
|
-
const eTag = (0, GenericFunctions_1.cleanETag)(currentPlan['@odata.etag']);
|
|
666
|
-
const body = {};
|
|
667
|
-
if (updateFields.title) {
|
|
668
|
-
body.title = updateFields.title;
|
|
669
|
-
}
|
|
670
|
-
if (Object.keys(body).length > 0) {
|
|
671
|
-
await GenericFunctions_1.microsoftApiRequest.call(this, 'PATCH', `/planner/plans/${planId}`, body, {}, undefined, {
|
|
672
|
-
'If-Match': eTag,
|
|
673
|
-
});
|
|
674
|
-
}
|
|
675
|
-
// Optionally update plan details if provided
|
|
676
|
-
const detailsBody = {};
|
|
677
|
-
if (updateFields.categoryDescriptions) {
|
|
678
|
-
const categories = updateFields.categoryDescriptions;
|
|
679
|
-
const cleanedCategories = {};
|
|
680
|
-
for (const key of Object.keys(categories)) {
|
|
681
|
-
const value = categories[key];
|
|
682
|
-
// Empty string in the UI means "unset" → send null to Graph
|
|
683
|
-
if (value === '') {
|
|
684
|
-
cleanedCategories[key] = null;
|
|
685
|
-
}
|
|
686
|
-
else if (value !== undefined) {
|
|
687
|
-
cleanedCategories[key] = value;
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
if (Object.keys(cleanedCategories).length > 0) {
|
|
691
|
-
detailsBody.categoryDescriptions = cleanedCategories;
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
if (updateFields.sharedWithJson) {
|
|
695
|
-
let sharedWithParsed;
|
|
696
|
-
try {
|
|
697
|
-
sharedWithParsed = JSON.parse(updateFields.sharedWithJson);
|
|
698
|
-
}
|
|
699
|
-
catch (error) {
|
|
700
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid JSON in Shared With (plannerUserIds JSON) field', {
|
|
701
|
-
itemIndex: i,
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
if (Object.keys(sharedWithParsed).length > 0) {
|
|
705
|
-
detailsBody.sharedWith = sharedWithParsed;
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
if (Object.keys(detailsBody).length > 0) {
|
|
709
|
-
const currentDetails = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/plans/${planId}/details`);
|
|
710
|
-
const detailsETag = (0, GenericFunctions_1.cleanETag)(currentDetails['@odata.etag']);
|
|
711
|
-
await GenericFunctions_1.microsoftApiRequest.call(this, 'PATCH', `/planner/plans/${planId}/details`, detailsBody, {}, undefined, {
|
|
712
|
-
'If-Match': detailsETag,
|
|
713
|
-
});
|
|
714
|
-
}
|
|
715
|
-
const responseData = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/plans/${planId}`);
|
|
716
|
-
// If plan details were updated, also include latest details in the response
|
|
717
|
-
if (Object.keys(detailsBody).length > 0) {
|
|
718
|
-
const details = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/plans/${planId}/details`);
|
|
719
|
-
responseData.details = details;
|
|
720
|
-
}
|
|
721
|
-
returnData.push(responseData);
|
|
722
|
-
}
|
|
723
|
-
// plan:delete -> DELETE /planner/plans/{planId} with ETag
|
|
724
|
-
if (operation === 'delete') {
|
|
725
|
-
const planId = this.getNodeParameter('planId', i);
|
|
726
|
-
const currentPlan = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/plans/${planId}`);
|
|
727
|
-
const eTag = (0, GenericFunctions_1.cleanETag)(currentPlan['@odata.etag']);
|
|
728
|
-
await GenericFunctions_1.microsoftApiRequest.call(this, 'DELETE', `/planner/plans/${planId}`, {}, {}, undefined, {
|
|
729
|
-
'If-Match': eTag,
|
|
156
|
+
let responseData;
|
|
157
|
+
switch (resource) {
|
|
158
|
+
case 'task':
|
|
159
|
+
responseData = await task_1.executeTask.call(this, operation, i, userCache);
|
|
160
|
+
break;
|
|
161
|
+
case 'plan':
|
|
162
|
+
responseData = await plan_1.executePlan.call(this, operation, i, userCache);
|
|
163
|
+
break;
|
|
164
|
+
case 'bucket':
|
|
165
|
+
responseData = await bucket_1.executeBucket.call(this, operation, i);
|
|
166
|
+
break;
|
|
167
|
+
case 'comment':
|
|
168
|
+
responseData = await comment_1.executeComment.call(this, operation, i);
|
|
169
|
+
break;
|
|
170
|
+
default:
|
|
171
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported resource "${resource}"`, {
|
|
172
|
+
itemIndex: i,
|
|
730
173
|
});
|
|
731
|
-
returnData.push({ success: true, planId });
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
// ----------------------------------
|
|
735
|
-
// bucket resource
|
|
736
|
-
// ----------------------------------
|
|
737
|
-
if (resource === 'bucket') {
|
|
738
|
-
// bucket:create -> POST /planner/buckets
|
|
739
|
-
if (operation === 'create') {
|
|
740
|
-
const planId = this.getNodeParameter('planId', i);
|
|
741
|
-
const name = this.getNodeParameter('name', i);
|
|
742
|
-
const upsert = this.getNodeParameter('upsert', i, false);
|
|
743
|
-
if (upsert) {
|
|
744
|
-
// Check if bucket already exists
|
|
745
|
-
const buckets = await GenericFunctions_1.microsoftApiRequestAllItems.call(this, 'value', 'GET', `/planner/plans/${planId}/buckets`);
|
|
746
|
-
const existingBucket = buckets.find((b) => b.name === name);
|
|
747
|
-
if (existingBucket) {
|
|
748
|
-
returnData.push(existingBucket);
|
|
749
|
-
continue;
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
const body = {
|
|
753
|
-
name,
|
|
754
|
-
planId,
|
|
755
|
-
};
|
|
756
|
-
const responseData = await GenericFunctions_1.microsoftApiRequest.call(this, 'POST', '/planner/buckets', body);
|
|
757
|
-
returnData.push(responseData);
|
|
758
|
-
}
|
|
759
|
-
// bucket:get -> GET /planner/buckets/{bucketId}
|
|
760
|
-
if (operation === 'get') {
|
|
761
|
-
const bucketId = this.getNodeParameter('bucketId', i);
|
|
762
|
-
const responseData = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/buckets/${bucketId}`);
|
|
763
|
-
returnData.push(responseData);
|
|
764
|
-
}
|
|
765
|
-
// bucket:getAll -> GET /planner/plans/{planId}/buckets
|
|
766
|
-
// Always return all buckets for the plan; Graph does not honor $top/$limit for Planner.
|
|
767
|
-
if (operation === 'getAll') {
|
|
768
|
-
const planId = this.getNodeParameter('planId', i);
|
|
769
|
-
const endpoint = `/planner/plans/${planId}/buckets`;
|
|
770
|
-
const additionalFields = this.getNodeParameter('additionalFields', i);
|
|
771
|
-
const qs = {};
|
|
772
|
-
if (additionalFields === null || additionalFields === void 0 ? void 0 : additionalFields.select) {
|
|
773
|
-
qs.$select = additionalFields.select;
|
|
774
|
-
}
|
|
775
|
-
const responseData = await GenericFunctions_1.microsoftApiRequestAllItems.call(this, 'value', 'GET', endpoint, {}, qs);
|
|
776
|
-
returnData.push(...responseData);
|
|
777
|
-
}
|
|
778
|
-
// bucket:update -> PATCH /planner/buckets/{bucketId} with ETag
|
|
779
|
-
if (operation === 'update') {
|
|
780
|
-
const bucketId = this.getNodeParameter('bucketId', i);
|
|
781
|
-
const updateFields = this.getNodeParameter('updateFields', i);
|
|
782
|
-
const currentBucket = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/buckets/${bucketId}`);
|
|
783
|
-
const eTag = (0, GenericFunctions_1.cleanETag)(currentBucket['@odata.etag']);
|
|
784
|
-
const body = {};
|
|
785
|
-
if (updateFields.name) {
|
|
786
|
-
body.name = updateFields.name;
|
|
787
|
-
}
|
|
788
|
-
if (updateFields.orderHint) {
|
|
789
|
-
body.orderHint = updateFields.orderHint;
|
|
790
|
-
}
|
|
791
|
-
if (Object.keys(body).length > 0) {
|
|
792
|
-
await GenericFunctions_1.microsoftApiRequest.call(this, 'PATCH', `/planner/buckets/${bucketId}`, body, {}, undefined, {
|
|
793
|
-
'If-Match': eTag,
|
|
794
|
-
});
|
|
795
|
-
}
|
|
796
|
-
const responseData = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/buckets/${bucketId}`);
|
|
797
|
-
returnData.push(responseData);
|
|
798
|
-
}
|
|
799
|
-
// bucket:delete -> DELETE /planner/buckets/{bucketId} with ETag
|
|
800
|
-
if (operation === 'delete') {
|
|
801
|
-
const bucketId = this.getNodeParameter('bucketId', i);
|
|
802
|
-
const currentBucket = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/buckets/${bucketId}`);
|
|
803
|
-
const eTag = (0, GenericFunctions_1.cleanETag)(currentBucket['@odata.etag']);
|
|
804
|
-
await GenericFunctions_1.microsoftApiRequest.call(this, 'DELETE', `/planner/buckets/${bucketId}`, {}, {}, undefined, {
|
|
805
|
-
'If-Match': eTag,
|
|
806
|
-
});
|
|
807
|
-
returnData.push({ success: true, bucketId });
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
// ----------------------------------
|
|
811
|
-
// comment resource
|
|
812
|
-
// ----------------------------------
|
|
813
|
-
if (resource === 'comment') {
|
|
814
|
-
// ----------------------------------
|
|
815
|
-
// comment:create
|
|
816
|
-
// ----------------------------------
|
|
817
|
-
if (operation === 'create') {
|
|
818
|
-
const taskIdParam = this.getNodeParameter('taskId', i);
|
|
819
|
-
const taskId = typeof taskIdParam === 'string' ? taskIdParam : taskIdParam.value;
|
|
820
|
-
const content = this.getNodeParameter('content', i);
|
|
821
|
-
const additionalFields = this.getNodeParameter('additionalFields', i);
|
|
822
|
-
const contentType = additionalFields.contentType || 'text';
|
|
823
|
-
// Get the task to retrieve planId and conversationThreadId
|
|
824
|
-
const task = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}`);
|
|
825
|
-
// Get the plan to retrieve the group ID
|
|
826
|
-
const plan = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/plans/${task.planId}`);
|
|
827
|
-
const groupId = plan.container.containerId;
|
|
828
|
-
const commentBody = (0, GenericFunctions_1.formatCommentContent)(content, contentType);
|
|
829
|
-
let conversationId;
|
|
830
|
-
let threadId;
|
|
831
|
-
if (!task.conversationThreadId) {
|
|
832
|
-
// Create a new conversation thread
|
|
833
|
-
const conversation = await GenericFunctions_1.microsoftApiRequest.call(this, 'POST', `/groups/${groupId}/conversations`, {
|
|
834
|
-
topic: `Comments on task "${task.title}"`,
|
|
835
|
-
threads: [
|
|
836
|
-
{
|
|
837
|
-
posts: [commentBody],
|
|
838
|
-
},
|
|
839
|
-
],
|
|
840
|
-
});
|
|
841
|
-
conversationId = conversation.id;
|
|
842
|
-
threadId = conversation.threads[0].id;
|
|
843
|
-
// Update the task with the new conversationThreadId
|
|
844
|
-
const taskETag = (0, GenericFunctions_1.cleanETag)(task['@odata.etag']);
|
|
845
|
-
await GenericFunctions_1.microsoftApiRequest.call(this, 'PATCH', `/planner/tasks/${taskId}`, {
|
|
846
|
-
conversationThreadId: threadId,
|
|
847
|
-
}, {}, undefined, {
|
|
848
|
-
'If-Match': taskETag,
|
|
849
|
-
});
|
|
850
|
-
}
|
|
851
|
-
else {
|
|
852
|
-
// Add a reply to the existing conversation thread
|
|
853
|
-
threadId = task.conversationThreadId;
|
|
854
|
-
// Get the conversation ID from the thread
|
|
855
|
-
const thread = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/groups/${groupId}/threads/${threadId}`);
|
|
856
|
-
conversationId = thread.conversationId || thread.id;
|
|
857
|
-
// Add reply to the thread
|
|
858
|
-
await GenericFunctions_1.microsoftApiRequest.call(this, 'POST', `/groups/${groupId}/conversations/${conversationId}/threads/${threadId}/reply`, {
|
|
859
|
-
post: commentBody,
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
|
-
returnData.push({
|
|
863
|
-
success: true,
|
|
864
|
-
taskId,
|
|
865
|
-
conversationId,
|
|
866
|
-
threadId,
|
|
867
|
-
content,
|
|
868
|
-
});
|
|
869
|
-
}
|
|
870
|
-
// ----------------------------------
|
|
871
|
-
// comment:getAll
|
|
872
|
-
// ----------------------------------
|
|
873
|
-
if (operation === 'getAll') {
|
|
874
|
-
const taskIdParam = this.getNodeParameter('taskId', i);
|
|
875
|
-
const taskId = typeof taskIdParam === 'string' ? taskIdParam : taskIdParam.value;
|
|
876
|
-
// Get the task to retrieve conversationThreadId
|
|
877
|
-
const task = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}`);
|
|
878
|
-
if (!task.conversationThreadId) {
|
|
879
|
-
// No comments on this task
|
|
880
|
-
returnData.push({
|
|
881
|
-
taskId,
|
|
882
|
-
comments: [],
|
|
883
|
-
commentCount: 0,
|
|
884
|
-
});
|
|
885
|
-
}
|
|
886
|
-
else {
|
|
887
|
-
// Get the plan to retrieve the group ID
|
|
888
|
-
const plan = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/plans/${task.planId}`);
|
|
889
|
-
const groupId = plan.container.containerId;
|
|
890
|
-
const threadId = task.conversationThreadId;
|
|
891
|
-
const additionalFields = this.getNodeParameter('additionalFields', i);
|
|
892
|
-
const qs = {};
|
|
893
|
-
if (additionalFields === null || additionalFields === void 0 ? void 0 : additionalFields.select) {
|
|
894
|
-
qs.$select = additionalFields.select;
|
|
895
|
-
}
|
|
896
|
-
// Get all posts from the conversation thread
|
|
897
|
-
const posts = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/groups/${groupId}/threads/${threadId}/posts`, {}, qs);
|
|
898
|
-
const comments = posts.value.map((post) => ({
|
|
899
|
-
id: post.id,
|
|
900
|
-
content: post.body,
|
|
901
|
-
from: post.from,
|
|
902
|
-
createdDateTime: post.createdDateTime,
|
|
903
|
-
lastModifiedDateTime: post.lastModifiedDateTime,
|
|
904
|
-
}));
|
|
905
|
-
returnData.push({
|
|
906
|
-
taskId,
|
|
907
|
-
comments,
|
|
908
|
-
commentCount: comments.length,
|
|
909
|
-
});
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
174
|
}
|
|
175
|
+
const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } });
|
|
176
|
+
returnData.push(...executionData);
|
|
913
177
|
}
|
|
914
178
|
catch (error) {
|
|
915
179
|
if (this.continueOnFail()) {
|
|
916
|
-
|
|
917
|
-
|
|
180
|
+
returnData.push({
|
|
181
|
+
json: { error: error.message },
|
|
182
|
+
pairedItem: { item: i },
|
|
183
|
+
});
|
|
918
184
|
continue;
|
|
919
185
|
}
|
|
920
|
-
|
|
186
|
+
const context = error.context;
|
|
187
|
+
if (context && context.itemIndex === undefined) {
|
|
188
|
+
context.itemIndex = i;
|
|
189
|
+
}
|
|
190
|
+
if (error instanceof n8n_workflow_1.NodeApiError) {
|
|
191
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
|
|
192
|
+
}
|
|
193
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i });
|
|
921
194
|
}
|
|
922
195
|
}
|
|
923
|
-
return [
|
|
196
|
+
return [returnData];
|
|
924
197
|
}
|
|
925
198
|
}
|
|
926
199
|
exports.MicrosoftPlanner = MicrosoftPlanner;
|
|
200
|
+
//# sourceMappingURL=MicrosoftPlanner.node.js.map
|