@pipedream/sharepoint 0.7.2 → 0.8.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/actions/create-folder/create-folder.mjs +1 -1
- package/actions/create-item/create-item.mjs +1 -1
- package/actions/create-link/create-link.mjs +1 -1
- package/actions/create-list/create-list.mjs +1 -1
- package/actions/download-file/download-file.mjs +1 -1
- package/actions/download-files/download-files.mjs +88 -0
- package/actions/find-file-by-name/find-file-by-name.mjs +1 -1
- package/actions/find-files-with-metadata/find-files-with-metadata.mjs +1 -1
- package/actions/get-excel-table/get-excel-table.mjs +1 -1
- package/actions/get-file-by-id/get-file-by-id.mjs +1 -1
- package/actions/get-site/get-site.mjs +1 -1
- package/actions/list-files-in-folder/list-files-in-folder.mjs +1 -1
- package/actions/list-sites/list-sites.mjs +1 -1
- package/actions/retrieve-file-metadata/retrieve-file-metadata.mjs +55 -0
- package/actions/search-and-filter-files/search-and-filter-files.mjs +1 -1
- package/actions/search-files/search-files.mjs +1 -1
- package/actions/search-sites/search-sites.mjs +1 -1
- package/actions/update-item/update-item.mjs +1 -1
- package/actions/upload-file/upload-file.mjs +1 -1
- package/common/constants.mjs +23 -0
- package/common/file-picker-base.mjs +308 -0
- package/common/utils.mjs +78 -0
- package/package.json +5 -2
- package/sharepoint.app.mjs +400 -3
- package/sources/new-file-created/new-file-created.mjs +1 -1
- package/sources/new-folder-created/new-folder-created.mjs +1 -1
- package/sources/new-list-item/new-list-item.mjs +1 -1
- package/sources/updated-file-instant/updated-file-instant.mjs +407 -0
- package/sources/updated-list-item/updated-list-item.mjs +1 -1
- package/actions/select-files/select-files.mjs +0 -198
package/common/utils.mjs
CHANGED
|
@@ -7,6 +7,84 @@ export default {
|
|
|
7
7
|
}
|
|
8
8
|
return o;
|
|
9
9
|
},
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal helper to unwrap a potentially labeled value.
|
|
13
|
+
* Note: For most use cases, prefer using sharepoint.resolveWrappedValue() from the app.
|
|
14
|
+
* This is only for utility functions that don't have access to the app instance.
|
|
15
|
+
* @private
|
|
16
|
+
* @param {*} value - The value to unwrap
|
|
17
|
+
* @returns {*} The unwrapped value
|
|
18
|
+
*/
|
|
19
|
+
_unwrapValue(value) {
|
|
20
|
+
return value?.value || value;
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parses a file or folder value from JSON string or returns wrapped object
|
|
25
|
+
* @param {*} value - The value to parse (JSON string or raw ID)
|
|
26
|
+
* @returns {{ id: string, name?: string, isFolder: boolean } | null}
|
|
27
|
+
*/
|
|
28
|
+
parseFileOrFolder(value) {
|
|
29
|
+
if (!value) return null;
|
|
30
|
+
const resolved = this._unwrapValue(value);
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(resolved);
|
|
33
|
+
} catch {
|
|
34
|
+
return {
|
|
35
|
+
id: resolved,
|
|
36
|
+
isFolder: false,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Parses a list of file or folder values
|
|
43
|
+
* @param {*} values - Single value or array of values
|
|
44
|
+
* @returns {Array<{ id: string, name?: string, isFolder: boolean }>}
|
|
45
|
+
*/
|
|
46
|
+
parseFileOrFolderList(values) {
|
|
47
|
+
if (!values) return [];
|
|
48
|
+
const list = Array.isArray(values)
|
|
49
|
+
? values
|
|
50
|
+
: [
|
|
51
|
+
values,
|
|
52
|
+
];
|
|
53
|
+
return list.map((v) => this.parseFileOrFolder(v)).filter(Boolean);
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Attempts to decode a base64 string (SharePoint encodes group names in base64)
|
|
58
|
+
* @param {string} str - String that may be base64 encoded
|
|
59
|
+
* @returns {string | null} Decoded string or null if not valid base64
|
|
60
|
+
*/
|
|
61
|
+
tryDecodeBase64(str) {
|
|
62
|
+
try {
|
|
63
|
+
if (/^[A-Za-z0-9+/]+=*$/.test(str) && str.length > 10) {
|
|
64
|
+
const decoded = Buffer.from(str, "base64").toString("utf-8");
|
|
65
|
+
if (/^[\x20-\x7E\s]+$/.test(decoded)) {
|
|
66
|
+
return decoded;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
// Not valid base64
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Determines access level from roles array
|
|
77
|
+
* @param {string[]} roles - Array of role strings
|
|
78
|
+
* @returns {"owner" | "write" | "read"}
|
|
79
|
+
*/
|
|
80
|
+
getAccessLevel(roles) {
|
|
81
|
+
if (!Array.isArray(roles)) return "read";
|
|
82
|
+
if (roles.includes("owner")) return "owner";
|
|
83
|
+
if (roles.includes("write")) return "write";
|
|
84
|
+
if (roles.includes("read")) return "read";
|
|
85
|
+
return "read";
|
|
86
|
+
},
|
|
87
|
+
|
|
10
88
|
parseObject(obj) {
|
|
11
89
|
if (!obj) return undefined;
|
|
12
90
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipedream/sharepoint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Pipedream Microsoft Sharepoint Online Components",
|
|
5
5
|
"main": "sharepoint.app.mjs",
|
|
6
6
|
"keywords": [
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
"access": "public"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@pipedream/platform": "^3.1.1"
|
|
16
|
+
"@pipedream/platform": "^3.1.1",
|
|
17
|
+
"@microsoft/microsoft-graph-client": "^3.0.7",
|
|
18
|
+
"async-retry": "^1.3.3",
|
|
19
|
+
"isomorphic-fetch": "^3.0.0"
|
|
17
20
|
}
|
|
18
21
|
}
|
package/sharepoint.app.mjs
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { axios } from "@pipedream/platform";
|
|
2
|
+
import { Client } from "@microsoft/microsoft-graph-client";
|
|
3
|
+
import retry from "async-retry";
|
|
4
|
+
import "isomorphic-fetch";
|
|
5
|
+
import { WEBHOOK_SUBSCRIPTION_EXPIRATION_TIME_MILLISECONDS } from "./common/constants.mjs";
|
|
2
6
|
|
|
3
7
|
export default {
|
|
4
8
|
type: "app",
|
|
@@ -136,6 +140,7 @@ export default {
|
|
|
136
140
|
if (!siteId) {
|
|
137
141
|
return [];
|
|
138
142
|
}
|
|
143
|
+
siteId = this.resolveWrappedValue(siteId);
|
|
139
144
|
const args = {
|
|
140
145
|
siteId,
|
|
141
146
|
};
|
|
@@ -235,6 +240,41 @@ export default {
|
|
|
235
240
|
}));
|
|
236
241
|
},
|
|
237
242
|
},
|
|
243
|
+
fileIds: {
|
|
244
|
+
type: "string[]",
|
|
245
|
+
label: "Files",
|
|
246
|
+
description: "Select files or enter custom file IDs.",
|
|
247
|
+
async options({
|
|
248
|
+
siteId, driveId, folderId,
|
|
249
|
+
}) {
|
|
250
|
+
const resolvedSiteId = this.resolveWrappedValue(siteId);
|
|
251
|
+
const resolvedDriveId = this.resolveWrappedValue(driveId);
|
|
252
|
+
const resolvedFolderId = this.resolveWrappedValue(folderId);
|
|
253
|
+
|
|
254
|
+
if (!resolvedSiteId || !resolvedDriveId) {
|
|
255
|
+
return [];
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const response = resolvedFolderId
|
|
259
|
+
? await this.listDriveItemsInFolder({
|
|
260
|
+
driveId: resolvedDriveId,
|
|
261
|
+
folderId: resolvedFolderId,
|
|
262
|
+
})
|
|
263
|
+
: await this.listDriveItems({
|
|
264
|
+
siteId: resolvedSiteId,
|
|
265
|
+
driveId: resolvedDriveId,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
return response.value
|
|
269
|
+
?.filter(({ folder }) => !folder)
|
|
270
|
+
.map(({
|
|
271
|
+
id, name,
|
|
272
|
+
}) => ({
|
|
273
|
+
label: name,
|
|
274
|
+
value: id,
|
|
275
|
+
})) || [];
|
|
276
|
+
},
|
|
277
|
+
},
|
|
238
278
|
excelFileId: {
|
|
239
279
|
type: "string",
|
|
240
280
|
label: "Spreadsheet",
|
|
@@ -320,13 +360,15 @@ export default {
|
|
|
320
360
|
driveId: resolvedDriveId,
|
|
321
361
|
});
|
|
322
362
|
return response.value?.map(({
|
|
323
|
-
id, name, folder, size,
|
|
363
|
+
id, name, folder, size, lastModifiedDateTime,
|
|
324
364
|
}) => ({
|
|
325
365
|
value: JSON.stringify({
|
|
326
366
|
id,
|
|
327
367
|
name,
|
|
328
368
|
isFolder: !!folder,
|
|
329
369
|
size,
|
|
370
|
+
childCount: folder?.childCount,
|
|
371
|
+
lastModifiedDateTime,
|
|
330
372
|
}),
|
|
331
373
|
label: folder
|
|
332
374
|
? `📁 ${name}`
|
|
@@ -336,12 +378,125 @@ export default {
|
|
|
336
378
|
},
|
|
337
379
|
},
|
|
338
380
|
methods: {
|
|
381
|
+
_graphClient: null,
|
|
382
|
+
/**
|
|
383
|
+
* Resolves a potentially wrapped labeled value to its actual value.
|
|
384
|
+
* Pipedream props with withLabel: true wrap values in a special format.
|
|
385
|
+
*
|
|
386
|
+
* @param {*} value - The value to resolve (may be wrapped or plain)
|
|
387
|
+
* @returns {*} The unwrapped value, or the original value if not wrapped
|
|
388
|
+
* @example
|
|
389
|
+
* // Wrapped labeled value
|
|
390
|
+
* resolveWrappedValue({ __lv: { label: "My Site", value: "abc123" } }) // "abc123"
|
|
391
|
+
*
|
|
392
|
+
* // Plain value (not wrapped)
|
|
393
|
+
* resolveWrappedValue("abc123") // "abc123"
|
|
394
|
+
*/
|
|
339
395
|
resolveWrappedValue(value) {
|
|
340
|
-
return value?.
|
|
396
|
+
return value?.value || value;
|
|
397
|
+
},
|
|
398
|
+
/**
|
|
399
|
+
* Resolves an array of potentially wrapped labeled values.
|
|
400
|
+
* Handles both Pipedream's labeled value array format and plain arrays.
|
|
401
|
+
*
|
|
402
|
+
* @param {Array|Object} arr
|
|
403
|
+
* Array to resolve (may be wrapped or plain)
|
|
404
|
+
* @returns {Array} Array of unwrapped values
|
|
405
|
+
* @example
|
|
406
|
+
* // Wrapped array of labeled values
|
|
407
|
+
* resolveWrappedArrayValues({
|
|
408
|
+
* __lv: [
|
|
409
|
+
* { label: "File 1", value: "id1" },
|
|
410
|
+
* { label: "File 2", value: "id2" }
|
|
411
|
+
* ]
|
|
412
|
+
* }) // ["id1", "id2"]
|
|
413
|
+
*
|
|
414
|
+
* // Plain array
|
|
415
|
+
* resolveWrappedArrayValues(["id1", "id2"]) // ["id1", "id2"]
|
|
416
|
+
*/
|
|
417
|
+
resolveWrappedArrayValues(arr) {
|
|
418
|
+
if (!arr) return [];
|
|
419
|
+
// Handle __lv wrapped array
|
|
420
|
+
const unwrapped = arr?.__lv || arr;
|
|
421
|
+
if (!Array.isArray(unwrapped)) return [];
|
|
422
|
+
// Extract value from each item if it's a labeled value object
|
|
423
|
+
return unwrapped.map((item) => item?.value ?? item);
|
|
341
424
|
},
|
|
342
425
|
_getAccessToken() {
|
|
343
426
|
return this.$auth.oauth_access_token;
|
|
344
427
|
},
|
|
428
|
+
/**
|
|
429
|
+
* Creates a Microsoft Graph SDK client with caching.
|
|
430
|
+
* This provides better consistency with other Microsoft components
|
|
431
|
+
* and includes built-in pagination and type safety.
|
|
432
|
+
*/
|
|
433
|
+
client() {
|
|
434
|
+
if (!this._graphClient) {
|
|
435
|
+
this._graphClient = Client.initWithMiddleware({
|
|
436
|
+
authProvider: {
|
|
437
|
+
getAccessToken: () => Promise.resolve(this._getAccessToken()),
|
|
438
|
+
},
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
return this._graphClient;
|
|
442
|
+
},
|
|
443
|
+
/**
|
|
444
|
+
* Makes a request to Microsoft Graph API with automatic retry logic.
|
|
445
|
+
* Retries on transient errors (5xx, 429) but not on auth/client
|
|
446
|
+
* errors (4xx).
|
|
447
|
+
*
|
|
448
|
+
* @param {Object} options - Request options
|
|
449
|
+
* @param {string} options.path - API path (e.g., "/sites/{siteId}")
|
|
450
|
+
* @param {string} [options.method="GET"] - HTTP method
|
|
451
|
+
* @param {*} [options.content] - Request body for POST/PATCH
|
|
452
|
+
* @param {number} [options.retries=3] - Number of retry attempts
|
|
453
|
+
* @returns {Promise<*>} API response
|
|
454
|
+
*/
|
|
455
|
+
async graphRequest({
|
|
456
|
+
path, method = "GET", content, retries = 3,
|
|
457
|
+
}) {
|
|
458
|
+
return retry(
|
|
459
|
+
async (bail) => {
|
|
460
|
+
try {
|
|
461
|
+
const api = this.client().api(path);
|
|
462
|
+
|
|
463
|
+
switch (method.toUpperCase()) {
|
|
464
|
+
case "GET":
|
|
465
|
+
return await api.get();
|
|
466
|
+
case "POST":
|
|
467
|
+
return await api.post(content);
|
|
468
|
+
case "PATCH":
|
|
469
|
+
return await api.patch(content);
|
|
470
|
+
case "DELETE":
|
|
471
|
+
return await api.delete();
|
|
472
|
+
default:
|
|
473
|
+
throw new Error(`Unsupported HTTP method: ${method}`);
|
|
474
|
+
}
|
|
475
|
+
} catch (error) {
|
|
476
|
+
// Don't retry on auth errors or client errors (4xx except 429)
|
|
477
|
+
const status = error.statusCode || error.response?.status;
|
|
478
|
+
if ([
|
|
479
|
+
400,
|
|
480
|
+
401,
|
|
481
|
+
403,
|
|
482
|
+
404,
|
|
483
|
+
].includes(status)) {
|
|
484
|
+
bail(error); // Throw immediately, don't retry
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
throw error; // Retry on other errors
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
retries,
|
|
492
|
+
minTimeout: 1000,
|
|
493
|
+
maxTimeout: 10000,
|
|
494
|
+
onRetry: (error, attempt) => {
|
|
495
|
+
console.log(`Retry attempt ${attempt} after error: ${error.message}`);
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
);
|
|
499
|
+
},
|
|
345
500
|
_baseUrl() {
|
|
346
501
|
return "https://graph.microsoft.com/v1.0";
|
|
347
502
|
},
|
|
@@ -354,15 +509,52 @@ export default {
|
|
|
354
509
|
_makeRequest({
|
|
355
510
|
$ = this,
|
|
356
511
|
path,
|
|
512
|
+
url,
|
|
357
513
|
headers,
|
|
358
514
|
...args
|
|
359
515
|
}) {
|
|
360
516
|
return axios($, {
|
|
361
|
-
url: `${this._baseUrl()}${path}`,
|
|
517
|
+
url: url || `${this._baseUrl()}${path}`,
|
|
362
518
|
headers: this._headers(headers),
|
|
363
519
|
...args,
|
|
364
520
|
});
|
|
365
521
|
},
|
|
522
|
+
/**
|
|
523
|
+
* Makes a request to SharePoint REST API (not Microsoft Graph).
|
|
524
|
+
* Use this for SharePoint-specific features not available in Graph API,
|
|
525
|
+
* such as native SharePoint site groups.
|
|
526
|
+
*
|
|
527
|
+
* @param {Object} options - Request options
|
|
528
|
+
* @param {string} options.siteWebUrl - Full site URL (e.g., "https://tenant.sharepoint.com/sites/MySite")
|
|
529
|
+
* @param {string} options.path - REST API path (e.g., "/web/sitegroups")
|
|
530
|
+
* @param {Object} [options.headers] - Additional headers
|
|
531
|
+
* @returns {Promise<Object>} API response
|
|
532
|
+
* @example
|
|
533
|
+
* // Get SharePoint site groups
|
|
534
|
+
* await this._makeSharePointRestRequest({
|
|
535
|
+
* siteWebUrl: "https://contoso.sharepoint.com/sites/TeamSite",
|
|
536
|
+
* path: "/web/sitegroups"
|
|
537
|
+
* })
|
|
538
|
+
*/
|
|
539
|
+
_makeSharePointRestRequest({
|
|
540
|
+
$ = this,
|
|
541
|
+
siteWebUrl,
|
|
542
|
+
path,
|
|
543
|
+
headers,
|
|
544
|
+
...args
|
|
545
|
+
}) {
|
|
546
|
+
// SharePoint REST API uses the site's webUrl as base
|
|
547
|
+
// e.g., https://tenant.sharepoint.com/sites/MySite/_api/web/sitegroups
|
|
548
|
+
const baseUrl = siteWebUrl.replace(/\/$/, ""); // Remove trailing slash if present
|
|
549
|
+
return axios($, {
|
|
550
|
+
url: `${baseUrl}/_api${path}`,
|
|
551
|
+
headers: this._headers({
|
|
552
|
+
Accept: "application/json;odata=verbose",
|
|
553
|
+
...headers,
|
|
554
|
+
}),
|
|
555
|
+
...args,
|
|
556
|
+
});
|
|
557
|
+
},
|
|
366
558
|
getSite({
|
|
367
559
|
siteId, ...args
|
|
368
560
|
}) {
|
|
@@ -371,6 +563,52 @@ export default {
|
|
|
371
563
|
...args,
|
|
372
564
|
});
|
|
373
565
|
},
|
|
566
|
+
/**
|
|
567
|
+
* Lists SharePoint-native site groups using the SharePoint REST API.
|
|
568
|
+
* These are different from Microsoft 365 Groups and can only be accessed via REST API.
|
|
569
|
+
*
|
|
570
|
+
* @param {Object} options - Request options
|
|
571
|
+
* @param {string} options.siteWebUrl - Full site URL
|
|
572
|
+
* @returns {Promise<Object>} Response with groups in d.results array
|
|
573
|
+
* @example
|
|
574
|
+
* const groups = await this.listSharePointSiteGroups({
|
|
575
|
+
* siteWebUrl: "https://contoso.sharepoint.com/sites/TeamSite"
|
|
576
|
+
* });
|
|
577
|
+
* // Returns: { d: { results: [{ Id: 5, Title: "Team Site Members" }, ...] } }
|
|
578
|
+
*/
|
|
579
|
+
listSharePointSiteGroups({
|
|
580
|
+
siteWebUrl, ...args
|
|
581
|
+
}) {
|
|
582
|
+
return this._makeSharePointRestRequest({
|
|
583
|
+
siteWebUrl,
|
|
584
|
+
path: "/web/sitegroups",
|
|
585
|
+
...args,
|
|
586
|
+
});
|
|
587
|
+
},
|
|
588
|
+
/**
|
|
589
|
+
* Gets members of a SharePoint-native site group using the SharePoint REST API.
|
|
590
|
+
* Returns user details including email, display name, and login name.
|
|
591
|
+
*
|
|
592
|
+
* @param {Object} options - Request options
|
|
593
|
+
* @param {string} options.siteWebUrl - Full site URL
|
|
594
|
+
* @param {number} options.groupId - SharePoint group ID
|
|
595
|
+
* @returns {Promise<Object>} Response with users in d.results array
|
|
596
|
+
* @example
|
|
597
|
+
* const members = await this.getSharePointSiteGroupMembers({
|
|
598
|
+
* siteWebUrl: "https://contoso.sharepoint.com/sites/TeamSite",
|
|
599
|
+
* groupId: 5
|
|
600
|
+
* });
|
|
601
|
+
* // Returns: { d: { results: [{ Email: "user@contoso.com", Title: "John Doe" }, ...] } }
|
|
602
|
+
*/
|
|
603
|
+
getSharePointSiteGroupMembers({
|
|
604
|
+
siteWebUrl, groupId, ...args
|
|
605
|
+
}) {
|
|
606
|
+
return this._makeSharePointRestRequest({
|
|
607
|
+
siteWebUrl,
|
|
608
|
+
path: `/web/sitegroups/getbyid(${groupId})/users`,
|
|
609
|
+
...args,
|
|
610
|
+
});
|
|
611
|
+
},
|
|
374
612
|
listSites(args = {}) {
|
|
375
613
|
return this._makeRequest({
|
|
376
614
|
path: "/me/followedSites",
|
|
@@ -514,6 +752,14 @@ export default {
|
|
|
514
752
|
...args,
|
|
515
753
|
});
|
|
516
754
|
},
|
|
755
|
+
listDriveItemPermissions({
|
|
756
|
+
driveId, itemId, ...args
|
|
757
|
+
}) {
|
|
758
|
+
return this._makeRequest({
|
|
759
|
+
path: `/drives/${driveId}/items/${itemId}/permissions`,
|
|
760
|
+
...args,
|
|
761
|
+
});
|
|
762
|
+
},
|
|
517
763
|
searchDriveItems({
|
|
518
764
|
siteId, query, ...args
|
|
519
765
|
}) {
|
|
@@ -559,6 +805,62 @@ export default {
|
|
|
559
805
|
...args,
|
|
560
806
|
});
|
|
561
807
|
},
|
|
808
|
+
listGroups(args = {}) {
|
|
809
|
+
return this._makeRequest({
|
|
810
|
+
path: "/groups",
|
|
811
|
+
...args,
|
|
812
|
+
});
|
|
813
|
+
},
|
|
814
|
+
listUsers(args = {}) {
|
|
815
|
+
return this._makeRequest({
|
|
816
|
+
path: "/users",
|
|
817
|
+
...args,
|
|
818
|
+
});
|
|
819
|
+
},
|
|
820
|
+
listGroupMembers({
|
|
821
|
+
groupId, ...args
|
|
822
|
+
}) {
|
|
823
|
+
return this._makeRequest({
|
|
824
|
+
path: `/groups/${groupId}/members`,
|
|
825
|
+
...args,
|
|
826
|
+
});
|
|
827
|
+
},
|
|
828
|
+
/**
|
|
829
|
+
* Get delta changes for a drive using Microsoft Graph delta query.
|
|
830
|
+
* Enables tracking changes to files and folders over time.
|
|
831
|
+
*
|
|
832
|
+
* @param {Object} options - Request options
|
|
833
|
+
* @param {string} options.driveId - Drive ID to track
|
|
834
|
+
* @param {string} [options.deltaLink]
|
|
835
|
+
* Delta link from previous response (for pagination/continuation)
|
|
836
|
+
* @returns {Promise<Object>} Response with changed items and
|
|
837
|
+
* @odata.deltaLink or @odata.nextLink
|
|
838
|
+
* @see https://learn.microsoft.com/en-us/graph/api/driveitem-delta
|
|
839
|
+
* @example
|
|
840
|
+
* // First call - get initial state
|
|
841
|
+
* const initial = await this.getDriveDelta({ driveId: "b!..." });
|
|
842
|
+
* const deltaLink = initial["@odata.deltaLink"];
|
|
843
|
+
*
|
|
844
|
+
* // Later - get changes since last call
|
|
845
|
+
* const changes = await this.getDriveDelta({
|
|
846
|
+
* driveId: "b!...", deltaLink
|
|
847
|
+
* });
|
|
848
|
+
*/
|
|
849
|
+
getDriveDelta({
|
|
850
|
+
driveId, deltaLink, ...args
|
|
851
|
+
}) {
|
|
852
|
+
// If we have a deltaLink/nextLink, use it as full URL; otherwise build path
|
|
853
|
+
if (deltaLink) {
|
|
854
|
+
return this._makeRequest({
|
|
855
|
+
url: deltaLink,
|
|
856
|
+
...args,
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
return this._makeRequest({
|
|
860
|
+
path: `/drives/${driveId}/root/delta`,
|
|
861
|
+
...args,
|
|
862
|
+
});
|
|
863
|
+
},
|
|
562
864
|
searchQuery(args = {}) {
|
|
563
865
|
return this._makeRequest({
|
|
564
866
|
method: "POST",
|
|
@@ -586,5 +888,100 @@ export default {
|
|
|
586
888
|
nextLink = response["@odata.nextLink"];
|
|
587
889
|
} while (nextLink);
|
|
588
890
|
},
|
|
891
|
+
/**
|
|
892
|
+
* Creates a Microsoft Graph subscription for change notifications
|
|
893
|
+
* (webhooks). Subscriptions expire after a set time and must be
|
|
894
|
+
* renewed periodically.
|
|
895
|
+
*
|
|
896
|
+
* @param {Object} options - Subscription options
|
|
897
|
+
* @param {string} options.resource
|
|
898
|
+
* Resource to monitor (e.g., "drives/{driveId}/root")
|
|
899
|
+
* @param {string} options.notificationUrl - Webhook endpoint URL
|
|
900
|
+
* @param {string} [options.changeType="updated"]
|
|
901
|
+
* Type of change to monitor (updated, created, deleted)
|
|
902
|
+
* @param {string} options.clientState
|
|
903
|
+
* Secret string for webhook validation
|
|
904
|
+
* @returns {Promise<Object>}
|
|
905
|
+
* Created subscription with id and expirationDateTime
|
|
906
|
+
* @see https://learn.microsoft.com/en-us/graph/api/subscription-post-subscriptions
|
|
907
|
+
* @example
|
|
908
|
+
* const subscription = await this.createSubscription({
|
|
909
|
+
* resource: "drives/b!abc123.../root",
|
|
910
|
+
* notificationUrl: "https://webhook.site/unique-id",
|
|
911
|
+
* changeType: "updated",
|
|
912
|
+
* clientState: "secret-validation-token"
|
|
913
|
+
* });
|
|
914
|
+
*/
|
|
915
|
+
createSubscription({
|
|
916
|
+
resource, notificationUrl, changeType = "updated", clientState, ...args
|
|
917
|
+
}) {
|
|
918
|
+
const expirationDateTime = new Date(
|
|
919
|
+
Date.now() + WEBHOOK_SUBSCRIPTION_EXPIRATION_TIME_MILLISECONDS,
|
|
920
|
+
).toISOString();
|
|
921
|
+
|
|
922
|
+
return this._makeRequest({
|
|
923
|
+
method: "POST",
|
|
924
|
+
path: "/subscriptions",
|
|
925
|
+
data: {
|
|
926
|
+
changeType,
|
|
927
|
+
notificationUrl,
|
|
928
|
+
resource,
|
|
929
|
+
expirationDateTime,
|
|
930
|
+
clientState,
|
|
931
|
+
},
|
|
932
|
+
...args,
|
|
933
|
+
});
|
|
934
|
+
},
|
|
935
|
+
/**
|
|
936
|
+
* Updates a subscription's expiration time (renewal).
|
|
937
|
+
* Subscriptions must be renewed before they expire to maintain continuous monitoring.
|
|
938
|
+
*
|
|
939
|
+
* @param {Object} options - Update options
|
|
940
|
+
* @param {string} options.subscriptionId - ID of subscription to renew
|
|
941
|
+
* @returns {Promise<Object>} Updated subscription with new expirationDateTime
|
|
942
|
+
* @see https://learn.microsoft.com/en-us/graph/api/subscription-update
|
|
943
|
+
* @example
|
|
944
|
+
* await this.updateSubscription({
|
|
945
|
+
* subscriptionId: "abc123-def456"
|
|
946
|
+
* });
|
|
947
|
+
*/
|
|
948
|
+
updateSubscription({
|
|
949
|
+
subscriptionId, ...args
|
|
950
|
+
}) {
|
|
951
|
+
const expirationDateTime = new Date(
|
|
952
|
+
Date.now() + WEBHOOK_SUBSCRIPTION_EXPIRATION_TIME_MILLISECONDS,
|
|
953
|
+
).toISOString();
|
|
954
|
+
|
|
955
|
+
return this._makeRequest({
|
|
956
|
+
method: "PATCH",
|
|
957
|
+
path: `/subscriptions/${subscriptionId}`,
|
|
958
|
+
data: {
|
|
959
|
+
expirationDateTime,
|
|
960
|
+
},
|
|
961
|
+
...args,
|
|
962
|
+
});
|
|
963
|
+
},
|
|
964
|
+
/**
|
|
965
|
+
* Deletes a subscription. Call this when deactivating a webhook source
|
|
966
|
+
* to stop receiving notifications and clean up resources.
|
|
967
|
+
*
|
|
968
|
+
* @param {Object} options - Delete options
|
|
969
|
+
* @param {string} options.subscriptionId - ID of subscription to delete
|
|
970
|
+
* @returns {Promise<void>}
|
|
971
|
+
* @see https://learn.microsoft.com/en-us/graph/api/subscription-delete
|
|
972
|
+
* @example
|
|
973
|
+
* await this.deleteSubscription({
|
|
974
|
+
* subscriptionId: "abc123-def456"
|
|
975
|
+
* });
|
|
976
|
+
*/
|
|
977
|
+
deleteSubscription({
|
|
978
|
+
subscriptionId, ...args
|
|
979
|
+
}) {
|
|
980
|
+
return this._makeRequest({
|
|
981
|
+
method: "DELETE",
|
|
982
|
+
path: `/subscriptions/${subscriptionId}`,
|
|
983
|
+
...args,
|
|
984
|
+
});
|
|
985
|
+
},
|
|
589
986
|
},
|
|
590
987
|
};
|