@jbctechsolutions/mcp-office365 2.5.1 → 4.0.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 +66 -57
- package/dist/approval/hash.d.ts +8 -8
- package/dist/approval/token-manager.d.ts +37 -19
- package/dist/approval/token-manager.d.ts.map +1 -1
- package/dist/approval/token-manager.js +133 -37
- package/dist/approval/token-manager.js.map +1 -1
- package/dist/approval/types.d.ts +7 -3
- package/dist/approval/types.d.ts.map +1 -1
- package/dist/cli.d.ts +21 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +100 -2
- package/dist/cli.js.map +1 -1
- package/dist/database/repository.d.ts +48 -48
- package/dist/database/repository.d.ts.map +1 -1
- package/dist/database/repository.js.map +1 -1
- package/dist/delta/mirror.d.ts +67 -0
- package/dist/delta/mirror.d.ts.map +1 -0
- package/dist/delta/mirror.js +183 -0
- package/dist/delta/mirror.js.map +1 -0
- package/dist/graph/auth/account-id.d.ts +19 -0
- package/dist/graph/auth/account-id.d.ts.map +1 -0
- package/dist/graph/auth/account-id.js +55 -0
- package/dist/graph/auth/account-id.js.map +1 -0
- package/dist/graph/auth/config.d.ts +1 -1
- package/dist/graph/auth/config.d.ts.map +1 -1
- package/dist/graph/auth/config.js +13 -1
- package/dist/graph/auth/config.js.map +1 -1
- package/dist/graph/auth/device-code-flow.d.ts +3 -1
- package/dist/graph/auth/device-code-flow.d.ts.map +1 -1
- package/dist/graph/auth/device-code-flow.js +42 -14
- package/dist/graph/auth/device-code-flow.js.map +1 -1
- package/dist/graph/auth/index.d.ts +1 -0
- package/dist/graph/auth/index.d.ts.map +1 -1
- package/dist/graph/auth/index.js +1 -0
- package/dist/graph/auth/index.js.map +1 -1
- package/dist/graph/auth/token-cache.js +2 -2
- package/dist/graph/auth/token-cache.js.map +1 -1
- package/dist/graph/client/graph-client.d.ts +141 -8
- package/dist/graph/client/graph-client.d.ts.map +1 -1
- package/dist/graph/client/graph-client.js +536 -48
- package/dist/graph/client/graph-client.js.map +1 -1
- package/dist/graph/client/immutable-id-middleware.d.ts +50 -0
- package/dist/graph/client/immutable-id-middleware.d.ts.map +1 -0
- package/dist/graph/client/immutable-id-middleware.js +137 -0
- package/dist/graph/client/immutable-id-middleware.js.map +1 -0
- package/dist/graph/content-readers.d.ts +0 -12
- package/dist/graph/content-readers.d.ts.map +1 -1
- package/dist/graph/content-readers.js +0 -21
- package/dist/graph/content-readers.js.map +1 -1
- package/dist/graph/index.d.ts +5 -6
- package/dist/graph/index.d.ts.map +1 -1
- package/dist/graph/index.js +5 -6
- package/dist/graph/index.js.map +1 -1
- package/dist/graph/mailbox-adapter.d.ts +15 -15
- package/dist/graph/mappers/contact-mapper.d.ts +4 -1
- package/dist/graph/mappers/contact-mapper.d.ts.map +1 -1
- package/dist/graph/mappers/contact-mapper.js +7 -3
- package/dist/graph/mappers/contact-mapper.js.map +1 -1
- package/dist/graph/mappers/email-mapper.d.ts.map +1 -1
- package/dist/graph/mappers/email-mapper.js +7 -4
- package/dist/graph/mappers/email-mapper.js.map +1 -1
- package/dist/graph/mappers/event-mapper.d.ts.map +1 -1
- package/dist/graph/mappers/event-mapper.js +6 -3
- package/dist/graph/mappers/event-mapper.js.map +1 -1
- package/dist/graph/mappers/folder-mapper.d.ts +0 -4
- package/dist/graph/mappers/folder-mapper.d.ts.map +1 -1
- package/dist/graph/mappers/folder-mapper.js +9 -19
- package/dist/graph/mappers/folder-mapper.js.map +1 -1
- package/dist/graph/mappers/index.d.ts +2 -2
- package/dist/graph/mappers/index.d.ts.map +1 -1
- package/dist/graph/mappers/index.js +2 -2
- package/dist/graph/mappers/index.js.map +1 -1
- package/dist/graph/mappers/task-mapper.d.ts +4 -2
- package/dist/graph/mappers/task-mapper.d.ts.map +1 -1
- package/dist/graph/mappers/task-mapper.js +7 -5
- package/dist/graph/mappers/task-mapper.js.map +1 -1
- package/dist/graph/mappers/utils.d.ts +0 -10
- package/dist/graph/mappers/utils.d.ts.map +1 -1
- package/dist/graph/mappers/utils.js +0 -19
- package/dist/graph/mappers/utils.js.map +1 -1
- package/dist/graph/repository.d.ts +409 -276
- package/dist/graph/repository.d.ts.map +1 -1
- package/dist/graph/repository.js +772 -1082
- package/dist/graph/repository.js.map +1 -1
- package/dist/ids/mint.d.ts +31 -0
- package/dist/ids/mint.d.ts.map +1 -0
- package/dist/ids/mint.js +36 -0
- package/dist/ids/mint.js.map +1 -0
- package/dist/ids/next-action.d.ts +29 -0
- package/dist/ids/next-action.d.ts.map +1 -0
- package/dist/ids/next-action.js +58 -0
- package/dist/ids/next-action.js.map +1 -0
- package/dist/ids/resolver.d.ts +31 -0
- package/dist/ids/resolver.d.ts.map +1 -0
- package/dist/ids/resolver.js +52 -0
- package/dist/ids/resolver.js.map +1 -0
- package/dist/ids/schema.d.ts +91 -0
- package/dist/ids/schema.d.ts.map +1 -0
- package/dist/ids/schema.js +131 -0
- package/dist/ids/schema.js.map +1 -0
- package/dist/ids/token.d.ts +62 -0
- package/dist/ids/token.d.ts.map +1 -0
- package/dist/ids/token.js +225 -0
- package/dist/ids/token.js.map +1 -0
- package/dist/index.d.ts +10 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +255 -5925
- package/dist/index.js.map +1 -1
- package/dist/parsers/index.d.ts +0 -1
- package/dist/parsers/index.d.ts.map +1 -1
- package/dist/parsers/index.js +0 -1
- package/dist/parsers/index.js.map +1 -1
- package/dist/registry/all-tools.d.ts +14 -0
- package/dist/registry/all-tools.d.ts.map +1 -0
- package/dist/registry/all-tools.js +68 -0
- package/dist/registry/all-tools.js.map +1 -0
- package/dist/registry/context.d.ts +19 -0
- package/dist/registry/context.d.ts.map +1 -0
- package/dist/registry/context.js +20 -0
- package/dist/registry/context.js.map +1 -0
- package/dist/registry/define-tool.d.ts +15 -0
- package/dist/registry/define-tool.d.ts.map +1 -0
- package/dist/registry/define-tool.js +8 -0
- package/dist/registry/define-tool.js.map +1 -0
- package/dist/registry/elicit-links.d.ts +35 -0
- package/dist/registry/elicit-links.d.ts.map +1 -0
- package/dist/registry/elicit-links.js +102 -0
- package/dist/registry/elicit-links.js.map +1 -0
- package/dist/registry/elicitor.d.ts +24 -0
- package/dist/registry/elicitor.d.ts.map +1 -0
- package/dist/registry/elicitor.js +51 -0
- package/dist/registry/elicitor.js.map +1 -0
- package/dist/registry/index.d.ts +13 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +8 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/registry.d.ts +73 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/registry.js +221 -0
- package/dist/registry/registry.js.map +1 -0
- package/dist/registry/types.d.ts +146 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +6 -0
- package/dist/registry/types.js.map +1 -0
- package/dist/search/compiler.d.ts +49 -0
- package/dist/search/compiler.d.ts.map +1 -0
- package/dist/search/compiler.js +168 -0
- package/dist/search/compiler.js.map +1 -0
- package/dist/signature.js +2 -2
- package/dist/signature.js.map +1 -1
- package/dist/state/delta-store.d.ts +85 -0
- package/dist/state/delta-store.d.ts.map +1 -0
- package/dist/state/delta-store.js +124 -0
- package/dist/state/delta-store.js.map +1 -0
- package/dist/state/migrate.d.ts +23 -0
- package/dist/state/migrate.d.ts.map +1 -0
- package/dist/state/migrate.js +81 -0
- package/dist/state/migrate.js.map +1 -0
- package/dist/state/schema.d.ts +26 -0
- package/dist/state/schema.d.ts.map +1 -0
- package/dist/state/schema.js +77 -0
- package/dist/state/schema.js.map +1 -0
- package/dist/state/store.d.ts +155 -0
- package/dist/state/store.d.ts.map +1 -0
- package/dist/state/store.js +321 -0
- package/dist/state/store.js.map +1 -0
- package/dist/tools/calendar-graph.d.ts +41 -0
- package/dist/tools/calendar-graph.d.ts.map +1 -0
- package/dist/tools/calendar-graph.js +299 -0
- package/dist/tools/calendar-graph.js.map +1 -0
- package/dist/tools/calendar-permissions.d.ts +17 -7
- package/dist/tools/calendar-permissions.d.ts.map +1 -1
- package/dist/tools/calendar-permissions.js +60 -4
- package/dist/tools/calendar-permissions.js.map +1 -1
- package/dist/tools/calendar.d.ts +133 -82
- package/dist/tools/calendar.d.ts.map +1 -1
- package/dist/tools/calendar.js +249 -136
- package/dist/tools/calendar.js.map +1 -1
- package/dist/tools/categories.d.ts +14 -4
- package/dist/tools/categories.d.ts.map +1 -1
- package/dist/tools/categories.js +59 -2
- package/dist/tools/categories.js.map +1 -1
- package/dist/tools/checklist-items.d.ts +19 -9
- package/dist/tools/checklist-items.d.ts.map +1 -1
- package/dist/tools/checklist-items.js +71 -5
- package/dist/tools/checklist-items.js.map +1 -1
- package/dist/tools/contact-folders.d.ts +50 -0
- package/dist/tools/contact-folders.d.ts.map +1 -0
- package/dist/tools/contact-folders.js +136 -0
- package/dist/tools/contact-folders.js.map +1 -0
- package/dist/tools/contacts-graph.d.ts +45 -0
- package/dist/tools/contacts-graph.d.ts.map +1 -0
- package/dist/tools/contacts-graph.js +174 -0
- package/dist/tools/contacts-graph.js.map +1 -0
- package/dist/tools/contacts.d.ts +61 -30
- package/dist/tools/contacts.d.ts.map +1 -1
- package/dist/tools/contacts.js +150 -93
- package/dist/tools/contacts.js.map +1 -1
- package/dist/tools/excel.d.ts +20 -10
- package/dist/tools/excel.d.ts.map +1 -1
- package/dist/tools/excel.js +82 -6
- package/dist/tools/excel.js.map +1 -1
- package/dist/tools/focused-overrides.d.ts +14 -4
- package/dist/tools/focused-overrides.d.ts.map +1 -1
- package/dist/tools/focused-overrides.js +59 -2
- package/dist/tools/focused-overrides.js.map +1 -1
- package/dist/tools/index.d.ts +4 -5
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -6
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/linked-resources.d.ts +17 -7
- package/dist/tools/linked-resources.d.ts.map +1 -1
- package/dist/tools/linked-resources.js +60 -4
- package/dist/tools/linked-resources.js.map +1 -1
- package/dist/tools/mail-graph.d.ts +38 -0
- package/dist/tools/mail-graph.d.ts.map +1 -0
- package/dist/tools/mail-graph.js +187 -0
- package/dist/tools/mail-graph.js.map +1 -0
- package/dist/tools/mail-rules.d.ts +20 -8
- package/dist/tools/mail-rules.d.ts.map +1 -1
- package/dist/tools/mail-rules.js +63 -7
- package/dist/tools/mail-rules.js.map +1 -1
- package/dist/tools/mail-send.d.ts +39 -27
- package/dist/tools/mail-send.d.ts.map +1 -1
- package/dist/tools/mail-send.js +249 -24
- package/dist/tools/mail-send.js.map +1 -1
- package/dist/tools/mail.d.ts +91 -68
- package/dist/tools/mail.d.ts.map +1 -1
- package/dist/tools/mail.js +232 -243
- package/dist/tools/mail.js.map +1 -1
- package/dist/tools/mailbox-organization.d.ts +51 -34
- package/dist/tools/mailbox-organization.d.ts.map +1 -1
- package/dist/tools/mailbox-organization.js +318 -35
- package/dist/tools/mailbox-organization.js.map +1 -1
- package/dist/tools/mailbox-settings.d.ts +59 -0
- package/dist/tools/mailbox-settings.d.ts.map +1 -0
- package/dist/tools/mailbox-settings.js +136 -0
- package/dist/tools/mailbox-settings.js.map +1 -0
- package/dist/tools/meetings.d.ts +24 -14
- package/dist/tools/meetings.d.ts.map +1 -1
- package/dist/tools/meetings.js +82 -6
- package/dist/tools/meetings.js.map +1 -1
- package/dist/tools/onedrive.d.ts +26 -16
- package/dist/tools/onedrive.d.ts.map +1 -1
- package/dist/tools/onedrive.js +140 -11
- package/dist/tools/onedrive.js.map +1 -1
- package/dist/tools/onenote.d.ts +63 -0
- package/dist/tools/onenote.d.ts.map +1 -0
- package/dist/tools/onenote.js +221 -0
- package/dist/tools/onenote.js.map +1 -0
- package/dist/tools/people.d.ts +10 -0
- package/dist/tools/people.d.ts.map +1 -1
- package/dist/tools/people.js +94 -0
- package/dist/tools/people.js.map +1 -1
- package/dist/tools/planner-visualization.d.ts +15 -5
- package/dist/tools/planner-visualization.d.ts.map +1 -1
- package/dist/tools/planner-visualization.js +58 -4
- package/dist/tools/planner-visualization.js.map +1 -1
- package/dist/tools/planner.d.ts +69 -39
- package/dist/tools/planner.d.ts.map +1 -1
- package/dist/tools/planner.js +231 -23
- package/dist/tools/planner.js.map +1 -1
- package/dist/tools/scheduling.d.ts +12 -0
- package/dist/tools/scheduling.d.ts.map +1 -1
- package/dist/tools/scheduling.js +38 -0
- package/dist/tools/scheduling.js.map +1 -1
- package/dist/tools/shared-mailbox.d.ts +164 -0
- package/dist/tools/shared-mailbox.d.ts.map +1 -0
- package/dist/tools/shared-mailbox.js +318 -0
- package/dist/tools/shared-mailbox.js.map +1 -0
- package/dist/tools/sharepoint-lists.d.ts +131 -0
- package/dist/tools/sharepoint-lists.d.ts.map +1 -0
- package/dist/tools/sharepoint-lists.js +246 -0
- package/dist/tools/sharepoint-lists.js.map +1 -0
- package/dist/tools/sharepoint.d.ts +24 -14
- package/dist/tools/sharepoint.d.ts.map +1 -1
- package/dist/tools/sharepoint.js +85 -9
- package/dist/tools/sharepoint.js.map +1 -1
- package/dist/tools/task-attachments.d.ts +17 -7
- package/dist/tools/task-attachments.d.ts.map +1 -1
- package/dist/tools/task-attachments.js +60 -4
- package/dist/tools/task-attachments.js.map +1 -1
- package/dist/tools/task-lists.d.ts +56 -0
- package/dist/tools/task-lists.d.ts.map +1 -0
- package/dist/tools/task-lists.js +155 -0
- package/dist/tools/task-lists.js.map +1 -0
- package/dist/tools/tasks-graph.d.ts +49 -0
- package/dist/tools/tasks-graph.d.ts.map +1 -0
- package/dist/tools/tasks-graph.js +193 -0
- package/dist/tools/tasks-graph.js.map +1 -0
- package/dist/tools/tasks.d.ts +121 -29
- package/dist/tools/tasks.d.ts.map +1 -1
- package/dist/tools/tasks.js +136 -84
- package/dist/tools/tasks.js.map +1 -1
- package/dist/tools/teams.d.ts +57 -46
- package/dist/tools/teams.d.ts.map +1 -1
- package/dist/tools/teams.js +288 -26
- package/dist/tools/teams.js.map +1 -1
- package/dist/tools/what-changed.d.ts +48 -0
- package/dist/tools/what-changed.d.ts.map +1 -0
- package/dist/tools/what-changed.js +149 -0
- package/dist/tools/what-changed.js.map +1 -0
- package/dist/types/calendar.d.ts +1 -1
- package/dist/types/calendar.d.ts.map +1 -1
- package/dist/types/calendar.js.map +1 -1
- package/dist/types/contacts.d.ts +1 -1
- package/dist/types/contacts.d.ts.map +1 -1
- package/dist/types/contacts.js.map +1 -1
- package/dist/types/mail.d.ts +1 -1
- package/dist/types/mail.d.ts.map +1 -1
- package/dist/types/mail.js.map +1 -1
- package/dist/utils/errors.d.ts +110 -38
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +239 -56
- package/dist/utils/errors.js.map +1 -1
- package/dist/visualization/types.d.ts +4 -4
- package/package.json +2 -3
- package/dist/applescript/account-repository.d.ts +0 -30
- package/dist/applescript/account-repository.d.ts.map +0 -1
- package/dist/applescript/account-repository.js +0 -38
- package/dist/applescript/account-repository.js.map +0 -1
- package/dist/applescript/account-scripts.d.ts +0 -21
- package/dist/applescript/account-scripts.d.ts.map +0 -1
- package/dist/applescript/account-scripts.js +0 -180
- package/dist/applescript/account-scripts.js.map +0 -1
- package/dist/applescript/calendar-manager.d.ts +0 -44
- package/dist/applescript/calendar-manager.d.ts.map +0 -1
- package/dist/applescript/calendar-manager.js +0 -92
- package/dist/applescript/calendar-manager.js.map +0 -1
- package/dist/applescript/calendar-writer.d.ts +0 -36
- package/dist/applescript/calendar-writer.d.ts.map +0 -1
- package/dist/applescript/calendar-writer.js +0 -94
- package/dist/applescript/calendar-writer.js.map +0 -1
- package/dist/applescript/content-readers.d.ts +0 -114
- package/dist/applescript/content-readers.d.ts.map +0 -1
- package/dist/applescript/content-readers.js +0 -328
- package/dist/applescript/content-readers.js.map +0 -1
- package/dist/applescript/executor.d.ts +0 -60
- package/dist/applescript/executor.d.ts.map +0 -1
- package/dist/applescript/executor.js +0 -173
- package/dist/applescript/executor.js.map +0 -1
- package/dist/applescript/index.d.ts +0 -20
- package/dist/applescript/index.d.ts.map +0 -1
- package/dist/applescript/index.js +0 -29
- package/dist/applescript/index.js.map +0 -1
- package/dist/applescript/mail-sender.d.ts +0 -38
- package/dist/applescript/mail-sender.d.ts.map +0 -1
- package/dist/applescript/mail-sender.js +0 -67
- package/dist/applescript/mail-sender.js.map +0 -1
- package/dist/applescript/parser.d.ts +0 -235
- package/dist/applescript/parser.d.ts.map +0 -1
- package/dist/applescript/parser.js +0 -496
- package/dist/applescript/parser.js.map +0 -1
- package/dist/applescript/repository.d.ts +0 -64
- package/dist/applescript/repository.d.ts.map +0 -1
- package/dist/applescript/repository.js +0 -444
- package/dist/applescript/repository.js.map +0 -1
- package/dist/applescript/scripts.d.ts +0 -265
- package/dist/applescript/scripts.d.ts.map +0 -1
- package/dist/applescript/scripts.js +0 -1483
- package/dist/applescript/scripts.js.map +0 -1
- package/dist/parsers/olk15.d.ts +0 -87
- package/dist/parsers/olk15.d.ts.map +0 -1
- package/dist/parsers/olk15.js +0 -368
- package/dist/parsers/olk15.js.map +0 -1
- package/dist/tools/notes.d.ts +0 -74
- package/dist/tools/notes.d.ts.map +0 -1
- package/dist/tools/notes.js +0 -136
- package/dist/tools/notes.js.map +0 -1
|
@@ -12,10 +12,47 @@
|
|
|
12
12
|
* - Error handling
|
|
13
13
|
*/
|
|
14
14
|
import 'isomorphic-fetch';
|
|
15
|
-
import { Client } from '@microsoft/microsoft-graph-client';
|
|
15
|
+
import { Client, ResponseType, AuthenticationHandler, RetryHandler, RetryHandlerOptions, RedirectHandler, HTTPMessageHandler, } from '@microsoft/microsoft-graph-client';
|
|
16
16
|
import { getAccessToken } from '../auth/index.js';
|
|
17
17
|
import { buildBatchPayload, splitIntoBatches, parseBatchResponse } from './batch.js';
|
|
18
18
|
import { ResponseCache, CacheTTL, createCacheKey } from './cache.js';
|
|
19
|
+
import { ImmutableIdMiddleware } from './immutable-id-middleware.js';
|
|
20
|
+
// The SDK's RetryHandler only considers 429/503/504 retriable (its private
|
|
21
|
+
// static RETRY_STATUS_CODES), gating `this.isRetry(response)` BEFORE our
|
|
22
|
+
// shouldRetry callback runs — so a callback can only *narrow* that set, never
|
|
23
|
+
// widen it. D5 also wants 502 Bad Gateway retried, so we widen the SDK's set
|
|
24
|
+
// once at load. Our shouldRetry still narrows to idempotent GET/HEAD/OPTIONS,
|
|
25
|
+
// and this server builds only its own client, so the shared-static mutation
|
|
26
|
+
// has no cross-tenant effect.
|
|
27
|
+
const RETRY_STATUS_CODES = RetryHandler
|
|
28
|
+
.RETRY_STATUS_CODES;
|
|
29
|
+
if (Array.isArray(RETRY_STATUS_CODES) && !RETRY_STATUS_CODES.includes(502)) {
|
|
30
|
+
RETRY_STATUS_CODES.push(502);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* D5 retry policy (exported for testing). Retries only idempotent reads on
|
|
34
|
+
* transient failures — 429 plus 502/503/504 — honoring Retry-After via the
|
|
35
|
+
* SDK-computed delay. NEVER retries a send/write once the body is on the wire:
|
|
36
|
+
* an ambiguous 429/5xx on a POST could double-send. Paired with the module-load
|
|
37
|
+
* widening above (the SDK gates 502 out by default), this makes 502 genuinely
|
|
38
|
+
* retriable for idempotent reads while leaving writes/sendMail untouched.
|
|
39
|
+
*/
|
|
40
|
+
export function shouldRetryGraphRequest(method, url, status) {
|
|
41
|
+
const m = method.toUpperCase();
|
|
42
|
+
if (m !== 'GET' && m !== 'HEAD' && m !== 'OPTIONS') {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
// Never retry an OData action (POST-shaped operation like /sendMail, /reply,
|
|
46
|
+
// /createReply of writes) even if the transport surfaces it as a GET. Anchor
|
|
47
|
+
// the action to the end of the path (or a query string) so a drive item
|
|
48
|
+
// literally named e.g. "reply.docx" read via GET is still retriable.
|
|
49
|
+
if (/\/(sendMail|reply|forward|createReply|createForward)(?:\?|$)/i.test(url)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return status === 429 || status === 502 || status === 503 || status === 504;
|
|
53
|
+
}
|
|
54
|
+
/** Fields selected for message search results (shared across search mechanisms). */
|
|
55
|
+
const MESSAGE_SEARCH_SELECT = 'id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId,internetMessageId,parentFolderId';
|
|
19
56
|
/**
|
|
20
57
|
* Graph client wrapper with caching and token management.
|
|
21
58
|
*/
|
|
@@ -32,18 +69,33 @@ export class GraphClient {
|
|
|
32
69
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
33
70
|
async getClient() {
|
|
34
71
|
if (this.client == null) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
72
|
+
const authProvider = {
|
|
73
|
+
getAccessToken: () => getAccessToken(this.deviceCodeCallback),
|
|
74
|
+
};
|
|
75
|
+
const shouldRetry = (_delay, _attempt, request, options, response) => {
|
|
76
|
+
const method = (options?.method ?? 'GET').toString();
|
|
77
|
+
// The SDK types `request` as RequestInfo, which resolves to `any` under
|
|
78
|
+
// this repo's lib config; cast to the shape we read to stay lint-clean.
|
|
79
|
+
const url = typeof request === 'string' ? request : String(request.url ?? '');
|
|
80
|
+
return shouldRetryGraphRequest(method, url, response?.status ?? 0);
|
|
81
|
+
};
|
|
82
|
+
// Chain mirrors the SDK default order (auth → retry → redirect → http),
|
|
83
|
+
// minus the cosmetic TelemetryHandler, plus the immutable-ID preference
|
|
84
|
+
// (U5b-3) sitting right after auth so its Prefer header rides every retry
|
|
85
|
+
// attempt without being re-appended. RedirectHandler matters: the binary
|
|
86
|
+
// download endpoints (/content, /photo/$value, meeting recordings) 302 to
|
|
87
|
+
// a pre-authenticated CDN URL, so the redirect must be followed explicitly
|
|
88
|
+
// rather than relying on the underlying fetch default.
|
|
89
|
+
const auth = new AuthenticationHandler(authProvider);
|
|
90
|
+
const immutableId = new ImmutableIdMiddleware();
|
|
91
|
+
const retry = new RetryHandler(new RetryHandlerOptions(undefined, undefined, shouldRetry));
|
|
92
|
+
const redirect = new RedirectHandler();
|
|
93
|
+
const http = new HTTPMessageHandler();
|
|
94
|
+
auth.setNext(immutableId);
|
|
95
|
+
immutableId.setNext(retry);
|
|
96
|
+
retry.setNext(redirect);
|
|
97
|
+
redirect.setNext(http);
|
|
98
|
+
this.client = Client.initWithMiddleware({ middleware: auth });
|
|
47
99
|
}
|
|
48
100
|
return this.client;
|
|
49
101
|
}
|
|
@@ -160,7 +212,9 @@ export class GraphClient {
|
|
|
160
212
|
.select('id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId,internetMessageId,parentFolderId')
|
|
161
213
|
.top(limit)
|
|
162
214
|
.get();
|
|
163
|
-
|
|
215
|
+
const messages = response.value;
|
|
216
|
+
await this.upgradeMessageIdsToImmutable(messages);
|
|
217
|
+
return messages;
|
|
164
218
|
}
|
|
165
219
|
/**
|
|
166
220
|
* Searches messages in a specific folder.
|
|
@@ -173,35 +227,142 @@ export class GraphClient {
|
|
|
173
227
|
.select('id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId,internetMessageId')
|
|
174
228
|
.top(limit)
|
|
175
229
|
.get();
|
|
176
|
-
|
|
230
|
+
const messages = response.value;
|
|
231
|
+
await this.upgradeMessageIdsToImmutable(messages);
|
|
232
|
+
return messages;
|
|
177
233
|
}
|
|
178
234
|
/**
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* enabling KQL operators like from:, subject:, hasAttachments:, received>=, AND, OR.
|
|
235
|
+
* Property-only structured search (U7 / D9): `$filter` on messages. The filter
|
|
236
|
+
* string is built server-side by the search compiler (validated OData syntax).
|
|
182
237
|
*/
|
|
183
|
-
async
|
|
238
|
+
async searchMessagesFilter(filter, limit = 50) {
|
|
184
239
|
const client = await this.getClient();
|
|
240
|
+
// No $orderby: Graph returns InefficientFilter when a sender/flag $filter is
|
|
241
|
+
// combined with $orderby (D9 spike), and /me/messages already defaults to
|
|
242
|
+
// receivedDateTime desc, so results stay recent-first without it.
|
|
185
243
|
const response = await client
|
|
186
244
|
.api('/me/messages')
|
|
187
|
-
.
|
|
188
|
-
.select(
|
|
245
|
+
.filter(filter)
|
|
246
|
+
.select(MESSAGE_SEARCH_SELECT)
|
|
189
247
|
.top(limit)
|
|
190
248
|
.get();
|
|
191
249
|
return response.value;
|
|
192
250
|
}
|
|
193
251
|
/**
|
|
194
|
-
*
|
|
252
|
+
* Free-text structured search (U7 / D9): quoted `$search` on messages. The
|
|
253
|
+
* search value is built by the compiler already correctly quoted (e.g.
|
|
254
|
+
* `"subject:report"`), which the D9 spike confirmed property-scopes correctly.
|
|
195
255
|
*/
|
|
196
|
-
async
|
|
256
|
+
async searchMessagesSearchValue(searchValue, limit = 50) {
|
|
197
257
|
const client = await this.getClient();
|
|
198
258
|
const response = await client
|
|
199
|
-
.api(
|
|
200
|
-
.search(
|
|
201
|
-
.select(
|
|
259
|
+
.api('/me/messages')
|
|
260
|
+
.search(searchValue)
|
|
261
|
+
.select(MESSAGE_SEARCH_SELECT)
|
|
202
262
|
.top(limit)
|
|
203
263
|
.get();
|
|
204
|
-
|
|
264
|
+
const messages = response.value;
|
|
265
|
+
await this.upgradeMessageIdsToImmutable(messages);
|
|
266
|
+
return messages;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Mixed property + free-text structured search (U7 / D9): `POST /search/query`
|
|
270
|
+
* with a server-built KQL string — the only single-request path when both are
|
|
271
|
+
* present. Normalizes the hitsContainers response to a Message[].
|
|
272
|
+
*
|
|
273
|
+
* Limitation (verified in the D9 spike): /search/query populates from/subject/
|
|
274
|
+
* receivedDateTime/isRead/hasAttachments/parentFolderId, but NOT toRecipients
|
|
275
|
+
* or flag, regardless of the requested fields — so mixed-mode results carry no
|
|
276
|
+
* recipient list or flag. Acceptable for a result list (sender/subject/date
|
|
277
|
+
* are present); the property-only and free-text-only paths are unaffected.
|
|
278
|
+
*/
|
|
279
|
+
async searchMessagesQuery(kql, limit = 50) {
|
|
280
|
+
const client = await this.getClient();
|
|
281
|
+
const body = {
|
|
282
|
+
requests: [
|
|
283
|
+
{
|
|
284
|
+
entityTypes: ['message'],
|
|
285
|
+
query: { queryString: kql },
|
|
286
|
+
from: 0,
|
|
287
|
+
size: limit,
|
|
288
|
+
fields: MESSAGE_SEARCH_SELECT.split(','),
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
};
|
|
292
|
+
const response = (await client.api('/search/query').post(body));
|
|
293
|
+
const hits = response.value?.[0]?.hitsContainers?.[0]?.hits ?? [];
|
|
294
|
+
// Guard against a hit without a resource so no undefined enters the mapper.
|
|
295
|
+
const messages = hits.map((h) => h.resource).filter((r) => r != null);
|
|
296
|
+
await this.upgradeMessageIdsToImmutable(messages);
|
|
297
|
+
return messages;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Translates Exchange item IDs between formats (U5b-3 / D2). Used to upgrade
|
|
301
|
+
* the mutable REST IDs that `$search` returns into immutable REST entry IDs, so
|
|
302
|
+
* search-minted self-encoding tokens (`em_`) survive a later move. Returns a map
|
|
303
|
+
* of source ID → immutable target ID; IDs that Graph could not translate are
|
|
304
|
+
* simply absent (partial success is normal and handled by the caller).
|
|
305
|
+
*/
|
|
306
|
+
async translateExchangeIds(inputIds, sourceIdType = 'restId', targetIdType = 'restImmutableEntryId') {
|
|
307
|
+
const out = new Map();
|
|
308
|
+
if (inputIds.length === 0) {
|
|
309
|
+
return out;
|
|
310
|
+
}
|
|
311
|
+
const client = await this.getClient();
|
|
312
|
+
// Graph caps translateExchangeIds at 1000 input IDs per call; chunk to stay
|
|
313
|
+
// under it even though search result sets are far smaller.
|
|
314
|
+
for (let i = 0; i < inputIds.length; i += 1000) {
|
|
315
|
+
const chunk = inputIds.slice(i, i + 1000);
|
|
316
|
+
const response = (await client.api('/me/translateExchangeIds').post({
|
|
317
|
+
inputIds: chunk,
|
|
318
|
+
sourceIdType,
|
|
319
|
+
targetIdType,
|
|
320
|
+
}));
|
|
321
|
+
for (const pair of response.value ?? []) {
|
|
322
|
+
if (pair.sourceId != null && pair.sourceId.length > 0 && pair.targetId != null && pair.targetId.length > 0) {
|
|
323
|
+
out.set(pair.sourceId, pair.targetId);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return out;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Rewrites the mutable `$search`-minted IDs on a message list to their
|
|
331
|
+
* immutable equivalents in place (U5b-3 / D2), so the mapper mints durable
|
|
332
|
+
* `em_` tokens. Graceful degradation: if translation fails wholesale (throw) or
|
|
333
|
+
* for individual IDs (absent from the map), those messages keep their mutable
|
|
334
|
+
* ID — the `em_` token still resolves this session, it just isn't move-durable.
|
|
335
|
+
* Returns the number of IDs left un-upgraded (the caller's `degraded_ids` count).
|
|
336
|
+
*/
|
|
337
|
+
async upgradeMessageIdsToImmutable(messages) {
|
|
338
|
+
const mutableIds = messages
|
|
339
|
+
.map((m) => m.id)
|
|
340
|
+
.filter((id) => typeof id === 'string' && id.length > 0);
|
|
341
|
+
if (mutableIds.length === 0) {
|
|
342
|
+
return 0;
|
|
343
|
+
}
|
|
344
|
+
let translated;
|
|
345
|
+
try {
|
|
346
|
+
translated = await this.translateExchangeIds(mutableIds);
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
// Whole-batch failure (throttled/unavailable): leave every ID mutable.
|
|
350
|
+
return mutableIds.length;
|
|
351
|
+
}
|
|
352
|
+
let degraded = 0;
|
|
353
|
+
for (const message of messages) {
|
|
354
|
+
if (typeof message.id !== 'string' || message.id.length === 0) {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const immutable = translated.get(message.id);
|
|
358
|
+
if (immutable != null) {
|
|
359
|
+
message.id = immutable;
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
degraded += 1;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return degraded;
|
|
205
366
|
}
|
|
206
367
|
/**
|
|
207
368
|
* Lists messages in a conversation by conversationId.
|
|
@@ -237,27 +398,62 @@ export class GraphClient {
|
|
|
237
398
|
*/
|
|
238
399
|
async getMessagesDelta(folderId, deltaLink) {
|
|
239
400
|
const client = await this.getClient();
|
|
240
|
-
let
|
|
401
|
+
let page;
|
|
241
402
|
if (deltaLink != null) {
|
|
242
|
-
|
|
403
|
+
page = await client.api(deltaLink).get();
|
|
243
404
|
}
|
|
244
405
|
else {
|
|
245
|
-
|
|
406
|
+
page = await client
|
|
246
407
|
.api(`/me/mailFolders/${folderId}/messages/delta`)
|
|
247
408
|
.select('id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId,internetMessageId,parentFolderId')
|
|
248
409
|
.top(50)
|
|
249
410
|
.get();
|
|
250
411
|
}
|
|
251
|
-
const messages = [...(
|
|
252
|
-
|
|
412
|
+
const messages = [...(page.value ?? [])];
|
|
413
|
+
// The deltaLink lands on the LAST page; paging through nextLinks and reading
|
|
414
|
+
// it from the first response would yield '' whenever the delta spans >1 page.
|
|
415
|
+
let nextLink = page['@odata.nextLink'];
|
|
253
416
|
while (nextLink != null) {
|
|
254
|
-
|
|
255
|
-
messages.push(...(
|
|
256
|
-
nextLink =
|
|
417
|
+
page = await client.api(nextLink).get();
|
|
418
|
+
messages.push(...(page.value ?? []));
|
|
419
|
+
nextLink = page['@odata.nextLink'];
|
|
257
420
|
}
|
|
258
|
-
const newDeltaLink =
|
|
421
|
+
const newDeltaLink = page['@odata.deltaLink'] ?? '';
|
|
259
422
|
return { messages, deltaLink: newDeltaLink };
|
|
260
423
|
}
|
|
424
|
+
/**
|
|
425
|
+
* Gets calendar-view delta for incremental event sync (U12).
|
|
426
|
+
*
|
|
427
|
+
* The v1.0 event delta is served by `/me/calendarView/delta`, bounded by a
|
|
428
|
+
* start/end window that is baked into the returned deltaLink — subsequent
|
|
429
|
+
* rounds just follow that link. `$select` is unsupported here, so full event
|
|
430
|
+
* objects come back; deletes arrive as `@removed` entries. The deltaLink lands
|
|
431
|
+
* on the *last* page, so it is read after paging (not from the first response).
|
|
432
|
+
*/
|
|
433
|
+
async getCalendarViewDelta(startDateTime, endDateTime, deltaLink) {
|
|
434
|
+
const client = await this.getClient();
|
|
435
|
+
let page;
|
|
436
|
+
if (deltaLink != null) {
|
|
437
|
+
page = await client.api(deltaLink).get();
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
const start = encodeURIComponent(startDateTime);
|
|
441
|
+
const end = encodeURIComponent(endDateTime);
|
|
442
|
+
page = await client
|
|
443
|
+
.api(`/me/calendarView/delta?startDateTime=${start}&endDateTime=${end}`)
|
|
444
|
+
.header('Prefer', 'odata.maxpagesize=50')
|
|
445
|
+
.get();
|
|
446
|
+
}
|
|
447
|
+
const events = [...(page.value ?? [])];
|
|
448
|
+
let nextLink = page['@odata.nextLink'];
|
|
449
|
+
while (nextLink != null) {
|
|
450
|
+
page = await client.api(nextLink).get();
|
|
451
|
+
events.push(...(page.value ?? []));
|
|
452
|
+
nextLink = page['@odata.nextLink'];
|
|
453
|
+
}
|
|
454
|
+
const newDeltaLink = page['@odata.deltaLink'] ?? '';
|
|
455
|
+
return { events, deltaLink: newDeltaLink };
|
|
456
|
+
}
|
|
261
457
|
// ===========================================================================
|
|
262
458
|
// Mail Rules
|
|
263
459
|
// ===========================================================================
|
|
@@ -560,7 +756,7 @@ export class GraphClient {
|
|
|
560
756
|
const client = await this.getClient();
|
|
561
757
|
return await client
|
|
562
758
|
.api(`/me/contacts/${contactId}/photo/$value`)
|
|
563
|
-
.get();
|
|
759
|
+
.responseType(ResponseType.ARRAYBUFFER).get();
|
|
564
760
|
}
|
|
565
761
|
/**
|
|
566
762
|
* Sets or updates the photo for a contact.
|
|
@@ -628,7 +824,7 @@ export class GraphClient {
|
|
|
628
824
|
const client = await this.getClient();
|
|
629
825
|
const response = await client
|
|
630
826
|
.api('/me/todo/lists')
|
|
631
|
-
.select('id,displayName,
|
|
827
|
+
.select('id,displayName,wellknownListName')
|
|
632
828
|
.get();
|
|
633
829
|
const result = response.value;
|
|
634
830
|
this.cache.set(cacheKey, result, CacheTTL.FOLDERS);
|
|
@@ -894,22 +1090,32 @@ export class GraphClient {
|
|
|
894
1090
|
/**
|
|
895
1091
|
* Creates a reply draft for a message.
|
|
896
1092
|
*/
|
|
897
|
-
async createReplyDraft(messageId) {
|
|
1093
|
+
async createReplyDraft(messageId, comment, body) {
|
|
898
1094
|
const client = await this.getClient();
|
|
1095
|
+
const postBody = {};
|
|
1096
|
+
if (comment != null)
|
|
1097
|
+
postBody.comment = comment;
|
|
1098
|
+
if (body != null)
|
|
1099
|
+
postBody.message = { body };
|
|
899
1100
|
const result = await client
|
|
900
1101
|
.api(`/me/messages/${messageId}/createReply`)
|
|
901
|
-
.post(null);
|
|
1102
|
+
.post(Object.keys(postBody).length > 0 ? postBody : null);
|
|
902
1103
|
this.cache.clear();
|
|
903
1104
|
return result;
|
|
904
1105
|
}
|
|
905
1106
|
/**
|
|
906
1107
|
* Creates a reply-all draft for a message.
|
|
907
1108
|
*/
|
|
908
|
-
async createReplyAllDraft(messageId) {
|
|
1109
|
+
async createReplyAllDraft(messageId, comment, body) {
|
|
909
1110
|
const client = await this.getClient();
|
|
1111
|
+
const postBody = {};
|
|
1112
|
+
if (comment != null)
|
|
1113
|
+
postBody.comment = comment;
|
|
1114
|
+
if (body != null)
|
|
1115
|
+
postBody.message = { body };
|
|
910
1116
|
const result = await client
|
|
911
1117
|
.api(`/me/messages/${messageId}/createReplyAll`)
|
|
912
|
-
.post(null);
|
|
1118
|
+
.post(Object.keys(postBody).length > 0 ? postBody : null);
|
|
913
1119
|
this.cache.clear();
|
|
914
1120
|
return result;
|
|
915
1121
|
}
|
|
@@ -1493,6 +1699,10 @@ export class GraphClient {
|
|
|
1493
1699
|
const client = await this.getClient();
|
|
1494
1700
|
return await client.api('/planner/buckets').post({ planId, name });
|
|
1495
1701
|
}
|
|
1702
|
+
async getBucket(bucketId) {
|
|
1703
|
+
const client = await this.getClient();
|
|
1704
|
+
return await client.api(`/planner/buckets/${bucketId}`).get();
|
|
1705
|
+
}
|
|
1496
1706
|
async updateBucket(bucketId, updates, etag) {
|
|
1497
1707
|
const client = await this.getClient();
|
|
1498
1708
|
return await client.api(`/planner/buckets/${bucketId}`).header('If-Match', etag).patch(updates);
|
|
@@ -1509,6 +1719,12 @@ export class GraphClient {
|
|
|
1509
1719
|
const response = await client.api(`/planner/plans/${planId}/tasks`).get();
|
|
1510
1720
|
return response.value;
|
|
1511
1721
|
}
|
|
1722
|
+
/** All Planner tasks assigned to the signed-in user, across every plan. */
|
|
1723
|
+
async listMyPlannerTasks() {
|
|
1724
|
+
const client = await this.getClient();
|
|
1725
|
+
const response = await client.api('/me/planner/tasks').get();
|
|
1726
|
+
return response.value;
|
|
1727
|
+
}
|
|
1512
1728
|
async getPlannerTask(taskId) {
|
|
1513
1729
|
const client = await this.getClient();
|
|
1514
1730
|
return await client.api(`/planner/tasks/${taskId}`).get();
|
|
@@ -1561,7 +1777,7 @@ export class GraphClient {
|
|
|
1561
1777
|
}
|
|
1562
1778
|
async getUserPhoto(identifier) {
|
|
1563
1779
|
const client = await this.getClient();
|
|
1564
|
-
return await client.api(`/users/${identifier}/photo/$value`).get();
|
|
1780
|
+
return await client.api(`/users/${identifier}/photo/$value`).responseType(ResponseType.ARRAYBUFFER).get();
|
|
1565
1781
|
}
|
|
1566
1782
|
async getUserPresence(identifier) {
|
|
1567
1783
|
const client = await this.getClient();
|
|
@@ -1572,6 +1788,116 @@ export class GraphClient {
|
|
|
1572
1788
|
const response = await client.api('/communications/getPresencesByUserId').post({ ids: userIds });
|
|
1573
1789
|
return response.value;
|
|
1574
1790
|
}
|
|
1791
|
+
// ===========================================================================
|
|
1792
|
+
// Shared Mailbox / Delegate Access (/users/{upn}/...) — read-only (#40)
|
|
1793
|
+
// ===========================================================================
|
|
1794
|
+
//
|
|
1795
|
+
// These mirror the `/me/...` read paths but target another user's mailbox,
|
|
1796
|
+
// calendar, or drive via delegate/shared access. Unlike the `/me` variants,
|
|
1797
|
+
// they deliberately DO NOT swallow errors: a 403 must surface (mapped to
|
|
1798
|
+
// GRAPH_PERMISSION_DENIED at the dispatch chokepoint) so the caller learns
|
|
1799
|
+
// they lack shared access rather than getting a silent empty/`null` result.
|
|
1800
|
+
// Results are returned with raw Graph ids (durable tokens are `/me`- and
|
|
1801
|
+
// account-scoped, so they cannot address another mailbox's items).
|
|
1802
|
+
async listSharedMailFolders(mailbox) {
|
|
1803
|
+
const client = await this.getClient();
|
|
1804
|
+
const response = await client
|
|
1805
|
+
.api(`/users/${encodeURIComponent(mailbox)}/mailFolders`)
|
|
1806
|
+
.select('id,displayName,parentFolderId,totalItemCount,unreadItemCount')
|
|
1807
|
+
.top(100)
|
|
1808
|
+
.get();
|
|
1809
|
+
return response.value;
|
|
1810
|
+
}
|
|
1811
|
+
async listSharedMessages(mailbox, folderId, limit = 25, unreadOnly = false) {
|
|
1812
|
+
const client = await this.getClient();
|
|
1813
|
+
const apiPath = folderId != null
|
|
1814
|
+
? `/users/${encodeURIComponent(mailbox)}/mailFolders/${encodeURIComponent(folderId)}/messages`
|
|
1815
|
+
: `/users/${encodeURIComponent(mailbox)}/messages`;
|
|
1816
|
+
let request = client
|
|
1817
|
+
.api(apiPath)
|
|
1818
|
+
.select('id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId')
|
|
1819
|
+
.top(limit);
|
|
1820
|
+
if (unreadOnly) {
|
|
1821
|
+
// Graph rejects `$orderby` on a property not led by `$filter` (isRead),
|
|
1822
|
+
// so drop the sort when filtering unread and rely on the default order.
|
|
1823
|
+
request = request.filter('isRead eq false');
|
|
1824
|
+
}
|
|
1825
|
+
else {
|
|
1826
|
+
request = request.orderby('receivedDateTime desc');
|
|
1827
|
+
}
|
|
1828
|
+
const response = await request.get();
|
|
1829
|
+
return response.value;
|
|
1830
|
+
}
|
|
1831
|
+
async getSharedMessage(mailbox, messageId, includeBody = false) {
|
|
1832
|
+
const client = await this.getClient();
|
|
1833
|
+
const select = includeBody
|
|
1834
|
+
? 'id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,body,bodyPreview,conversationId,parentFolderId'
|
|
1835
|
+
: 'id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId,parentFolderId';
|
|
1836
|
+
return await client
|
|
1837
|
+
.api(`/users/${encodeURIComponent(mailbox)}/messages/${encodeURIComponent(messageId)}`)
|
|
1838
|
+
.select(select)
|
|
1839
|
+
.get();
|
|
1840
|
+
}
|
|
1841
|
+
async searchSharedMessages(mailbox, query, limit = 25) {
|
|
1842
|
+
const client = await this.getClient();
|
|
1843
|
+
// Mail $search cannot be combined with $orderby, so ordering is omitted.
|
|
1844
|
+
// Strip embedded double quotes so a stray `"` can't break out of the
|
|
1845
|
+
// quoted KQL `$search="..."` term.
|
|
1846
|
+
const safeQuery = query.replace(/"/g, ' ');
|
|
1847
|
+
const response = await client
|
|
1848
|
+
.api(`/users/${encodeURIComponent(mailbox)}/messages`)
|
|
1849
|
+
.query({ $search: `"${safeQuery}"` })
|
|
1850
|
+
.select('id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId')
|
|
1851
|
+
.top(limit)
|
|
1852
|
+
.get();
|
|
1853
|
+
return response.value;
|
|
1854
|
+
}
|
|
1855
|
+
async listSharedEvents(mailbox, limit = 25, startDate, endDate) {
|
|
1856
|
+
const client = await this.getClient();
|
|
1857
|
+
if (startDate != null && endDate != null) {
|
|
1858
|
+
const response = await client
|
|
1859
|
+
.api(`/users/${encodeURIComponent(mailbox)}/calendarView`)
|
|
1860
|
+
.query({ startDateTime: startDate.toISOString(), endDateTime: endDate.toISOString() })
|
|
1861
|
+
.select('id,subject,start,end,location,isAllDay,organizer,attendees,bodyPreview')
|
|
1862
|
+
.orderby('start/dateTime')
|
|
1863
|
+
.top(limit)
|
|
1864
|
+
.get();
|
|
1865
|
+
return response.value;
|
|
1866
|
+
}
|
|
1867
|
+
const response = await client
|
|
1868
|
+
.api(`/users/${encodeURIComponent(mailbox)}/events`)
|
|
1869
|
+
.select('id,subject,start,end,location,isAllDay,organizer,attendees,bodyPreview')
|
|
1870
|
+
.orderby('start/dateTime')
|
|
1871
|
+
.top(limit)
|
|
1872
|
+
.get();
|
|
1873
|
+
return response.value;
|
|
1874
|
+
}
|
|
1875
|
+
async getSharedEvent(mailbox, eventId) {
|
|
1876
|
+
const client = await this.getClient();
|
|
1877
|
+
return await client
|
|
1878
|
+
.api(`/users/${encodeURIComponent(mailbox)}/events/${encodeURIComponent(eventId)}`)
|
|
1879
|
+
.select('id,subject,start,end,location,isAllDay,organizer,attendees,body,recurrence,bodyPreview')
|
|
1880
|
+
.get();
|
|
1881
|
+
}
|
|
1882
|
+
async listSharedDriveItems(mailbox, itemId) {
|
|
1883
|
+
const client = await this.getClient();
|
|
1884
|
+
const apiPath = itemId != null
|
|
1885
|
+
? `/users/${encodeURIComponent(mailbox)}/drive/items/${encodeURIComponent(itemId)}/children`
|
|
1886
|
+
: `/users/${encodeURIComponent(mailbox)}/drive/root/children`;
|
|
1887
|
+
const response = await client.api(apiPath).get();
|
|
1888
|
+
return response.value;
|
|
1889
|
+
}
|
|
1890
|
+
async searchSharedDriveItems(mailbox, query, limit = 25) {
|
|
1891
|
+
const client = await this.getClient();
|
|
1892
|
+
// Escape for the OData string literal first (a lone `'` closes the literal;
|
|
1893
|
+
// OData escapes it by doubling), then URL-encode the whole term.
|
|
1894
|
+
const safeQuery = encodeURIComponent(query.replace(/'/g, "''"));
|
|
1895
|
+
const response = await client
|
|
1896
|
+
.api(`/users/${encodeURIComponent(mailbox)}/drive/root/search(q='${safeQuery}')`)
|
|
1897
|
+
.top(limit)
|
|
1898
|
+
.get();
|
|
1899
|
+
return response.value;
|
|
1900
|
+
}
|
|
1575
1901
|
/**
|
|
1576
1902
|
* Sends multiple requests in a single $batch call to the Graph API.
|
|
1577
1903
|
* Automatically splits into multiple batches if there are more than 20 requests.
|
|
@@ -1595,11 +1921,13 @@ export class GraphClient {
|
|
|
1595
1921
|
// ===========================================================================
|
|
1596
1922
|
async listOnlineMeetings(limit = 20) {
|
|
1597
1923
|
const client = await this.getClient();
|
|
1924
|
+
// Graph rejects $top on /me/onlineMeetings, so limit client-side. Guard a
|
|
1925
|
+
// negative limit (slice(0, -n) would count from the end).
|
|
1926
|
+
const cap = Math.max(0, limit);
|
|
1598
1927
|
const response = await client.api('/me/onlineMeetings')
|
|
1599
|
-
.top(limit)
|
|
1600
1928
|
.orderby('startDateTime desc')
|
|
1601
1929
|
.get();
|
|
1602
|
-
return response.value;
|
|
1930
|
+
return response.value.slice(0, cap);
|
|
1603
1931
|
}
|
|
1604
1932
|
async getOnlineMeeting(meetingId) {
|
|
1605
1933
|
const client = await this.getClient();
|
|
@@ -1612,7 +1940,7 @@ export class GraphClient {
|
|
|
1612
1940
|
}
|
|
1613
1941
|
async getMeetingRecordingContent(meetingId, recordingId) {
|
|
1614
1942
|
const client = await this.getClient();
|
|
1615
|
-
return await client.api(`/me/onlineMeetings/${meetingId}/recordings/${recordingId}/content`).get();
|
|
1943
|
+
return await client.api(`/me/onlineMeetings/${meetingId}/recordings/${recordingId}/content`).responseType(ResponseType.ARRAYBUFFER).get();
|
|
1616
1944
|
}
|
|
1617
1945
|
async listMeetingTranscripts(meetingId) {
|
|
1618
1946
|
const client = await this.getClient();
|
|
@@ -1685,7 +2013,7 @@ export class GraphClient {
|
|
|
1685
2013
|
}
|
|
1686
2014
|
async downloadDriveItem(itemId) {
|
|
1687
2015
|
const client = await this.getClient();
|
|
1688
|
-
return await client.api(`/me/drive/items/${itemId}/content`).get();
|
|
2016
|
+
return await client.api(`/me/drive/items/${itemId}/content`).responseType(ResponseType.ARRAYBUFFER).get();
|
|
1689
2017
|
}
|
|
1690
2018
|
async uploadDriveItem(parentPath, fileName, content) {
|
|
1691
2019
|
const client = await this.getClient();
|
|
@@ -1759,7 +2087,167 @@ export class GraphClient {
|
|
|
1759
2087
|
*/
|
|
1760
2088
|
async downloadLibraryFile(driveId, itemId) {
|
|
1761
2089
|
const client = await this.getClient();
|
|
1762
|
-
return await client.api(`/drives/${driveId}/items/${itemId}/content`).get();
|
|
2090
|
+
return await client.api(`/drives/${driveId}/items/${itemId}/content`).responseType(ResponseType.ARRAYBUFFER).get();
|
|
2091
|
+
}
|
|
2092
|
+
// ===========================================================================
|
|
2093
|
+
// SharePoint Lists
|
|
2094
|
+
// ===========================================================================
|
|
2095
|
+
/**
|
|
2096
|
+
* Lists the SharePoint lists in a site.
|
|
2097
|
+
*/
|
|
2098
|
+
async listSharePointLists(siteId) {
|
|
2099
|
+
const client = await this.getClient();
|
|
2100
|
+
const response = await client
|
|
2101
|
+
.api(`/sites/${siteId}/lists`)
|
|
2102
|
+
.select('id,name,displayName,description,webUrl,createdDateTime,lastModifiedDateTime')
|
|
2103
|
+
.get();
|
|
2104
|
+
return response.value;
|
|
2105
|
+
}
|
|
2106
|
+
/**
|
|
2107
|
+
* Gets a specific SharePoint list.
|
|
2108
|
+
*/
|
|
2109
|
+
async getSharePointList(siteId, listId) {
|
|
2110
|
+
const client = await this.getClient();
|
|
2111
|
+
return await client.api(`/sites/${siteId}/lists/${listId}`).get();
|
|
2112
|
+
}
|
|
2113
|
+
/**
|
|
2114
|
+
* Creates a SharePoint list in a site.
|
|
2115
|
+
*/
|
|
2116
|
+
async createSharePointList(siteId, body) {
|
|
2117
|
+
const client = await this.getClient();
|
|
2118
|
+
return await client.api(`/sites/${siteId}/lists`).post(body);
|
|
2119
|
+
}
|
|
2120
|
+
/**
|
|
2121
|
+
* Lists the column definitions for a SharePoint list.
|
|
2122
|
+
*/
|
|
2123
|
+
async listSharePointListColumns(siteId, listId) {
|
|
2124
|
+
const client = await this.getClient();
|
|
2125
|
+
const response = await client
|
|
2126
|
+
.api(`/sites/${siteId}/lists/${listId}/columns`)
|
|
2127
|
+
.get();
|
|
2128
|
+
return response.value;
|
|
2129
|
+
}
|
|
2130
|
+
/**
|
|
2131
|
+
* Lists the items in a SharePoint list, expanding their field values.
|
|
2132
|
+
*/
|
|
2133
|
+
async listSharePointListItems(siteId, listId, limit = 50) {
|
|
2134
|
+
const client = await this.getClient();
|
|
2135
|
+
const response = await client
|
|
2136
|
+
.api(`/sites/${siteId}/lists/${listId}/items`)
|
|
2137
|
+
.expand('fields')
|
|
2138
|
+
.top(limit)
|
|
2139
|
+
.get();
|
|
2140
|
+
return response.value;
|
|
2141
|
+
}
|
|
2142
|
+
/**
|
|
2143
|
+
* Gets a specific SharePoint list item, expanding its field values.
|
|
2144
|
+
*/
|
|
2145
|
+
async getSharePointListItem(siteId, listId, itemId) {
|
|
2146
|
+
const client = await this.getClient();
|
|
2147
|
+
return await client
|
|
2148
|
+
.api(`/sites/${siteId}/lists/${listId}/items/${itemId}`)
|
|
2149
|
+
.expand('fields')
|
|
2150
|
+
.get();
|
|
2151
|
+
}
|
|
2152
|
+
/**
|
|
2153
|
+
* Creates an item in a SharePoint list from a map of column → value.
|
|
2154
|
+
*/
|
|
2155
|
+
async createSharePointListItem(siteId, listId, fields) {
|
|
2156
|
+
const client = await this.getClient();
|
|
2157
|
+
return await client.api(`/sites/${siteId}/lists/${listId}/items`).post({ fields });
|
|
2158
|
+
}
|
|
2159
|
+
/**
|
|
2160
|
+
* Updates the field values of a SharePoint list item.
|
|
2161
|
+
*/
|
|
2162
|
+
async updateSharePointListItem(siteId, listId, itemId, fields) {
|
|
2163
|
+
const client = await this.getClient();
|
|
2164
|
+
return await client.api(`/sites/${siteId}/lists/${listId}/items/${itemId}/fields`).patch(fields);
|
|
2165
|
+
}
|
|
2166
|
+
/**
|
|
2167
|
+
* Deletes an item from a SharePoint list.
|
|
2168
|
+
*/
|
|
2169
|
+
async deleteSharePointListItem(siteId, listId, itemId) {
|
|
2170
|
+
const client = await this.getClient();
|
|
2171
|
+
await client.api(`/sites/${siteId}/lists/${listId}/items/${itemId}`).delete();
|
|
2172
|
+
}
|
|
2173
|
+
// ===========================================================================
|
|
2174
|
+
// OneNote
|
|
2175
|
+
// ===========================================================================
|
|
2176
|
+
/**
|
|
2177
|
+
* Lists all OneNote notebooks for the current user.
|
|
2178
|
+
*/
|
|
2179
|
+
async listNotebooks() {
|
|
2180
|
+
const client = await this.getClient();
|
|
2181
|
+
const response = await client.api('/me/onenote/notebooks').get();
|
|
2182
|
+
return response.value;
|
|
2183
|
+
}
|
|
2184
|
+
/**
|
|
2185
|
+
* Lists OneNote sections, optionally scoped to a notebook.
|
|
2186
|
+
*/
|
|
2187
|
+
async listNoteSections(notebookGraphId) {
|
|
2188
|
+
const client = await this.getClient();
|
|
2189
|
+
const apiPath = notebookGraphId != null
|
|
2190
|
+
? `/me/onenote/notebooks/${notebookGraphId}/sections`
|
|
2191
|
+
: '/me/onenote/sections';
|
|
2192
|
+
const response = await client.api(apiPath).get();
|
|
2193
|
+
return response.value;
|
|
2194
|
+
}
|
|
2195
|
+
/**
|
|
2196
|
+
* Lists OneNote pages, optionally scoped to a section.
|
|
2197
|
+
*/
|
|
2198
|
+
async listNotePages(sectionGraphId) {
|
|
2199
|
+
const client = await this.getClient();
|
|
2200
|
+
if (sectionGraphId != null) {
|
|
2201
|
+
const response = await client.api(`/me/onenote/sections/${sectionGraphId}/pages`).get();
|
|
2202
|
+
return response.value;
|
|
2203
|
+
}
|
|
2204
|
+
const response = await client
|
|
2205
|
+
.api('/me/onenote/pages')
|
|
2206
|
+
.top(50)
|
|
2207
|
+
.orderby('lastModifiedDateTime desc')
|
|
2208
|
+
.get();
|
|
2209
|
+
return response.value;
|
|
2210
|
+
}
|
|
2211
|
+
/**
|
|
2212
|
+
* Gets a OneNote page's metadata.
|
|
2213
|
+
*/
|
|
2214
|
+
async getNotePage(pageGraphId) {
|
|
2215
|
+
const client = await this.getClient();
|
|
2216
|
+
return await client.api(`/me/onenote/pages/${pageGraphId}`).get();
|
|
2217
|
+
}
|
|
2218
|
+
/**
|
|
2219
|
+
* Gets a OneNote page's HTML content.
|
|
2220
|
+
*/
|
|
2221
|
+
async getNotePageContent(pageGraphId) {
|
|
2222
|
+
const client = await this.getClient();
|
|
2223
|
+
return await client
|
|
2224
|
+
.api(`/me/onenote/pages/${pageGraphId}/content`)
|
|
2225
|
+
.responseType(ResponseType.TEXT)
|
|
2226
|
+
.get();
|
|
2227
|
+
}
|
|
2228
|
+
/**
|
|
2229
|
+
* Searches OneNote pages by keyword.
|
|
2230
|
+
*/
|
|
2231
|
+
async searchNotePages(query) {
|
|
2232
|
+
const client = await this.getClient();
|
|
2233
|
+
// Strip embedded double-quotes so they can't unbalance the quoted $search
|
|
2234
|
+
// expression (`$search="..."`) and 400 the request.
|
|
2235
|
+
const safeQuery = query.replace(/"/g, '');
|
|
2236
|
+
const response = await client
|
|
2237
|
+
.api('/me/onenote/pages')
|
|
2238
|
+
.search(`"${safeQuery}"`)
|
|
2239
|
+
.get();
|
|
2240
|
+
return response.value;
|
|
2241
|
+
}
|
|
2242
|
+
/**
|
|
2243
|
+
* Creates a new OneNote page in a section from raw HTML.
|
|
2244
|
+
*/
|
|
2245
|
+
async createNotePage(sectionGraphId, html) {
|
|
2246
|
+
const client = await this.getClient();
|
|
2247
|
+
return await client
|
|
2248
|
+
.api(`/me/onenote/sections/${sectionGraphId}/pages`)
|
|
2249
|
+
.header('Content-Type', 'text/html')
|
|
2250
|
+
.post(html);
|
|
1763
2251
|
}
|
|
1764
2252
|
}
|
|
1765
2253
|
/**
|