UNPKG

nestjs-temporal-core

Version:

Complete NestJS integration for Temporal.io with auto-discovery, declarative scheduling, enhanced monitoring, and enterprise-ready features

238 lines 11.2 kB
"use strict"; 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 TemporalScheduleManagerService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.TemporalScheduleManagerService = void 0; const common_1 = require("@nestjs/common"); const temporal_discovery_service_1 = require("./temporal-discovery.service"); const client_1 = require("../client"); const logger_1 = require("../utils/logger"); let TemporalScheduleManagerService = TemporalScheduleManagerService_1 = class TemporalScheduleManagerService { constructor(discoveryService, scheduleService) { this.discoveryService = discoveryService; this.scheduleService = scheduleService; this.logger = (0, logger_1.createLogger)(TemporalScheduleManagerService_1.name); this.managedSchedules = new Map(); this.setupPromises = new Map(); } async onApplicationBootstrap() { await this.setupDiscoveredSchedules(); } async onModuleDestroy() { this.logger.log('Schedule manager shutting down'); } async setupDiscoveredSchedules() { const scheduledWorkflows = this.discoveryService.getScheduledWorkflows(); if (scheduledWorkflows.length === 0) { this.logger.log('No scheduled workflows found'); return; } this.logger.log(`Setting up ${scheduledWorkflows.length} scheduled workflows`); const setupPromises = scheduledWorkflows.map((scheduled) => this.setupSingleSchedule(scheduled)); const results = await Promise.allSettled(setupPromises); this.logSetupResults(results, scheduledWorkflows); } async setupSingleSchedule(scheduled) { const { scheduleOptions, workflowName } = scheduled; const { scheduleId } = scheduleOptions; if (this.setupPromises.has(scheduleId)) { await this.setupPromises.get(scheduleId); return; } const setupPromise = this.performScheduleSetup(scheduled); this.setupPromises.set(scheduleId, setupPromise); try { await setupPromise; this.updateScheduleStatus(scheduleId, workflowName, true, true); this.logger.debug(`Successfully set up schedule: ${scheduleId}`); } catch (error) { this.updateScheduleStatus(scheduleId, workflowName, false, false, error.message); this.logger.error(`Failed to setup schedule ${scheduleId}: ${error.message}`); } finally { this.setupPromises.delete(scheduleId); } } async performScheduleSetup(scheduled) { const { scheduleOptions, workflowName, controllerInfo } = scheduled; const { scheduleId } = scheduleOptions; if (scheduleOptions.autoStart === false) { this.logger.debug(`Auto-start disabled for schedule ${scheduleId}`); this.updateScheduleStatus(scheduleId, workflowName, true, false, 'Auto-start disabled'); return; } if (await this.scheduleService.scheduleExists(scheduleId)) { this.logger.debug(`Schedule ${scheduleId} already exists, skipping creation`); this.updateScheduleStatus(scheduleId, workflowName, true, true, 'Already exists'); return; } const taskQueue = this.resolveTaskQueue(scheduleOptions, controllerInfo); if (scheduleOptions.cron) { await this.createCronSchedule(scheduled, taskQueue); } else if (scheduleOptions.interval) { await this.createIntervalSchedule(scheduled, taskQueue); } else { throw new Error('Schedule must have either cron or interval configuration'); } if (scheduleOptions.startPaused) { await this.scheduleService.pauseSchedule(scheduleId, 'Started in paused state'); this.logger.debug(`Schedule ${scheduleId} created in paused state`); } } async createCronSchedule(scheduled, taskQueue) { const { scheduleOptions, workflowName } = scheduled; await this.scheduleService.createCronSchedule(scheduleOptions.scheduleId, workflowName, scheduleOptions.cron, taskQueue, [], { description: scheduleOptions.description, timezone: scheduleOptions.timezone, overlapPolicy: scheduleOptions.overlapPolicy, startPaused: scheduleOptions.startPaused, }); this.logger.log(`Created cron schedule: ${scheduleOptions.scheduleId} -> ${workflowName} (${scheduleOptions.cron})`); } async createIntervalSchedule(scheduled, taskQueue) { const { scheduleOptions, workflowName } = scheduled; await this.scheduleService.createIntervalSchedule(scheduleOptions.scheduleId, workflowName, scheduleOptions.interval, taskQueue, [], { description: scheduleOptions.description, overlapPolicy: scheduleOptions.overlapPolicy, startPaused: scheduleOptions.startPaused, }); this.logger.log(`Created interval schedule: ${scheduleOptions.scheduleId} -> ${workflowName} (${scheduleOptions.interval})`); } async triggerSchedule(scheduleId) { this.ensureScheduleManaged(scheduleId); await this.scheduleService.triggerSchedule(scheduleId); this.logger.log(`Triggered schedule: ${scheduleId}`); } async pauseSchedule(scheduleId, note) { this.ensureScheduleManaged(scheduleId); await this.scheduleService.pauseSchedule(scheduleId, note); this.updateScheduleStatus(scheduleId, this.getScheduleWorkflowName(scheduleId), true, false); this.logger.log(`Paused schedule: ${scheduleId}${note ? ` (${note})` : ''}`); } async resumeSchedule(scheduleId, note) { this.ensureScheduleManaged(scheduleId); await this.scheduleService.resumeSchedule(scheduleId, note); this.updateScheduleStatus(scheduleId, this.getScheduleWorkflowName(scheduleId), true, true); this.logger.log(`Resumed schedule: ${scheduleId}${note ? ` (${note})` : ''}`); } async deleteSchedule(scheduleId, force = false) { this.ensureScheduleManaged(scheduleId); if (!force) { this.logger.warn(`Deleting schedule ${scheduleId} requires force=true. This action cannot be undone.`); throw new Error('Schedule deletion requires force=true confirmation'); } await this.scheduleService.deleteSchedule(scheduleId); this.managedSchedules.delete(scheduleId); this.logger.log(`Deleted schedule: ${scheduleId}`); } async retryFailedSetups() { const failedSchedules = this.discoveryService .getScheduledWorkflows() .filter((scheduled) => { const status = this.managedSchedules.get(scheduled.scheduleOptions.scheduleId); return !status || !status.isManaged; }); if (failedSchedules.length === 0) { this.logger.log('No failed schedules to retry'); return; } this.logger.log(`Retrying ${failedSchedules.length} failed schedule setups`); for (const scheduled of failedSchedules) { await this.setupSingleSchedule(scheduled); } } getManagedSchedules() { return Array.from(this.managedSchedules.keys()); } getManagedScheduleStatuses() { return Array.from(this.managedSchedules.values()); } getScheduleStatus(scheduleId) { return this.managedSchedules.get(scheduleId); } isScheduleManaged(scheduleId) { return this.managedSchedules.has(scheduleId); } isScheduleActive(scheduleId) { const status = this.managedSchedules.get(scheduleId); return status?.isActive || false; } getScheduleStats() { const statuses = Array.from(this.managedSchedules.values()); return { total: statuses.length, active: statuses.filter((s) => s.isActive).length, inactive: statuses.filter((s) => !s.isActive).length, errors: statuses.filter((s) => s.lastError).length, }; } getHealthStatus() { const stats = this.getScheduleStats(); let status; if (stats.errors > 0) { status = stats.errors === stats.total ? 'unhealthy' : 'degraded'; } else { status = 'healthy'; } return { status, managedSchedules: stats.total, activeSchedules: stats.active, errorCount: stats.errors, }; } resolveTaskQueue(scheduleOptions, controllerInfo) { return (scheduleOptions.taskQueue || controllerInfo.taskQueue || 'default'); } updateScheduleStatus(scheduleId, workflowName, isManaged, isActive, error) { const existing = this.managedSchedules.get(scheduleId); const now = new Date(); this.managedSchedules.set(scheduleId, { scheduleId, workflowName, isManaged, isActive, lastError: error, createdAt: existing?.createdAt || now, lastUpdatedAt: now, }); } logSetupResults(results, scheduledWorkflows) { const successful = results.filter((r) => r.status === 'fulfilled').length; const failed = results.filter((r) => r.status === 'rejected').length; this.logger.log(`Schedule setup completed: ${successful} successful, ${failed} failed out of ${scheduledWorkflows.length} total`); if (failed > 0) { this.logger.warn(`${failed} schedules failed to setup. Use retryFailedSetups() to retry.`); } } ensureScheduleManaged(scheduleId) { if (!this.isScheduleManaged(scheduleId)) { throw new Error(`Schedule '${scheduleId}' is not managed by this service`); } } getScheduleWorkflowName(scheduleId) { const status = this.managedSchedules.get(scheduleId); return status?.workflowName || 'unknown'; } }; exports.TemporalScheduleManagerService = TemporalScheduleManagerService; exports.TemporalScheduleManagerService = TemporalScheduleManagerService = TemporalScheduleManagerService_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [temporal_discovery_service_1.TemporalDiscoveryService, client_1.TemporalScheduleService]) ], TemporalScheduleManagerService); //# sourceMappingURL=temporal-schedule-manager.service.js.map