@checkfirst/nestjs-outlook 0.1.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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +224 -0
  3. package/assets/checkfirst-logo.png +0 -0
  4. package/dist/constants.d.ts +1 -0
  5. package/dist/constants.js +5 -0
  6. package/dist/constants.js.map +1 -0
  7. package/dist/controllers/microsoft-auth.controller.d.ts +7 -0
  8. package/dist/controllers/microsoft-auth.controller.js +101 -0
  9. package/dist/controllers/microsoft-auth.controller.js.map +1 -0
  10. package/dist/controllers/outlook.controller.d.ts +8 -0
  11. package/dist/controllers/outlook.controller.js +117 -0
  12. package/dist/controllers/outlook.controller.js.map +1 -0
  13. package/dist/dto/outlook-webhook-notification.dto.d.ts +19 -0
  14. package/dist/dto/outlook-webhook-notification.dto.js +149 -0
  15. package/dist/dto/outlook-webhook-notification.dto.js.map +1 -0
  16. package/dist/entities/csrf-token.entity.d.ts +7 -0
  17. package/dist/entities/csrf-token.entity.js +48 -0
  18. package/dist/entities/csrf-token.entity.js.map +1 -0
  19. package/dist/entities/outlook-webhook-subscription.entity.d.ts +15 -0
  20. package/dist/entities/outlook-webhook-subscription.entity.js +87 -0
  21. package/dist/entities/outlook-webhook-subscription.entity.js.map +1 -0
  22. package/dist/event-types.enum.d.ts +7 -0
  23. package/dist/event-types.enum.js +12 -0
  24. package/dist/event-types.enum.js.map +1 -0
  25. package/dist/index.d.ts +11 -0
  26. package/dist/index.js +28 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/interfaces/config/outlook-config.interface.d.ts +7 -0
  29. package/dist/interfaces/config/outlook-config.interface.js +3 -0
  30. package/dist/interfaces/config/outlook-config.interface.js.map +1 -0
  31. package/dist/interfaces/microsoft-auth/microsoft-token-api-response.interface.d.ts +9 -0
  32. package/dist/interfaces/microsoft-auth/microsoft-token-api-response.interface.js +3 -0
  33. package/dist/interfaces/microsoft-auth/microsoft-token-api-response.interface.js.map +1 -0
  34. package/dist/interfaces/microsoft-auth/state-object.interface.d.ts +5 -0
  35. package/dist/interfaces/microsoft-auth/state-object.interface.js +3 -0
  36. package/dist/interfaces/microsoft-auth/state-object.interface.js.map +1 -0
  37. package/dist/interfaces/outlook/token-response.interface.d.ts +5 -0
  38. package/dist/interfaces/outlook/token-response.interface.js +3 -0
  39. package/dist/interfaces/outlook/token-response.interface.js.map +1 -0
  40. package/dist/microsoft-outlook.module.d.ts +4 -0
  41. package/dist/microsoft-outlook.module.js +50 -0
  42. package/dist/microsoft-outlook.module.js.map +1 -0
  43. package/dist/migrations/1697025846000-CreateOutlookTables.d.ts +5 -0
  44. package/dist/migrations/1697025846000-CreateOutlookTables.js +40 -0
  45. package/dist/migrations/1697025846000-CreateOutlookTables.js.map +1 -0
  46. package/dist/repositories/microsoft-csrf-token.repository.d.ts +9 -0
  47. package/dist/repositories/microsoft-csrf-token.repository.js +60 -0
  48. package/dist/repositories/microsoft-csrf-token.repository.js.map +1 -0
  49. package/dist/repositories/outlook-webhook-subscription.repository.d.ts +12 -0
  50. package/dist/repositories/outlook-webhook-subscription.repository.js +70 -0
  51. package/dist/repositories/outlook-webhook-subscription.repository.js.map +1 -0
  52. package/dist/services/microsoft-auth.service.d.ts +32 -0
  53. package/dist/services/microsoft-auth.service.js +254 -0
  54. package/dist/services/microsoft-auth.service.js.map +1 -0
  55. package/dist/services/outlook.service.d.ts +28 -0
  56. package/dist/services/outlook.service.js +265 -0
  57. package/dist/services/outlook.service.js.map +1 -0
  58. package/dist/tsconfig.tsbuildinfo +1 -0
  59. package/package.json +103 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 CheckFirst Ltd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,224 @@
1
+ # NestJS Outlook
2
+
3
+ <p align="center">
4
+ <a href="https://checkfirst.ai" target="_blank">
5
+ <img src="https://raw.githubusercontent.com/checkfirst-ltd/nestjs-outlook/main/assets/checkfirst-logo.png" width="400" alt="CheckFirst Logo" />
6
+ </a>
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://www.npmjs.com/package/@checkfirst/nestjs-outlook"><img src="https://img.shields.io/npm/v/@checkfirst/nestjs-outlook.svg" alt="NPM Version" /></a>
11
+ <a href="https://www.npmjs.com/package/@checkfirst/nestjs-outlook"><img src="https://img.shields.io/npm/dm/@checkfirst/nestjs-outlook.svg" alt="NPM Downloads" /></a>
12
+ <a href="https://github.com/checkfirst-ltd/nestjs-outlook/blob/main/LICENSE"><img src="https://img.shields.io/github/license/checkfirst-ltd/nestjs-outlook" alt="License" /></a>
13
+ </p>
14
+
15
+ An opinionated NestJS module for Microsoft Outlook integration that provides easy access to Microsoft Graph API for emails, calendars, and more.
16
+
17
+ ## Features
18
+
19
+ - 🔄 Simplified Microsoft OAuth flow
20
+ - 📅 Calendar events management
21
+ - 🔔 Real-time webhooks for changes
22
+ - 🔐 Secure token storage and refresh
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install @checkfirst/nestjs-outlook
28
+ ```
29
+
30
+ ## Setup
31
+
32
+ ### 1. Database Setup
33
+
34
+ This library requires two database tables in your application's database. Create these tables using a migration:
35
+
36
+ ```typescript
37
+ import { MigrationInterface, QueryRunner } from 'typeorm';
38
+
39
+ export class CreateOutlookTables1697025846000 implements MigrationInterface {
40
+ public async up(queryRunner: QueryRunner): Promise<void> {
41
+ // Create outlook_webhook_subscriptions table
42
+ await queryRunner.query(`
43
+ CREATE TABLE outlook_webhook_subscriptions (
44
+ id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
45
+ subscription_id VARCHAR(255) NOT NULL,
46
+ user_id INTEGER NOT NULL,
47
+ resource VARCHAR(255) NOT NULL,
48
+ change_type VARCHAR(255) NOT NULL,
49
+ client_state VARCHAR(255) NOT NULL,
50
+ notification_url VARCHAR(255) NOT NULL,
51
+ expiration_date_time TIMESTAMP NOT NULL,
52
+ is_active BOOLEAN DEFAULT true,
53
+ access_token TEXT,
54
+ refresh_token TEXT,
55
+ created_at TIMESTAMP DEFAULT NOW() NOT NULL,
56
+ updated_at TIMESTAMP DEFAULT NOW() NOT NULL
57
+ );
58
+ `);
59
+
60
+ // Create microsoft_csrf_tokens table
61
+ await queryRunner.query(`
62
+ CREATE TABLE microsoft_csrf_tokens (
63
+ id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
64
+ token VARCHAR(64) NOT NULL,
65
+ user_id VARCHAR(255) NOT NULL,
66
+ expires TIMESTAMP NOT NULL,
67
+ created_at TIMESTAMP DEFAULT NOW() NOT NULL,
68
+ CONSTRAINT "UQ_microsoft_csrf_tokens_token" UNIQUE (token)
69
+ );
70
+ `);
71
+ }
72
+
73
+ public async down(queryRunner: QueryRunner): Promise<void> {
74
+ await queryRunner.query(`DROP TABLE IF EXISTS outlook_webhook_subscriptions`);
75
+ await queryRunner.query(`DROP TABLE IF EXISTS microsoft_csrf_tokens`);
76
+ }
77
+ }
78
+ ```
79
+
80
+ You can customize this migration to match your database dialect (PostgreSQL, MySQL, etc.) if needed.
81
+
82
+ ### 2. Import Required Modules
83
+
84
+ Register the module in your NestJS application and include the module entities in TypeORM:
85
+
86
+ ```typescript
87
+ import { Module } from '@nestjs/common';
88
+ import { TypeOrmModule } from '@nestjs/typeorm';
89
+ import { EventEmitterModule } from '@nestjs/event-emitter';
90
+ import { ScheduleModule } from '@nestjs/schedule';
91
+ import { MicrosoftOutlookModule } from '@checkfirst/nestjs-outlook';
92
+ import * as path from 'path';
93
+
94
+ // Resolve the path to the outlook package
95
+ const outlookPackagePath = path.dirname(require.resolve('@checkfirst/nestjs-outlook/package.json'));
96
+
97
+ @Module({
98
+ imports: [
99
+ // Required modules
100
+ TypeOrmModule.forRoot({
101
+ // Your TypeORM configuration
102
+ entities: [
103
+ // Your app entities
104
+ __dirname + '/**/*.entity{.ts,.js}',
105
+ // Include outlook module entities
106
+ path.join(outlookPackagePath, 'dist', 'entities', '*.entity.js')
107
+ ],
108
+ }),
109
+ ScheduleModule.forRoot(),
110
+ EventEmitterModule.forRoot(),
111
+
112
+ // Microsoft Outlook Module
113
+ MicrosoftOutlookModule.forRoot({
114
+ clientId: 'YOUR_MICROSOFT_APP_CLIENT_ID',
115
+ clientSecret: 'YOUR_MICROSOFT_APP_CLIENT_SECRET',
116
+ redirectPath: 'auth/microsoft/callback',
117
+ backendBaseUrl: 'https://your-api.example.com',
118
+ basePath: 'api/v1',
119
+ }),
120
+ ],
121
+ })
122
+ export class AppModule {}
123
+ ```
124
+
125
+ ### 3. Create a Login Controller
126
+
127
+ Create a controller to handle Microsoft authentication:
128
+
129
+ ```typescript
130
+ export class CalendarController {
131
+ constructor(
132
+ private readonly microsoftAuthService: MicrosoftAuthService
133
+ ) {}
134
+
135
+ @UseGuards(AuthGuard)
136
+ @Get('auth/microsoft/login')
137
+ async login(@Req() req: Request) {
138
+ const user = req.user as UserTokenPayload;
139
+ if (!user?.id) {
140
+ throw new ForbiddenException('User not authenticated');
141
+ }
142
+
143
+ // Get the login URL from the Microsoft auth service
144
+ return await this.microsoftAuthService.getLoginUrl(user.id.toString());
145
+ }
146
+ }
147
+ ```
148
+
149
+ ## Available Services
150
+
151
+ - `OutlookService` - Main service for Microsoft Graph API operations on Outlook
152
+ - `MicrosoftAuthService` - For authentication and token management
153
+
154
+ ## Events
155
+
156
+ The library uses NestJS's EventEmitter to emit events for various Outlook activities. You can listen to these events in your application to react to changes.
157
+
158
+ ### Available Events
159
+
160
+ The library exposes event types through the `OutlookEventTypes` enum:
161
+
162
+ - `OutlookEventTypes.AUTH_TOKENS_SAVE` - Emitted when OAuth tokens are initially saved
163
+ - `OutlookEventTypes.AUTH_TOKENS_UPDATE` - Emitted when OAuth tokens are refreshed
164
+ - `OutlookEventTypes.EVENT_CREATED` - Emitted when a new Outlook calendar event is created via webhook
165
+ - `OutlookEventTypes.EVENT_UPDATED` - Emitted when an Outlook calendar event is updated via webhook
166
+ - `OutlookEventTypes.EVENT_DELETED` - Emitted when an Outlook calendar event is deleted via webhook
167
+
168
+ ### Listening to Events
169
+
170
+ You can listen to these events in your application using the `@OnEvent` decorator from `@nestjs/event-emitter` and the `OutlookEventTypes` enum:
171
+
172
+ ```typescript
173
+ import { Injectable } from '@nestjs/common';
174
+ import { OnEvent } from '@nestjs/event-emitter';
175
+ import { OutlookEventTypes, OutlookResourceData } from '@checkfirst/nestjs-outlook';
176
+
177
+ @Injectable()
178
+ export class YourService {
179
+ @OnEvent(OutlookEventTypes.EVENT_CREATED)
180
+ handleOutlookEventCreated(data: OutlookResourceData) {
181
+ console.log('New Outlook event created:', data.id);
182
+ // Handle the new event...
183
+ }
184
+
185
+ @OnEvent(OutlookEventTypes.EVENT_UPDATED)
186
+ handleOutlookEventUpdated(data: OutlookResourceData) {
187
+ console.log('Outlook event updated:', data.id);
188
+ // Handle the updated event...
189
+ }
190
+
191
+ @OnEvent(OutlookEventTypes.EVENT_DELETED)
192
+ handleOutlookEventDeleted(data: OutlookResourceData) {
193
+ console.log('Outlook event deleted:', data.id);
194
+ // Handle the deleted event...
195
+ }
196
+ }
197
+ ```
198
+
199
+ These events are emitted when Microsoft Graph sends webhook notifications to your application for calendar events changes.
200
+
201
+ ## Support
202
+
203
+ - [GitHub Issues](https://github.com/checkfirst-ltd/nestjs-outlook/issues)
204
+ - [Documentation](https://github.com/checkfirst-ltd/nestjs-outlook#readme)
205
+
206
+ ## Contributing
207
+
208
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for more details.
209
+
210
+ ## Code of Conduct
211
+
212
+ This project adheres to a Code of Conduct that all participants are expected to follow. Please read the [Code of Conduct](https://github.com/checkfirst-ltd/nestjs-outlook/blob/main/CONTRIBUTING.md#code-of-conduct) for details on our expectations.
213
+
214
+ ## About CheckFirst
215
+
216
+ <a href="https://checkfirst.ai" target="_blank">
217
+ <img src="https://raw.githubusercontent.com/checkfirst-ltd/nestjs-outlook/main/assets/checkfirst-banner.png" alt="CheckFirst Banner" width="100%" />
218
+ </a>
219
+
220
+ [CheckFirst](https://checkfirst.ai) is a trusted provider of developer tools and solutions. We build open-source libraries that help developers create better applications faster.
221
+
222
+ ## License
223
+
224
+ [MIT](LICENSE) © [CheckFirst Ltd](https://checkfirst.ai)
Binary file
@@ -0,0 +1 @@
1
+ export declare const MICROSOFT_CONFIG = "MICROSOFT_CONFIG";
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MICROSOFT_CONFIG = void 0;
4
+ exports.MICROSOFT_CONFIG = 'MICROSOFT_CONFIG';
5
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AACa,QAAA,gBAAgB,GAAG,kBAAkB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { Response } from 'express';
2
+ import { MicrosoftAuthService } from '../services/microsoft-auth.service';
3
+ export declare class MicrosoftAuthController {
4
+ private readonly microsoftAuthService;
5
+ constructor(microsoftAuthService: MicrosoftAuthService);
6
+ callback(code: string, state: string, res: Response): Promise<Response<any, Record<string, any>>>;
7
+ }
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.MicrosoftAuthController = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const swagger_1 = require("@nestjs/swagger");
18
+ const microsoft_auth_service_1 = require("../services/microsoft-auth.service");
19
+ let MicrosoftAuthController = class MicrosoftAuthController {
20
+ constructor(microsoftAuthService) {
21
+ this.microsoftAuthService = microsoftAuthService;
22
+ }
23
+ async callback(code, state, res) {
24
+ if (!code) {
25
+ throw new common_1.BadRequestException('No code provided');
26
+ }
27
+ if (!state) {
28
+ throw new common_1.BadRequestException('No state parameter provided');
29
+ }
30
+ try {
31
+ await this.microsoftAuthService.exchangeCodeForToken(code, state);
32
+ return res.status(common_1.HttpStatus.OK).send(`
33
+ <h1>Authorization successful!</h1>
34
+ <p>Your Microsoft account has been linked successfully.</p>
35
+ <p>You can close this tab now.</p>
36
+ <script>
37
+ // Optionally notify the parent window
38
+ if (window.opener) {
39
+ window.opener.postMessage('microsoft-auth-success', '*');
40
+ }
41
+ </script>
42
+ `);
43
+ }
44
+ catch (error) {
45
+ console.error('Authentication failed:', error);
46
+ throw new common_1.InternalServerErrorException('Authentication failed. Please try again.');
47
+ }
48
+ }
49
+ };
50
+ exports.MicrosoftAuthController = MicrosoftAuthController;
51
+ __decorate([
52
+ (0, common_1.Get)('callback'),
53
+ (0, swagger_1.ApiOperation)({
54
+ summary: 'Microsoft OAuth callback handler',
55
+ description: 'Processes the callback from Microsoft OAuth authentication flow. Exchanges the authorization code for access and refresh tokens, saves them for the user, and sets up necessary webhooks for calendar synchronization. The user ID is extracted from the state parameter.',
56
+ }),
57
+ (0, swagger_1.ApiQuery)({
58
+ name: 'code',
59
+ description: 'Authorization code from Microsoft',
60
+ required: true,
61
+ type: String,
62
+ example: 'M.R3_BAY.c0def4c2-12bf-0b29-9a3a-f8a1c4f56789',
63
+ }),
64
+ (0, swagger_1.ApiQuery)({
65
+ name: 'state',
66
+ description: 'Base64 encoded state containing user ID and CSRF token',
67
+ required: true,
68
+ type: String,
69
+ example: 'eyJ1c2VySWQiOiI3IiwiY3NyZiI6IjEyMzQ1In0',
70
+ }),
71
+ (0, swagger_1.ApiResponse)({
72
+ status: 200,
73
+ description: 'Authentication successful, tokens saved and webhooks created',
74
+ content: {
75
+ 'text/html': {
76
+ example: '<h1>Authorization successful!</h1><p>Your Microsoft account has been linked successfully.</p>',
77
+ },
78
+ },
79
+ }),
80
+ (0, swagger_1.ApiResponse)({
81
+ status: 400,
82
+ description: 'Invalid or missing code/state parameters',
83
+ }),
84
+ (0, swagger_1.ApiResponse)({
85
+ status: 500,
86
+ description: 'Server error during authentication process',
87
+ }),
88
+ (0, swagger_1.ApiProduces)('text/html'),
89
+ __param(0, (0, common_1.Query)('code')),
90
+ __param(1, (0, common_1.Query)('state')),
91
+ __param(2, (0, common_1.Res)()),
92
+ __metadata("design:type", Function),
93
+ __metadata("design:paramtypes", [String, String, Object]),
94
+ __metadata("design:returntype", Promise)
95
+ ], MicrosoftAuthController.prototype, "callback", null);
96
+ exports.MicrosoftAuthController = MicrosoftAuthController = __decorate([
97
+ (0, swagger_1.ApiTags)('Microsoft Auth'),
98
+ (0, common_1.Controller)('auth/microsoft'),
99
+ __metadata("design:paramtypes", [microsoft_auth_service_1.MicrosoftAuthService])
100
+ ], MicrosoftAuthController);
101
+ //# sourceMappingURL=microsoft-auth.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"microsoft-auth.controller.js","sourceRoot":"","sources":["../../src/controllers/microsoft-auth.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAQwB;AAExB,6CAA4F;AAC5F,+EAA0E;AAInE,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IAClC,YAA6B,oBAA0C;QAA1C,yBAAoB,GAApB,oBAAoB,CAAsB;IAAG,CAAC;IA4DrE,AAAN,KAAK,CAAC,QAAQ,CAAgB,IAAY,EAAkB,KAAa,EAAS,GAAa;QAC7F,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,4BAAmB,CAAC,kBAAkB,CAAC,CAAC;QACpD,CAAC;QAED,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,4BAAmB,CAAC,6BAA6B,CAAC,CAAC;QAC/D,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,oBAAoB,CAAC,oBAAoB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAGlE,OAAO,GAAG,CAAC,MAAM,CAAC,mBAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC;;;;;;;;;;OAUrC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;YAC/C,MAAM,IAAI,qCAA4B,CAAC,0CAA0C,CAAC,CAAC;QACrF,CAAC;IACH,CAAC;CACF,CAAA;AA1FY,0DAAuB;AA6D5B;IAvCL,IAAA,YAAG,EAAC,UAAU,CAAC;IACf,IAAA,sBAAY,EAAC;QACZ,OAAO,EAAE,kCAAkC;QAC3C,WAAW,EACT,2QAA2Q;KAC9Q,CAAC;IACD,IAAA,kBAAQ,EAAC;QACR,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE,mCAAmC;QAChD,QAAQ,EAAE,IAAI;QACd,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,+CAA+C;KACzD,CAAC;IACD,IAAA,kBAAQ,EAAC;QACR,IAAI,EAAE,OAAO;QACb,WAAW,EAAE,wDAAwD;QACrE,QAAQ,EAAE,IAAI;QACd,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,yCAAyC;KACnD,CAAC;IACD,IAAA,qBAAW,EAAC;QACX,MAAM,EAAE,GAAG;QACX,WAAW,EAAE,8DAA8D;QAC3E,OAAO,EAAE;YACP,WAAW,EAAE;gBACX,OAAO,EACL,+FAA+F;aAClG;SACF;KACF,CAAC;IACD,IAAA,qBAAW,EAAC;QACX,MAAM,EAAE,GAAG;QACX,WAAW,EAAE,0CAA0C;KACxD,CAAC;IACD,IAAA,qBAAW,EAAC;QACX,MAAM,EAAE,GAAG;QACX,WAAW,EAAE,4CAA4C;KAC1D,CAAC;IACD,IAAA,qBAAW,EAAC,WAAW,CAAC;IACT,WAAA,IAAA,cAAK,EAAC,MAAM,CAAC,CAAA;IAAgB,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;IAAiB,WAAA,IAAA,YAAG,GAAE,CAAA;;;;uDA4BhF;kCAzFU,uBAAuB;IAFnC,IAAA,iBAAO,EAAC,gBAAgB,CAAC;IACzB,IAAA,mBAAU,EAAC,gBAAgB,CAAC;qCAEwB,6CAAoB;GAD5D,uBAAuB,CA0FnC"}
@@ -0,0 +1,8 @@
1
+ import { Response, Request } from 'express';
2
+ import { OutlookService } from '../services/outlook.service';
3
+ import { OutlookWebhookNotificationDto } from '../dto/outlook-webhook-notification.dto';
4
+ export declare class OutlookController {
5
+ private readonly outlookService;
6
+ constructor(outlookService: OutlookService);
7
+ handleOutlookWebhook(validationToken: string, notificationBody: OutlookWebhookNotificationDto, req: Request, res: Response): Promise<void>;
8
+ }
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.OutlookController = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const swagger_1 = require("@nestjs/swagger");
18
+ const outlook_service_1 = require("../services/outlook.service");
19
+ const outlook_webhook_notification_dto_1 = require("../dto/outlook-webhook-notification.dto");
20
+ let OutlookController = class OutlookController {
21
+ constructor(outlookService) {
22
+ this.outlookService = outlookService;
23
+ }
24
+ async handleOutlookWebhook(validationToken, notificationBody, req, res) {
25
+ if (validationToken) {
26
+ console.log('Handling Microsoft Graph validation request with token:', validationToken);
27
+ const decodedToken = decodeURIComponent(validationToken);
28
+ res.set('Content-Type', 'text/plain; charset=utf-8');
29
+ res.send(decodedToken);
30
+ return;
31
+ }
32
+ try {
33
+ console.log('Received webhook notification:', JSON.stringify(notificationBody));
34
+ if (Array.isArray(notificationBody.value)) {
35
+ const processedEvents = new Set();
36
+ let hasSuccessfullyProcessed = false;
37
+ for (const item of notificationBody.value) {
38
+ const resourceData = item.resourceData;
39
+ if (resourceData.id && processedEvents.has(resourceData.id)) {
40
+ continue;
41
+ }
42
+ if (resourceData.id) {
43
+ processedEvents.add(resourceData.id);
44
+ }
45
+ if (resourceData.id && ['deleted', 'created', 'updated'].includes(item.changeType)) {
46
+ try {
47
+ const changeNotification = {
48
+ subscriptionId: item.subscriptionId,
49
+ subscriptionExpirationDateTime: item.subscriptionExpirationDateTime,
50
+ changeType: item.changeType,
51
+ resource: item.resource,
52
+ resourceData: resourceData,
53
+ clientState: item.clientState,
54
+ tenantId: item.tenantId,
55
+ };
56
+ const result = await this.outlookService.handleOutlookWebhook(changeNotification);
57
+ console.log(`Processed ${item.changeType} event for ${resourceData.id}: ${JSON.stringify(result)}`);
58
+ hasSuccessfullyProcessed = true;
59
+ }
60
+ catch (error) {
61
+ console.error(`Error processing ${item.changeType} event ${resourceData.id}:`, error);
62
+ }
63
+ }
64
+ else {
65
+ console.log(`Skipping notification of type: ${item.changeType}`);
66
+ }
67
+ }
68
+ res.json({
69
+ success: true,
70
+ message: hasSuccessfullyProcessed
71
+ ? `Successfully processed events`
72
+ : `Received notifications, but no events were processed`,
73
+ });
74
+ return;
75
+ }
76
+ res.json({
77
+ success: true,
78
+ message: `Received webhook notification in unexpected format`,
79
+ });
80
+ }
81
+ catch (error) {
82
+ console.error('Error processing webhook notification:', error);
83
+ res.status(500).json({
84
+ success: false,
85
+ message: 'Error processing webhook notification',
86
+ });
87
+ }
88
+ }
89
+ };
90
+ exports.OutlookController = OutlookController;
91
+ __decorate([
92
+ (0, common_1.Post)('webhook'),
93
+ (0, common_1.HttpCode)(200),
94
+ (0, swagger_1.ApiResponse)({
95
+ status: 200,
96
+ description: 'Webhook notification processed successfully',
97
+ }),
98
+ (0, swagger_1.ApiBody)({
99
+ description: 'Microsoft Graph webhook notification payload',
100
+ type: outlook_webhook_notification_dto_1.OutlookWebhookNotificationDto,
101
+ required: false,
102
+ }),
103
+ __param(0, (0, common_1.Query)('validationToken')),
104
+ __param(1, (0, common_1.Body)()),
105
+ __param(2, (0, common_1.Req)()),
106
+ __param(3, (0, common_1.Res)()),
107
+ __metadata("design:type", Function),
108
+ __metadata("design:paramtypes", [String, outlook_webhook_notification_dto_1.OutlookWebhookNotificationDto, Object, Object]),
109
+ __metadata("design:returntype", Promise)
110
+ ], OutlookController.prototype, "handleOutlookWebhook", null);
111
+ exports.OutlookController = OutlookController = __decorate([
112
+ (0, swagger_1.ApiTags)('Outlook'),
113
+ (0, common_1.Controller)('outlook'),
114
+ (0, common_1.Injectable)(),
115
+ __metadata("design:paramtypes", [outlook_service_1.OutlookService])
116
+ ], OutlookController);
117
+ //# sourceMappingURL=outlook.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outlook.controller.js","sourceRoot":"","sources":["../../src/controllers/outlook.controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export declare class OutlookResourceData {
2
+ '@odata.type'?: string;
3
+ '@odata.id'?: string;
4
+ '@odata.etag'?: string;
5
+ id: string;
6
+ [key: string]: unknown;
7
+ }
8
+ export declare class OutlookWebhookNotificationItemDto {
9
+ subscriptionId: string;
10
+ subscriptionExpirationDateTime: string;
11
+ changeType: string;
12
+ resource: string;
13
+ resourceData: OutlookResourceData;
14
+ clientState?: string;
15
+ tenantId?: string;
16
+ }
17
+ export declare class OutlookWebhookNotificationDto {
18
+ value: OutlookWebhookNotificationItemDto[];
19
+ }