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
JavaScript
"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