UNPKG

nestjs-temporal-core

Version:

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

382 lines 15.5 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 __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var TemporalScheduleService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.TemporalScheduleService = void 0; const common_1 = require("@nestjs/common"); const core_1 = require("@nestjs/core"); const client_1 = require("@temporalio/client"); const constants_1 = require("../constants"); const temporal_metadata_service_1 = require("./temporal-metadata.service"); const logger_1 = require("../utils/logger"); let TemporalScheduleService = TemporalScheduleService_1 = class TemporalScheduleService { constructor(options, client, discoveryService, metadataAccessor) { this.options = options; this.client = client; this.discoveryService = discoveryService; this.metadataAccessor = metadataAccessor; this.scheduleHandles = new Map(); this.isInitialized = false; this.logger = (0, logger_1.createLogger)(TemporalScheduleService_1.name, { enableLogger: options.enableLogger, logLevel: options.logLevel, }); } async onModuleInit() { try { this.logger.info('Initializing Temporal Schedule Service...'); await this.initializeScheduleClient(); await this.discoverAndRegisterSchedules(); this.isInitialized = true; this.logger.info('Temporal Schedule Service initialized successfully'); } catch (error) { this.logger.error(`Failed to initialize Temporal Schedule Service: ${error instanceof Error ? error.message : 'Unknown error'}`, error); throw error; } } async onModuleDestroy() { try { this.logger.info('Shutting down Temporal Schedule Service...'); this.scheduleHandles.clear(); this.isInitialized = false; this.logger.info('Temporal Schedule Service shut down successfully'); } catch (error) { this.logger.error(`Error during schedule service shutdown: ${error instanceof Error ? error.message : 'Unknown error'}`, error); } } async initializeScheduleClient() { try { if (this.client?.schedule) { this.scheduleClient = this.client.schedule; this.logger.debug('Schedule client initialized from existing client'); return { success: true, client: this.scheduleClient, source: 'existing', }; } else { try { this.scheduleClient = new client_1.ScheduleClient({ connection: this.client.connection, namespace: this.options.connection?.namespace || 'default', }); this.logger.debug('Schedule client initialized successfully'); return { success: true, client: this.scheduleClient, source: 'new', }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; this.logger.warn(`Schedule client not available: ${errorMessage}`); this.scheduleClient = undefined; return { success: false, error: error instanceof Error ? error : new Error(errorMessage), source: 'none', }; } } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; this.logger.error(`Failed to initialize schedule client: ${errorMessage}`, error); this.scheduleClient = undefined; return { success: false, error: error instanceof Error ? error : new Error(errorMessage), source: 'none', }; } } async discoverAndRegisterSchedules() { const startTime = Date.now(); const discoveredCount = 0; try { this.logger.debug('Schedule discovery skipped - schedule decorators not implemented'); const duration = Date.now() - startTime; this.logger.debug(`Discovered and registered ${discoveredCount} scheduled workflows`); return { success: true, discoveredCount, errors: [], duration, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; this.logger.error(`Failed to discover schedules: ${errorMessage}`, error); return { success: false, discoveredCount: 0, errors: [{ schedule: 'discovery', error: errorMessage }], duration: Date.now() - startTime, }; } } async registerScheduledWorkflow(instance, metatype, scheduleMetadata) { try { const scheduleId = scheduleMetadata.scheduleId || `${metatype.name}-schedule`; const workflowType = scheduleMetadata.workflowType || metatype.name; const scheduleSpecResult = this.buildScheduleSpec(scheduleMetadata); if (!scheduleSpecResult.success) { return { success: false, scheduleId, error: scheduleSpecResult.error, }; } const workflowOptions = this.buildWorkflowOptions(scheduleMetadata); const action = { type: 'startWorkflow', workflowType, taskQueue: scheduleMetadata.taskQueue || 'default', args: scheduleMetadata.args || [], ...workflowOptions, }; const scheduleOptions = { scheduleId, spec: scheduleSpecResult.spec, action, memo: scheduleMetadata.memo || {}, searchAttributes: scheduleMetadata.searchAttributes || {}, }; const scheduleHandle = await this.scheduleClient.create(scheduleOptions); this.scheduleHandles.set(scheduleId, scheduleHandle); this.logger.debug(`Registered scheduled workflow: ${scheduleId} with type: ${workflowType}`); return { success: true, scheduleId, handle: scheduleHandle, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; this.logger.error(`Failed to register scheduled workflow: ${errorMessage}`, error); return { success: false, scheduleId: scheduleMetadata.scheduleId || `${metatype.name}-schedule`, error: error instanceof Error ? error : new Error(errorMessage), }; } } buildScheduleSpec(scheduleMetadata) { try { const spec = {}; if (scheduleMetadata.cron) { spec.cronExpressions = Array.isArray(scheduleMetadata.cron) ? scheduleMetadata.cron : [scheduleMetadata.cron]; } if (scheduleMetadata.interval) { const intervals = Array.isArray(scheduleMetadata.interval) ? scheduleMetadata.interval : [scheduleMetadata.interval]; const parsedIntervals = intervals .map((interval) => { const result = this.parseInterval(interval); return result.success ? result.interval : null; }) .filter(Boolean); if (parsedIntervals.length > 0) { spec.intervals = parsedIntervals; } } if (scheduleMetadata.calendar) { spec.calendars = Array.isArray(scheduleMetadata.calendar) ? scheduleMetadata.calendar : [scheduleMetadata.calendar]; } if (scheduleMetadata.timezone) { spec.timeZone = scheduleMetadata.timezone; } if (scheduleMetadata.jitter) { spec.jitter = scheduleMetadata.jitter; } return { success: true, spec, }; } catch (error) { return { success: false, error: error instanceof Error ? error : new Error('Unknown error'), }; } } buildWorkflowOptions(scheduleMetadata) { const options = {}; if (scheduleMetadata.taskQueue) { options.taskQueue = scheduleMetadata.taskQueue; } if (scheduleMetadata.workflowId) { options.workflowId = scheduleMetadata.workflowId; } if (scheduleMetadata.workflowExecutionTimeout) { options.workflowExecutionTimeout = scheduleMetadata.workflowExecutionTimeout; } if (scheduleMetadata.workflowRunTimeout) { options.workflowRunTimeout = scheduleMetadata.workflowRunTimeout; } if (scheduleMetadata.workflowTaskTimeout) { options.workflowTaskTimeout = scheduleMetadata.workflowTaskTimeout; } if (scheduleMetadata.retryPolicy) { options.retryPolicy = scheduleMetadata.retryPolicy; } if (scheduleMetadata.args) { options.args = scheduleMetadata.args; } return options; } parseInterval(interval) { try { if (typeof interval === 'number') { return { success: true, interval: { every: `${interval}ms` }, }; } const intervalStr = interval.toString().toLowerCase(); if (intervalStr.includes('ms')) { return { success: true, interval: { every: intervalStr }, }; } if (intervalStr.includes('s')) { return { success: true, interval: { every: intervalStr }, }; } if (intervalStr.includes('m')) { return { success: true, interval: { every: intervalStr }, }; } if (intervalStr.includes('h')) { return { success: true, interval: { every: intervalStr }, }; } return { success: true, interval: { every: `${interval}ms` }, }; } catch (error) { return { success: false, error: error instanceof Error ? error : new Error('Unknown error'), }; } } async createSchedule(options) { this.ensureInitialized(); try { const scheduleHandle = await this.scheduleClient.create(options); this.scheduleHandles.set(options.scheduleId, scheduleHandle); this.logger.debug(`Created schedule: ${options.scheduleId}`); return { success: true, scheduleId: options.scheduleId, handle: scheduleHandle, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; this.logger.error(`Failed to create schedule ${options.scheduleId}: ${errorMessage}`, error); return { success: false, scheduleId: options.scheduleId, error: error instanceof Error ? error : new Error(errorMessage), }; } } async getSchedule(scheduleId) { this.ensureInitialized(); try { let scheduleHandle = this.scheduleHandles.get(scheduleId); if (!scheduleHandle) { scheduleHandle = this.scheduleClient.getHandle(scheduleId); this.scheduleHandles.set(scheduleId, scheduleHandle); } return { success: true, handle: scheduleHandle, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; this.logger.error(`Failed to get schedule ${scheduleId}: ${errorMessage}`, error); return { success: false, error: error instanceof Error ? error : new Error(errorMessage), }; } } isHealthy() { return this.isInitialized; } getScheduleStats() { return { total: this.scheduleHandles.size, active: this.scheduleHandles.size, inactive: 0, errors: 0, lastUpdated: new Date(), }; } getStatus() { return { available: this.isInitialized, healthy: this.isHealthy(), schedulesSupported: !!this.scheduleClient, initialized: this.isInitialized, }; } getHealth() { return { status: this.isInitialized ? 'healthy' : 'unhealthy', schedulesCount: this.scheduleHandles.size, isInitialized: this.isInitialized, details: { scheduleIds: Array.from(this.scheduleHandles.keys()), hasScheduleClient: !!this.scheduleClient, }, }; } ensureInitialized() { if (!this.isInitialized) { throw new Error('Temporal Schedule Service is not initialized'); } } }; exports.TemporalScheduleService = TemporalScheduleService; exports.TemporalScheduleService = TemporalScheduleService = TemporalScheduleService_1 = __decorate([ (0, common_1.Injectable)(), __param(0, (0, common_1.Inject)(constants_1.TEMPORAL_MODULE_OPTIONS)), __param(1, (0, common_1.Inject)(constants_1.TEMPORAL_CLIENT)), __metadata("design:paramtypes", [Object, Object, core_1.DiscoveryService, temporal_metadata_service_1.TemporalMetadataAccessor]) ], TemporalScheduleService); //# sourceMappingURL=temporal-schedule.service.js.map