@memberjunction/communication-ms-graph 5.48.0 → 5.50.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/dist/MSGraphProvider.d.ts +114 -1
- package/dist/MSGraphProvider.d.ts.map +1 -1
- package/dist/MSGraphProvider.js +374 -14
- package/dist/MSGraphProvider.js.map +1 -1
- package/dist/__tests__/DryRun.test.d.ts +2 -0
- package/dist/__tests__/DryRun.test.d.ts.map +1 -0
- package/dist/__tests__/DryRun.test.js +136 -0
- package/dist/__tests__/DryRun.test.js.map +1 -0
- package/dist/__tests__/MSGraphProvider.test.js +326 -4
- package/dist/__tests__/MSGraphProvider.test.js.map +1 -1
- package/package.json +9 -9
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseCommunicationProvider, CreateDraftParams, CreateDraftResult, ForwardMessageParams, ForwardMessageResult, GetMessagesParams, GetMessagesResult, GetSingleMessageParams, GetSingleMessageResult, DeleteMessageParams, DeleteMessageResult, MoveMessageParams, MoveMessageResult, ListFoldersParams, ListFoldersResult, MarkAsReadParams, MarkAsReadResult, ArchiveMessageParams, ArchiveMessageResult, SearchMessagesParams, SearchMessagesResult, ListAttachmentsParams, ListAttachmentsResult, DownloadAttachmentParams, DownloadAttachmentResult, ProviderOperation, MessageResult, ProcessedMessage, ProviderCredentialsBase, ReplyToMessageParams, ReplyToMessageResult } from "@memberjunction/communication-types";
|
|
1
|
+
import { BaseCommunicationProvider, CreateDraftParams, CreateDraftResult, ForwardMessageParams, ForwardMessageResult, GetMessagesParams, GetMessagesResult, GetSingleMessageParams, GetSingleMessageResult, DeleteMessageParams, DeleteMessageResult, MoveMessageParams, MoveMessageResult, ListFoldersParams, ListFoldersResult, MarkAsReadParams, MarkAsReadResult, ArchiveMessageParams, ArchiveMessageResult, SearchMessagesParams, SearchMessagesResult, ListAttachmentsParams, ListAttachmentsResult, DownloadAttachmentParams, DownloadAttachmentResult, ProviderOperation, MessageResult, ProcessedMessage, ProviderCredentialsBase, ReplyToMessageParams, ReplyToMessageResult, CreateSubscriptionParams, RenewSubscriptionParams, DeleteSubscriptionParams, SubscriptionResult, SubscriptionCapabilities, WebhookNotificationInput, ParseNotificationResult, BaseMessageResult } from "@memberjunction/communication-types";
|
|
2
2
|
import { Client } from '@microsoft/microsoft-graph-client';
|
|
3
3
|
import { Message } from "@microsoft/microsoft-graph-types";
|
|
4
4
|
/**
|
|
@@ -99,6 +99,11 @@ export declare class MSGraphProvider extends BaseCommunicationProvider {
|
|
|
99
99
|
* Gets the API URI for Graph API calls.
|
|
100
100
|
*/
|
|
101
101
|
private getApiUri;
|
|
102
|
+
/**
|
|
103
|
+
* Flattens a Graph recipient collection (toRecipients/ccRecipients/bccRecipients)
|
|
104
|
+
* into bare email addresses, dropping entries with no resolvable address.
|
|
105
|
+
*/
|
|
106
|
+
private extractRecipientAddresses;
|
|
102
107
|
/**
|
|
103
108
|
* Sends a single email message via MS Graph.
|
|
104
109
|
*
|
|
@@ -208,6 +213,77 @@ export declare class MSGraphProvider extends BaseCommunicationProvider {
|
|
|
208
213
|
* MS Graph supports all mailbox operations.
|
|
209
214
|
*/
|
|
210
215
|
getSupportedOperations(): ProviderOperation[];
|
|
216
|
+
/**
|
|
217
|
+
* Builds the base URL for Graph subscription resources. Subscriptions live at the
|
|
218
|
+
* Graph service root (`/v1.0/subscriptions`), NOT under the users-rooted
|
|
219
|
+
* {@link getApiUri}. Built from `AZURE_GRAPH_ENDPOINT` so sovereign-cloud overrides
|
|
220
|
+
* are honored.
|
|
221
|
+
*/
|
|
222
|
+
private getSubscriptionsUri;
|
|
223
|
+
/**
|
|
224
|
+
* Resolves the Graph `resource` folder segment for a subscription. Returns the
|
|
225
|
+
* segment to embed in `mailFolders('<segment>')`, or `null` when a custom folder name
|
|
226
|
+
* could not be resolved to an ID.
|
|
227
|
+
*
|
|
228
|
+
* Hot path first: an explicit `folderId` is used verbatim, and a well-known folder
|
|
229
|
+
* name is passed through - both with zero extra Graph calls. Only a custom display
|
|
230
|
+
* name incurs a resolution lookup.
|
|
231
|
+
*/
|
|
232
|
+
private resolveSubscriptionFolderSegment;
|
|
233
|
+
/**
|
|
234
|
+
* Creates a Microsoft Graph change-notification subscription for messages in a
|
|
235
|
+
* mailbox folder. Graph validates the notification endpoint synchronously during
|
|
236
|
+
* this call (it must echo the validation token), so an unreachable/incorrect endpoint
|
|
237
|
+
* surfaces here as a Graph error.
|
|
238
|
+
*
|
|
239
|
+
* @requires MS Graph Scope: Mail.Read (Application)
|
|
240
|
+
* @param params - What to watch, where to notify, and the clientState secret
|
|
241
|
+
* @param credentials - Optional credentials override for this request
|
|
242
|
+
* @returns Promise<SubscriptionResult> - Subscription ID and expiration on success
|
|
243
|
+
*/
|
|
244
|
+
CreateSubscription(params: CreateSubscriptionParams, credentials?: MSGraphCredentials): Promise<SubscriptionResult>;
|
|
245
|
+
/**
|
|
246
|
+
* Renews an existing Graph subscription before it expires. The caller MUST pass
|
|
247
|
+
* credentials for the same app registration that created the subscription - Graph
|
|
248
|
+
* subscriptions are visible only to their creator, so a credential mismatch surfaces
|
|
249
|
+
* as a 404.
|
|
250
|
+
*
|
|
251
|
+
* @requires MS Graph Scope: Mail.Read (Application)
|
|
252
|
+
* @param params - The subscription ID and requested new expiration
|
|
253
|
+
* @param credentials - Optional credentials override for this request
|
|
254
|
+
* @returns Promise<SubscriptionResult> - The renewed expiration on success
|
|
255
|
+
*/
|
|
256
|
+
RenewSubscription(params: RenewSubscriptionParams, credentials?: MSGraphCredentials): Promise<SubscriptionResult>;
|
|
257
|
+
/**
|
|
258
|
+
* Deletes an existing Graph subscription. Idempotent from the consumer's perspective:
|
|
259
|
+
* a 404 (already gone) is treated as success. The caller MUST pass credentials for
|
|
260
|
+
* the same app registration that created the subscription.
|
|
261
|
+
*
|
|
262
|
+
* @requires MS Graph Scope: Mail.Read (Application)
|
|
263
|
+
* @param params - The subscription ID to delete
|
|
264
|
+
* @param credentials - Optional credentials override for this request
|
|
265
|
+
* @returns Promise<BaseMessageResult> - Result of the delete operation
|
|
266
|
+
*/
|
|
267
|
+
DeleteSubscription(params: DeleteSubscriptionParams, credentials?: MSGraphCredentials): Promise<BaseMessageResult>;
|
|
268
|
+
/**
|
|
269
|
+
* Parses and validates an inbound Graph change notification. Pure: no Graph client,
|
|
270
|
+
* no network. Safe on hostile/garbage input - never throws; returns `Success: false`
|
|
271
|
+
* with a 400 suggested status on malformed payloads.
|
|
272
|
+
*
|
|
273
|
+
* Graph has no cryptographic signature scheme, so `SignatureValid` is left undefined;
|
|
274
|
+
* the consumer authenticates each notification by comparing its `ClientState` against
|
|
275
|
+
* the secret stored alongside the subscription.
|
|
276
|
+
*
|
|
277
|
+
* @param input - Transport-neutral capture of the inbound webhook request
|
|
278
|
+
* @returns Promise<ParseNotificationResult> - Handshake or normalized notifications
|
|
279
|
+
*/
|
|
280
|
+
ParseNotification(input: WebhookNotificationInput, _credentials?: MSGraphCredentials): Promise<ParseNotificationResult>;
|
|
281
|
+
/**
|
|
282
|
+
* Returns MS Graph's subscription capabilities. Graph mail subscriptions last at most
|
|
283
|
+
* 4230 minutes (~3 days), support all three change types, and require synchronous
|
|
284
|
+
* endpoint validation at create time.
|
|
285
|
+
*/
|
|
286
|
+
GetSubscriptionCapabilities(): SubscriptionCapabilities;
|
|
211
287
|
/**
|
|
212
288
|
* Gets a single message by ID from MS Graph.
|
|
213
289
|
*
|
|
@@ -285,5 +361,42 @@ export declare class MSGraphProvider extends BaseCommunicationProvider {
|
|
|
285
361
|
* Maps folder display name to system folder type.
|
|
286
362
|
*/
|
|
287
363
|
private mapSystemFolderType;
|
|
364
|
+
/**
|
|
365
|
+
* Resolves a folder name to its Microsoft Graph well-known name when it is one,
|
|
366
|
+
* matching case-insensitively against both friendly aliases and the canonical Graph
|
|
367
|
+
* names. Graph accepts a well-known name directly in a resource path (no ID-resolution
|
|
368
|
+
* call needed). Returns undefined for a custom display name, which then requires a
|
|
369
|
+
* lookup. Shared by findSystemFolder and the CreateSubscription resource builder.
|
|
370
|
+
*/
|
|
371
|
+
private resolveWellKnownFolder;
|
|
372
|
+
/**
|
|
373
|
+
* Extracts an HTTP status code from a thrown Microsoft Graph client error. The
|
|
374
|
+
* middleware client throws a GraphError-shaped object carrying a numeric statusCode;
|
|
375
|
+
* this narrows an untyped catch value to that code (or undefined when absent).
|
|
376
|
+
*/
|
|
377
|
+
private getGraphStatusCode;
|
|
378
|
+
/**
|
|
379
|
+
* Parses the mailbox identifier (user ID / email) out of a Graph change-notification
|
|
380
|
+
* resource string. Tolerant of both Users/{id}/Messages/{msgId} and
|
|
381
|
+
* users/{id}/mailFolders('...')/messages/{msgId} shapes, case-insensitively. Returns
|
|
382
|
+
* undefined when no pattern matches - the identifier is a routing convenience only.
|
|
383
|
+
*/
|
|
384
|
+
private parseIdentifierFromResource;
|
|
385
|
+
/**
|
|
386
|
+
* Maps a raw Graph changeType value onto the normalized SubscriptionChangeType union,
|
|
387
|
+
* returning undefined for anything unrecognized.
|
|
388
|
+
*/
|
|
389
|
+
private mapChangeType;
|
|
390
|
+
/**
|
|
391
|
+
* Maps a raw Graph lifecycleEvent value onto the normalized lifecycle-event union,
|
|
392
|
+
* returning undefined for anything unrecognized.
|
|
393
|
+
*/
|
|
394
|
+
private mapLifecycleEvent;
|
|
395
|
+
/**
|
|
396
|
+
* Clamps a requested subscription expiration to Graph's maximum lifetime, returning an
|
|
397
|
+
* ISO-8601 UTC string. When no expiration is requested (or it exceeds the max), the
|
|
398
|
+
* maximum-allowed expiration from now is used.
|
|
399
|
+
*/
|
|
400
|
+
private clampExpiration;
|
|
288
401
|
}
|
|
289
402
|
//# sourceMappingURL=MSGraphProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MSGraphProvider.d.ts","sourceRoot":"","sources":["../src/MSGraphProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,wBAAwB,EAGxB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,EACpB,oBAAoB,
|
|
1
|
+
{"version":3,"file":"MSGraphProvider.d.ts","sourceRoot":"","sources":["../src/MSGraphProvider.ts"],"names":[],"mappings":"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"}
|
package/dist/MSGraphProvider.js
CHANGED
|
@@ -16,6 +16,11 @@ import { LogError, LogStatus } from "@memberjunction/core";
|
|
|
16
16
|
import { compile } from 'html-to-text';
|
|
17
17
|
import * as Auth from "./auth.js";
|
|
18
18
|
import * as Config from "./config.js";
|
|
19
|
+
/**
|
|
20
|
+
* The maximum lifetime, in minutes, that Microsoft Graph allows for a mail-resource
|
|
21
|
+
* change-notification subscription (~3 days). Requested expirations are clamped to this.
|
|
22
|
+
*/
|
|
23
|
+
const MSGRAPH_MAX_SUBSCRIPTION_MINUTES = 4230;
|
|
19
24
|
/**
|
|
20
25
|
* Implementation of the MS Graph provider for sending and receiving messages.
|
|
21
26
|
*
|
|
@@ -106,6 +111,13 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
|
|
|
106
111
|
getApiUri() {
|
|
107
112
|
return Auth.ApiConfig.uri;
|
|
108
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Flattens a Graph recipient collection (toRecipients/ccRecipients/bccRecipients)
|
|
116
|
+
* into bare email addresses, dropping entries with no resolvable address.
|
|
117
|
+
*/
|
|
118
|
+
extractRecipientAddresses(recipients) {
|
|
119
|
+
return recipients?.map((recipient) => recipient.emailAddress?.address || '').filter((address) => address.length > 0) || [];
|
|
120
|
+
}
|
|
109
121
|
/**
|
|
110
122
|
* Sends a single email message via MS Graph.
|
|
111
123
|
*
|
|
@@ -166,6 +178,18 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
|
|
|
166
178
|
}
|
|
167
179
|
// Use email address directly in API path instead of looking up user ID
|
|
168
180
|
const sendMessagePath = `${this.getApiUri()}/${encodeURIComponent(senderEmail)}/sendMail`;
|
|
181
|
+
// DRY RUN: full pipeline ran (credential resolution/validation, sender selection,
|
|
182
|
+
// complete Graph sendMail payload + headers + API path construction above) — stop at
|
|
183
|
+
// the transport boundary, never calling Microsoft Graph.
|
|
184
|
+
if (message.DryRun) {
|
|
185
|
+
LogStatus(`[DryRun] Microsoft Graph: sendMail payload constructed for ${message.To} — external send skipped`);
|
|
186
|
+
return {
|
|
187
|
+
Message: message,
|
|
188
|
+
Success: true,
|
|
189
|
+
Error: '',
|
|
190
|
+
DryRun: true
|
|
191
|
+
};
|
|
192
|
+
}
|
|
169
193
|
await client.api(sendMessagePath).post(sendMail);
|
|
170
194
|
return {
|
|
171
195
|
Message: message,
|
|
@@ -280,6 +304,8 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
|
|
|
280
304
|
return {
|
|
281
305
|
From: msgTyped.from?.emailAddress?.address || '',
|
|
282
306
|
To: primaryToRecipient,
|
|
307
|
+
ToRecipients: this.extractRecipientAddresses(msgTyped.toRecipients),
|
|
308
|
+
CCRecipients: this.extractRecipientAddresses(msgTyped.ccRecipients),
|
|
283
309
|
ReplyTo: replyTo,
|
|
284
310
|
Subject: msgTyped.subject || '',
|
|
285
311
|
Body: contextData?.ReturnAsPlainText || contextData?.ReturnAsPlainTex ? this.HTMLConverter(msgTyped.body?.content || '') : msgTyped.body?.content || '',
|
|
@@ -568,9 +594,274 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
|
|
|
568
594
|
'ArchiveMessage',
|
|
569
595
|
'SearchMessages',
|
|
570
596
|
'ListAttachments',
|
|
571
|
-
'DownloadAttachment'
|
|
597
|
+
'DownloadAttachment',
|
|
598
|
+
'CreateSubscription',
|
|
599
|
+
'RenewSubscription',
|
|
600
|
+
'DeleteSubscription',
|
|
601
|
+
'ParseNotification'
|
|
572
602
|
];
|
|
573
603
|
}
|
|
604
|
+
// ========================================================================
|
|
605
|
+
// PUSH-NOTIFICATION SUBSCRIPTIONS
|
|
606
|
+
// Graph change-notification subscriptions. CRUD rides the existing authenticated
|
|
607
|
+
// client; ParseNotification is pure (no client, no network). The provider is
|
|
608
|
+
// stateless - the consumer persists subscription IDs/expirations/secrets.
|
|
609
|
+
// ========================================================================
|
|
610
|
+
/**
|
|
611
|
+
* Builds the base URL for Graph subscription resources. Subscriptions live at the
|
|
612
|
+
* Graph service root (`/v1.0/subscriptions`), NOT under the users-rooted
|
|
613
|
+
* {@link getApiUri}. Built from `AZURE_GRAPH_ENDPOINT` so sovereign-cloud overrides
|
|
614
|
+
* are honored.
|
|
615
|
+
*/
|
|
616
|
+
getSubscriptionsUri() {
|
|
617
|
+
return `${Config.AZURE_GRAPH_ENDPOINT}/v1.0/subscriptions`;
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Resolves the Graph `resource` folder segment for a subscription. Returns the
|
|
621
|
+
* segment to embed in `mailFolders('<segment>')`, or `null` when a custom folder name
|
|
622
|
+
* could not be resolved to an ID.
|
|
623
|
+
*
|
|
624
|
+
* Hot path first: an explicit `folderId` is used verbatim, and a well-known folder
|
|
625
|
+
* name is passed through - both with zero extra Graph calls. Only a custom display
|
|
626
|
+
* name incurs a resolution lookup.
|
|
627
|
+
*/
|
|
628
|
+
async resolveSubscriptionFolderSegment(client, mailboxId, context) {
|
|
629
|
+
if (context?.folderId) {
|
|
630
|
+
return context.folderId;
|
|
631
|
+
}
|
|
632
|
+
const folderName = context?.folderName || 'inbox';
|
|
633
|
+
const wellKnown = this.resolveWellKnownFolder(folderName);
|
|
634
|
+
if (wellKnown) {
|
|
635
|
+
return wellKnown;
|
|
636
|
+
}
|
|
637
|
+
// Custom display name - resolve to an ID rather than subscribing to a guessed path.
|
|
638
|
+
return await this.findSystemFolder(client, mailboxId, folderName);
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Creates a Microsoft Graph change-notification subscription for messages in a
|
|
642
|
+
* mailbox folder. Graph validates the notification endpoint synchronously during
|
|
643
|
+
* this call (it must echo the validation token), so an unreachable/incorrect endpoint
|
|
644
|
+
* surfaces here as a Graph error.
|
|
645
|
+
*
|
|
646
|
+
* @requires MS Graph Scope: Mail.Read (Application)
|
|
647
|
+
* @param params - What to watch, where to notify, and the clientState secret
|
|
648
|
+
* @param credentials - Optional credentials override for this request
|
|
649
|
+
* @returns Promise<SubscriptionResult> - Subscription ID and expiration on success
|
|
650
|
+
*/
|
|
651
|
+
async CreateSubscription(params, credentials) {
|
|
652
|
+
// Fail-fast input validation (before any Graph call).
|
|
653
|
+
if (!params.ClientState) {
|
|
654
|
+
return { Success: false, ErrorMessage: 'CreateSubscription requires a non-empty ClientState' };
|
|
655
|
+
}
|
|
656
|
+
if (params.ClientState.length > 128) {
|
|
657
|
+
return { Success: false, ErrorMessage: `ClientState exceeds Microsoft Graph's 128-character limit (got ${params.ClientState.length})` };
|
|
658
|
+
}
|
|
659
|
+
if (!params.NotificationUrl || !params.NotificationUrl.toLowerCase().startsWith('https://')) {
|
|
660
|
+
return { Success: false, ErrorMessage: 'NotificationUrl must be an https:// URL' };
|
|
661
|
+
}
|
|
662
|
+
if (params.LifecycleNotificationUrl && !params.LifecycleNotificationUrl.toLowerCase().startsWith('https://')) {
|
|
663
|
+
return { Success: false, ErrorMessage: 'LifecycleNotificationUrl must be an https:// URL' };
|
|
664
|
+
}
|
|
665
|
+
if (!params.ChangeTypes || params.ChangeTypes.length === 0) {
|
|
666
|
+
return { Success: false, ErrorMessage: 'CreateSubscription requires at least one ChangeType' };
|
|
667
|
+
}
|
|
668
|
+
try {
|
|
669
|
+
const creds = this.resolveCredentials(credentials);
|
|
670
|
+
const client = this.getGraphClient(creds);
|
|
671
|
+
const mailboxId = params.Identifier || creds.accountEmail;
|
|
672
|
+
const context = params.ContextData;
|
|
673
|
+
const folderSegment = await this.resolveSubscriptionFolderSegment(client, mailboxId, context);
|
|
674
|
+
if (!folderSegment) {
|
|
675
|
+
return {
|
|
676
|
+
Success: false,
|
|
677
|
+
ErrorMessage: `Could not resolve folder '${context?.folderName}' for mailbox '${mailboxId}'`
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
const body = {
|
|
681
|
+
changeType: params.ChangeTypes.join(','),
|
|
682
|
+
notificationUrl: params.NotificationUrl,
|
|
683
|
+
resource: `/users/${mailboxId}/mailFolders('${folderSegment}')/messages`,
|
|
684
|
+
clientState: params.ClientState,
|
|
685
|
+
expirationDateTime: this.clampExpiration(params.RequestedExpiration)
|
|
686
|
+
};
|
|
687
|
+
if (params.LifecycleNotificationUrl) {
|
|
688
|
+
body.lifecycleNotificationUrl = params.LifecycleNotificationUrl;
|
|
689
|
+
}
|
|
690
|
+
const resp = await client.api(this.getSubscriptionsUri()).post(body);
|
|
691
|
+
return {
|
|
692
|
+
Success: true,
|
|
693
|
+
SubscriptionID: resp?.id,
|
|
694
|
+
ExpiresAt: resp?.expirationDateTime ? new Date(resp.expirationDateTime) : undefined,
|
|
695
|
+
Result: resp
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
catch (ex) {
|
|
699
|
+
LogError('Error creating subscription via MS Graph', undefined, ex);
|
|
700
|
+
return {
|
|
701
|
+
Success: false,
|
|
702
|
+
ErrorMessage: `Error creating subscription: ${ex instanceof Error ? ex.message : String(ex)}`
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Renews an existing Graph subscription before it expires. The caller MUST pass
|
|
708
|
+
* credentials for the same app registration that created the subscription - Graph
|
|
709
|
+
* subscriptions are visible only to their creator, so a credential mismatch surfaces
|
|
710
|
+
* as a 404.
|
|
711
|
+
*
|
|
712
|
+
* @requires MS Graph Scope: Mail.Read (Application)
|
|
713
|
+
* @param params - The subscription ID and requested new expiration
|
|
714
|
+
* @param credentials - Optional credentials override for this request
|
|
715
|
+
* @returns Promise<SubscriptionResult> - The renewed expiration on success
|
|
716
|
+
*/
|
|
717
|
+
async RenewSubscription(params, credentials) {
|
|
718
|
+
try {
|
|
719
|
+
const creds = this.resolveCredentials(credentials);
|
|
720
|
+
const client = this.getGraphClient(creds);
|
|
721
|
+
const resp = await client.api(`${this.getSubscriptionsUri()}/${params.SubscriptionID}`).patch({
|
|
722
|
+
expirationDateTime: this.clampExpiration(params.RequestedExpiration)
|
|
723
|
+
});
|
|
724
|
+
return {
|
|
725
|
+
Success: true,
|
|
726
|
+
SubscriptionID: resp?.id ?? params.SubscriptionID,
|
|
727
|
+
ExpiresAt: resp?.expirationDateTime ? new Date(resp.expirationDateTime) : undefined,
|
|
728
|
+
Result: resp
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
catch (ex) {
|
|
732
|
+
if (this.getGraphStatusCode(ex) === 404) {
|
|
733
|
+
LogError(`Subscription '${params.SubscriptionID}' not found during renewal - expired or created under different credentials`);
|
|
734
|
+
return {
|
|
735
|
+
Success: false,
|
|
736
|
+
ErrorMessage: 'subscription not found - expired or created under different credentials'
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
LogError('Error renewing subscription via MS Graph', undefined, ex);
|
|
740
|
+
return {
|
|
741
|
+
Success: false,
|
|
742
|
+
ErrorMessage: `Error renewing subscription: ${ex instanceof Error ? ex.message : String(ex)}`
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Deletes an existing Graph subscription. Idempotent from the consumer's perspective:
|
|
748
|
+
* a 404 (already gone) is treated as success. The caller MUST pass credentials for
|
|
749
|
+
* the same app registration that created the subscription.
|
|
750
|
+
*
|
|
751
|
+
* @requires MS Graph Scope: Mail.Read (Application)
|
|
752
|
+
* @param params - The subscription ID to delete
|
|
753
|
+
* @param credentials - Optional credentials override for this request
|
|
754
|
+
* @returns Promise<BaseMessageResult> - Result of the delete operation
|
|
755
|
+
*/
|
|
756
|
+
async DeleteSubscription(params, credentials) {
|
|
757
|
+
try {
|
|
758
|
+
const creds = this.resolveCredentials(credentials);
|
|
759
|
+
const client = this.getGraphClient(creds);
|
|
760
|
+
await client.api(`${this.getSubscriptionsUri()}/${params.SubscriptionID}`).delete();
|
|
761
|
+
return { Success: true };
|
|
762
|
+
}
|
|
763
|
+
catch (ex) {
|
|
764
|
+
if (this.getGraphStatusCode(ex) === 404) {
|
|
765
|
+
// Already gone - deletion is idempotent from the consumer's perspective.
|
|
766
|
+
return { Success: true };
|
|
767
|
+
}
|
|
768
|
+
LogError('Error deleting subscription via MS Graph', undefined, ex);
|
|
769
|
+
return {
|
|
770
|
+
Success: false,
|
|
771
|
+
ErrorMessage: `Error deleting subscription: ${ex instanceof Error ? ex.message : String(ex)}`
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Parses and validates an inbound Graph change notification. Pure: no Graph client,
|
|
777
|
+
* no network. Safe on hostile/garbage input - never throws; returns `Success: false`
|
|
778
|
+
* with a 400 suggested status on malformed payloads.
|
|
779
|
+
*
|
|
780
|
+
* Graph has no cryptographic signature scheme, so `SignatureValid` is left undefined;
|
|
781
|
+
* the consumer authenticates each notification by comparing its `ClientState` against
|
|
782
|
+
* the secret stored alongside the subscription.
|
|
783
|
+
*
|
|
784
|
+
* @param input - Transport-neutral capture of the inbound webhook request
|
|
785
|
+
* @returns Promise<ParseNotificationResult> - Handshake or normalized notifications
|
|
786
|
+
*/
|
|
787
|
+
async ParseNotification(input, _credentials) {
|
|
788
|
+
// 1. Endpoint-validation handshake. QueryParams are already framework-decoded per
|
|
789
|
+
// the WebhookNotificationInput contract - echo the token verbatim.
|
|
790
|
+
const validationToken = input?.QueryParams?.['validationToken'];
|
|
791
|
+
if (validationToken !== undefined) {
|
|
792
|
+
return {
|
|
793
|
+
Success: true,
|
|
794
|
+
Handshake: {
|
|
795
|
+
ResponseStatus: 200,
|
|
796
|
+
ResponseBody: validationToken,
|
|
797
|
+
ResponseContentType: 'text/plain'
|
|
798
|
+
},
|
|
799
|
+
Notifications: [],
|
|
800
|
+
SuggestedResponseStatus: 200
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
// 2. Parse the notification batch. Never throw on bad input.
|
|
804
|
+
let payload;
|
|
805
|
+
try {
|
|
806
|
+
payload = JSON.parse(input?.RawBody ?? '');
|
|
807
|
+
}
|
|
808
|
+
catch {
|
|
809
|
+
return {
|
|
810
|
+
Success: false,
|
|
811
|
+
ErrorMessage: 'Malformed notification body (invalid JSON)',
|
|
812
|
+
Notifications: [],
|
|
813
|
+
SuggestedResponseStatus: 400
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
const items = payload?.value;
|
|
817
|
+
if (!Array.isArray(items)) {
|
|
818
|
+
return {
|
|
819
|
+
Success: false,
|
|
820
|
+
ErrorMessage: 'Notification body missing value[] array',
|
|
821
|
+
Notifications: [],
|
|
822
|
+
SuggestedResponseStatus: 400
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
const notifications = items.map((raw) => {
|
|
826
|
+
const item = (raw ?? {});
|
|
827
|
+
const lifecycleEvent = item['lifecycleEvent'];
|
|
828
|
+
const resourceData = item['resourceData'];
|
|
829
|
+
const messageId = typeof resourceData?.id === 'string' ? resourceData.id : undefined;
|
|
830
|
+
return {
|
|
831
|
+
Kind: lifecycleEvent != null ? 'lifecycle' : 'message',
|
|
832
|
+
SubscriptionID: typeof item['subscriptionId'] === 'string' ? item['subscriptionId'] : undefined,
|
|
833
|
+
ClientState: typeof item['clientState'] === 'string' ? item['clientState'] : undefined,
|
|
834
|
+
Identifier: this.parseIdentifierFromResource(item['resource']),
|
|
835
|
+
ChangeType: this.mapChangeType(item['changeType']),
|
|
836
|
+
MessageIDs: messageId ? [messageId] : [],
|
|
837
|
+
LifecycleEvent: this.mapLifecycleEvent(lifecycleEvent),
|
|
838
|
+
RawData: raw
|
|
839
|
+
};
|
|
840
|
+
});
|
|
841
|
+
return {
|
|
842
|
+
Success: true,
|
|
843
|
+
SignatureValid: undefined,
|
|
844
|
+
Notifications: notifications,
|
|
845
|
+
SuggestedResponseStatus: 202
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Returns MS Graph's subscription capabilities. Graph mail subscriptions last at most
|
|
850
|
+
* 4230 minutes (~3 days), support all three change types, and require synchronous
|
|
851
|
+
* endpoint validation at create time.
|
|
852
|
+
*/
|
|
853
|
+
GetSubscriptionCapabilities() {
|
|
854
|
+
return {
|
|
855
|
+
MaxLifetimeMinutes: MSGRAPH_MAX_SUBSCRIPTION_MINUTES,
|
|
856
|
+
SupportedChangeTypes: ['created', 'updated', 'deleted'],
|
|
857
|
+
RequiresEndpointValidation: true,
|
|
858
|
+
// Graph is subscription-managed (CreateSubscription/RenewSubscription/DeleteSubscription)
|
|
859
|
+
// and HINT-mode: notifications carry resourceData IDs, the consumer re-fetches via
|
|
860
|
+
// GetMessages/GetSingleMessage — the payload is never delivered inline.
|
|
861
|
+
SupportsSubscriptionManagement: true,
|
|
862
|
+
DeliversPayloadInline: false
|
|
863
|
+
};
|
|
864
|
+
}
|
|
574
865
|
/**
|
|
575
866
|
* Gets a single message by ID from MS Graph.
|
|
576
867
|
*
|
|
@@ -597,6 +888,8 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
|
|
|
597
888
|
Message: {
|
|
598
889
|
From: msgTyped.from?.emailAddress?.address || '',
|
|
599
890
|
To: replyTo.length > 0 ? replyTo[0] : '',
|
|
891
|
+
ToRecipients: this.extractRecipientAddresses(msgTyped.toRecipients),
|
|
892
|
+
CCRecipients: this.extractRecipientAddresses(msgTyped.ccRecipients),
|
|
600
893
|
ReplyTo: replyTo,
|
|
601
894
|
Subject: msgTyped.subject || '',
|
|
602
895
|
Body: msgTyped.body?.content || '',
|
|
@@ -850,6 +1143,8 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
|
|
|
850
1143
|
const messages = response.value.map((msg) => ({
|
|
851
1144
|
From: msg.from?.emailAddress?.address || '',
|
|
852
1145
|
To: msg.toRecipients?.[0]?.emailAddress?.address || '',
|
|
1146
|
+
ToRecipients: this.extractRecipientAddresses(msg.toRecipients),
|
|
1147
|
+
CCRecipients: this.extractRecipientAddresses(msg.ccRecipients),
|
|
853
1148
|
Subject: msg.subject || '',
|
|
854
1149
|
Body: msg.bodyPreview || '',
|
|
855
1150
|
ExternalSystemRecordID: msg.id || '',
|
|
@@ -962,19 +1257,8 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
|
|
|
962
1257
|
*/
|
|
963
1258
|
async findSystemFolder(client, emailAddress, folderName) {
|
|
964
1259
|
try {
|
|
965
|
-
// MS Graph well-known folder names
|
|
966
|
-
const
|
|
967
|
-
'inbox': 'inbox',
|
|
968
|
-
'sent': 'sentitems',
|
|
969
|
-
'drafts': 'drafts',
|
|
970
|
-
'deleteditems': 'deleteditems',
|
|
971
|
-
'trash': 'deleteditems',
|
|
972
|
-
'junkemail': 'junkemail',
|
|
973
|
-
'spam': 'junkemail',
|
|
974
|
-
'archive': 'archive'
|
|
975
|
-
};
|
|
976
|
-
const normalizedName = folderName.toLowerCase();
|
|
977
|
-
const graphFolderName = wellKnownNames[normalizedName] || normalizedName;
|
|
1260
|
+
// MS Graph well-known folder names (shared with the subscription resolver)
|
|
1261
|
+
const graphFolderName = this.resolveWellKnownFolder(folderName) || folderName.toLowerCase();
|
|
978
1262
|
// Try well-known folder endpoint first - use email address directly
|
|
979
1263
|
const folderPath = `${this.getApiUri()}/${encodeURIComponent(emailAddress)}/mailFolders/${graphFolderName}`;
|
|
980
1264
|
const folder = await client.api(folderPath).get();
|
|
@@ -1042,6 +1326,82 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
|
|
|
1042
1326
|
};
|
|
1043
1327
|
return mapping[displayName.toLowerCase()] || 'other';
|
|
1044
1328
|
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Resolves a folder name to its Microsoft Graph well-known name when it is one,
|
|
1331
|
+
* matching case-insensitively against both friendly aliases and the canonical Graph
|
|
1332
|
+
* names. Graph accepts a well-known name directly in a resource path (no ID-resolution
|
|
1333
|
+
* call needed). Returns undefined for a custom display name, which then requires a
|
|
1334
|
+
* lookup. Shared by findSystemFolder and the CreateSubscription resource builder.
|
|
1335
|
+
*/
|
|
1336
|
+
resolveWellKnownFolder(folderName) {
|
|
1337
|
+
const wellKnownNames = {
|
|
1338
|
+
'inbox': 'inbox',
|
|
1339
|
+
'sent': 'sentitems',
|
|
1340
|
+
'sentitems': 'sentitems',
|
|
1341
|
+
'drafts': 'drafts',
|
|
1342
|
+
'deleteditems': 'deleteditems',
|
|
1343
|
+
'trash': 'deleteditems',
|
|
1344
|
+
'junkemail': 'junkemail',
|
|
1345
|
+
'spam': 'junkemail',
|
|
1346
|
+
'archive': 'archive'
|
|
1347
|
+
};
|
|
1348
|
+
return wellKnownNames[folderName.toLowerCase()];
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Extracts an HTTP status code from a thrown Microsoft Graph client error. The
|
|
1352
|
+
* middleware client throws a GraphError-shaped object carrying a numeric statusCode;
|
|
1353
|
+
* this narrows an untyped catch value to that code (or undefined when absent).
|
|
1354
|
+
*/
|
|
1355
|
+
getGraphStatusCode(ex) {
|
|
1356
|
+
if (ex && typeof ex === 'object' && 'statusCode' in ex) {
|
|
1357
|
+
const sc = ex.statusCode;
|
|
1358
|
+
return typeof sc === 'number' ? sc : undefined;
|
|
1359
|
+
}
|
|
1360
|
+
return undefined;
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* Parses the mailbox identifier (user ID / email) out of a Graph change-notification
|
|
1364
|
+
* resource string. Tolerant of both Users/{id}/Messages/{msgId} and
|
|
1365
|
+
* users/{id}/mailFolders('...')/messages/{msgId} shapes, case-insensitively. Returns
|
|
1366
|
+
* undefined when no pattern matches - the identifier is a routing convenience only.
|
|
1367
|
+
*/
|
|
1368
|
+
parseIdentifierFromResource(resource) {
|
|
1369
|
+
if (typeof resource !== 'string') {
|
|
1370
|
+
return undefined;
|
|
1371
|
+
}
|
|
1372
|
+
const match = resource.match(/users\/([^/]+)/i);
|
|
1373
|
+
return match ? match[1] : undefined;
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* Maps a raw Graph changeType value onto the normalized SubscriptionChangeType union,
|
|
1377
|
+
* returning undefined for anything unrecognized.
|
|
1378
|
+
*/
|
|
1379
|
+
mapChangeType(raw) {
|
|
1380
|
+
if (raw === 'created' || raw === 'updated' || raw === 'deleted') {
|
|
1381
|
+
return raw;
|
|
1382
|
+
}
|
|
1383
|
+
return undefined;
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Maps a raw Graph lifecycleEvent value onto the normalized lifecycle-event union,
|
|
1387
|
+
* returning undefined for anything unrecognized.
|
|
1388
|
+
*/
|
|
1389
|
+
mapLifecycleEvent(raw) {
|
|
1390
|
+
if (raw === 'subscriptionRemoved' || raw === 'missed' || raw === 'reauthorizationRequired') {
|
|
1391
|
+
return raw;
|
|
1392
|
+
}
|
|
1393
|
+
return undefined;
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Clamps a requested subscription expiration to Graph's maximum lifetime, returning an
|
|
1397
|
+
* ISO-8601 UTC string. When no expiration is requested (or it exceeds the max), the
|
|
1398
|
+
* maximum-allowed expiration from now is used.
|
|
1399
|
+
*/
|
|
1400
|
+
clampExpiration(requested) {
|
|
1401
|
+
const max = new Date(Date.now() + MSGRAPH_MAX_SUBSCRIPTION_MINUTES * 60 * 1000);
|
|
1402
|
+
const effective = requested && requested.getTime() < max.getTime() ? requested : max;
|
|
1403
|
+
return effective.toISOString();
|
|
1404
|
+
}
|
|
1045
1405
|
};
|
|
1046
1406
|
MSGraphProvider = __decorate([
|
|
1047
1407
|
RegisterClass(BaseCommunicationProvider, 'Microsoft Graph'),
|