@checkfirst/nestjs-outlook
Version:
An opinionated NestJS module for Microsoft Outlook integration that provides easy access to Microsoft Graph API for emails, calendars, and more.
201 lines • 10.6 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var LifecycleEventHandlerService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.LifecycleEventHandlerService = void 0;
const common_1 = require("@nestjs/common");
const event_emitter_1 = require("@nestjs/event-emitter");
const lifecycle_event_types_enum_1 = require("../../enums/lifecycle-event-types.enum");
const event_types_enum_1 = require("../../enums/event-types.enum");
const outlook_webhook_subscription_repository_1 = require("../../repositories/outlook-webhook-subscription.repository");
const calendar_service_1 = require("./calendar.service");
const user_id_converter_service_1 = require("../shared/user-id-converter.service");
let LifecycleEventHandlerService = LifecycleEventHandlerService_1 = class LifecycleEventHandlerService {
constructor(calendarService, subscriptionRepository, eventEmitter, userIdConverter) {
this.calendarService = calendarService;
this.subscriptionRepository = subscriptionRepository;
this.eventEmitter = eventEmitter;
this.userIdConverter = userIdConverter;
this.logger = new common_1.Logger(LifecycleEventHandlerService_1.name);
}
async handleLifecycleEvent(notificationItem) {
const { lifecycleEvent, subscriptionId, tenantId } = notificationItem;
if (!lifecycleEvent) {
return {
success: false,
message: 'Missing lifecycleEvent field',
};
}
if (!subscriptionId) {
this.logger.warn('Lifecycle event received without subscriptionId');
return {
success: false,
message: 'Missing subscriptionId field',
};
}
this.logger.log(`Handling lifecycle event: ${lifecycleEvent} for subscription ${subscriptionId}`);
try {
switch (lifecycleEvent) {
case lifecycle_event_types_enum_1.LifecycleEventType.REAUTHORIZATION_REQUIRED:
return await this.handleReauthorizationRequired(subscriptionId, tenantId);
case lifecycle_event_types_enum_1.LifecycleEventType.SUBSCRIPTION_REMOVED:
return await this.handleSubscriptionRemoved(subscriptionId, tenantId);
case lifecycle_event_types_enum_1.LifecycleEventType.MISSED:
return await this.handleMissedNotifications(subscriptionId, tenantId);
default:
this.logger.warn(`Unknown lifecycle event type: ${lifecycleEvent}`);
return {
success: false,
message: `Unknown lifecycle event type: ${lifecycleEvent}`,
};
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
this.logger.error(`Error handling lifecycle event ${lifecycleEvent}: ${errorMessage}`);
return {
success: false,
message: errorMessage,
};
}
}
async handleReauthorizationRequired(subscriptionId, tenantId) {
this.logger.log(`[REAUTHORIZATION_REQUIRED] Subscription ${subscriptionId} requires reauthorization`);
try {
const subscription = await this.subscriptionRepository.findBySubscriptionId(subscriptionId);
if (!subscription) {
this.logger.warn(`[REAUTHORIZATION_REQUIRED] Subscription ${subscriptionId} not found in database`);
return {
success: false,
message: 'Subscription not found',
};
}
this.logger.log(`[REAUTHORIZATION_REQUIRED] Attempting to renew subscription ${subscriptionId}`);
const renewedSubscription = await this.calendarService.renewWebhookSubscription(subscriptionId, subscription.userId);
const expirationDate = renewedSubscription.expirationDateTime
? new Date(renewedSubscription.expirationDateTime).toISOString()
: 'unknown';
this.logger.log(`[REAUTHORIZATION_REQUIRED] Successfully renewed subscription ${subscriptionId}. New expiration: ${expirationDate}`);
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.LIFECYCLE_REAUTHORIZATION_REQUIRED, {
subscriptionId,
tenantId,
userId: subscription.userId,
renewalSuccessful: true,
});
return {
success: true,
message: 'Subscription renewed successfully',
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
this.logger.error(`[REAUTHORIZATION_REQUIRED] Failed to renew subscription ${subscriptionId}: ${errorMessage}`);
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.LIFECYCLE_REAUTHORIZATION_REQUIRED, {
subscriptionId,
tenantId,
renewalSuccessful: false,
error: errorMessage,
});
return {
success: false,
message: `Failed to renew subscription: ${errorMessage}`,
};
}
}
async handleSubscriptionRemoved(subscriptionId, tenantId) {
this.logger.warn(`[SUBSCRIPTION_REMOVED] Subscription ${subscriptionId} has been removed by Microsoft Graph`);
try {
const subscription = await this.subscriptionRepository.findBySubscriptionId(subscriptionId);
if (!subscription) {
this.logger.warn(`[SUBSCRIPTION_REMOVED] Subscription ${subscriptionId} not found in database`);
return {
success: true,
message: 'Subscription not found (already removed)',
};
}
await this.subscriptionRepository.deactivateSubscription(subscriptionId);
this.logger.log(`[SUBSCRIPTION_REMOVED] Deactivated subscription ${subscriptionId} in database`);
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.LIFECYCLE_SUBSCRIPTION_REMOVED, {
subscriptionId,
tenantId,
userId: subscription.userId,
resource: subscription.resource,
});
return {
success: true,
message: 'Subscription marked as inactive',
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
this.logger.error(`[SUBSCRIPTION_REMOVED] Error handling removed subscription ${subscriptionId}: ${errorMessage}`);
return {
success: false,
message: errorMessage,
};
}
}
async handleMissedNotifications(subscriptionId, tenantId) {
this.logger.warn(`[MISSED] Notifications were missed for subscription ${subscriptionId}. Triggering delta sync.`);
try {
const subscription = await this.subscriptionRepository.findBySubscriptionId(subscriptionId);
if (!subscription) {
this.logger.warn(`[MISSED] Subscription ${subscriptionId} not found in database`);
return {
success: false,
message: 'Subscription not found',
};
}
this.logger.log(`[MISSED] Starting delta sync for user ${subscription.userId} to recover missed changes`);
const externalUserId = await this.userIdConverter.internalToExternal(subscription.userId);
const changes = await this.calendarService.fetchAndSortChanges(externalUserId, false);
const changesCount = changes.length;
this.logger.log(`[MISSED] Delta sync completed. Found ${changesCount} changes for subscription ${subscriptionId}`);
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.LIFECYCLE_MISSED, {
subscriptionId,
tenantId,
userId: subscription.userId,
changesFound: changesCount,
});
return {
success: true,
message: `Delta sync completed. Found ${changesCount} changes.`,
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
this.logger.error(`[MISSED] Error during delta sync for subscription ${subscriptionId}: ${errorMessage}`);
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.LIFECYCLE_MISSED, {
subscriptionId,
tenantId,
syncSuccessful: false,
error: errorMessage,
});
return {
success: false,
message: `Delta sync failed: ${errorMessage}`,
};
}
}
};
exports.LifecycleEventHandlerService = LifecycleEventHandlerService;
exports.LifecycleEventHandlerService = LifecycleEventHandlerService = LifecycleEventHandlerService_1 = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, common_1.Inject)((0, common_1.forwardRef)(() => calendar_service_1.CalendarService))),
__metadata("design:paramtypes", [calendar_service_1.CalendarService,
outlook_webhook_subscription_repository_1.OutlookWebhookSubscriptionRepository,
event_emitter_1.EventEmitter2,
user_id_converter_service_1.UserIdConverterService])
], LifecycleEventHandlerService);
//# sourceMappingURL=lifecycle-event-handler.service.js.map