@node-ts/bus-workflow
Version:
A workflow engine for orchestrating logic flows in distributed applications.
111 lines • 5.98 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkflowRegistry = void 0;
const tslib_1 = require("tslib");
const inversify_1 = require("inversify");
const bus_core_1 = require("@node-ts/bus-core");
const bus_workflow_symbols_1 = require("../../bus-workflow-symbols");
const started_by_1 = require("../decorators/started-by");
const handles_1 = require("../decorators/handles");
const logger_core_1 = require("@node-ts/logger-core");
/**
* The central workflow registry that holds all workflows managed by the application. This includes
* - the list of workflows
* - what messages start the workflow
* - what messages are handled by each workflow
*/
let WorkflowRegistry = class WorkflowRegistry {
constructor(handlerRegistry, persistence, startedByFactory, handlesFactory, logger) {
this.handlerRegistry = handlerRegistry;
this.persistence = persistence;
this.startedByFactory = startedByFactory;
this.handlesFactory = handlesFactory;
this.logger = logger;
this.workflowRegistry = [];
this.isInitialized = false;
this.isInitializing = false;
}
register(workflowConstructor, workflowDataConstructor) {
if (this.isInitialized) {
throw new Error(`Attempted to register workflow (${workflowConstructor.name}) after workflows have been initialized`);
}
const duplicateWorkflowName = this.workflowRegistry
.some(r => r.workflowConstructor.name === workflowConstructor.name);
if (duplicateWorkflowName) {
throw new Error(`Attempted to register two workflows with the same name (${workflowConstructor.name})`);
}
this.workflowRegistry.push({
workflowConstructor,
workflowDataConstructor
});
}
/**
* Initialize all services that are used to support workflows. This registers all messages subscribed to
* in workflows as handlers with the bus, as well as initializing the persistence service so that workflow
* states can be stored.
*
* This should be called once as the application is starting.
*/
async initializeWorkflows() {
if (this.isInitialized || this.isInitializing) {
throw new Error('Attempted to initialize workflow registry after it has already been initialized.');
}
this.isInitializing = true;
this.logger.info('Initializing workflows...');
if (this.persistence.initialize) {
await this.persistence.initialize();
}
for (const registration of this.workflowRegistry) {
const startedByHandlers = started_by_1.WorkflowStartedByMetadata.getSteps(registration.workflowConstructor);
this.registerStartedBy(startedByHandlers, registration);
const messageHandlers = handles_1.WorkflowHandlesMetadata.getSteps(registration.workflowConstructor);
this.registerHandles(messageHandlers, registration);
const messageWorkflowMappings = messageHandlers.map(s => s.messageWorkflowMapping);
await this.persistence.initializeWorkflow(registration.workflowDataConstructor, messageWorkflowMappings);
this.logger.debug('Workflow initialized', { workflowName: registration.workflowConstructor.name });
}
this.workflowRegistry = [];
this.isInitialized = true;
this.isInitializing = false;
this.logger.info('Workflows initialized');
}
async dispose() {
if (this.persistence.dispose) {
await this.persistence.dispose();
}
}
registerStartedBy(startedByHandlers, registration) {
if (!startedByHandlers.length) {
throw new Error(`Workflow ${registration.workflowConstructor.name} does not have a started by step`);
}
startedByHandlers.forEach(step => {
const messageName = new step.messageConstructor().$name;
const handlerFactory = (context) => {
const workflow = context.container.resolve(registration.workflowConstructor);
return this.startedByFactory(registration.workflowDataConstructor, workflow[step.propertyKey].bind(workflow));
};
this.handlerRegistry.register((m) => m.$name === messageName, Symbol.for(`node-ts/bus/workflow/${registration.workflowConstructor.name}-${messageName}-started-by-proxy`), handlerFactory, step.messageConstructor);
});
}
registerHandles(messageHandlers, registration) {
messageHandlers.forEach(step => {
const messageName = new step.messageConstructor().$name;
const handler = (context) => {
const workflow = context.container.resolve(registration.workflowConstructor);
return this.handlesFactory(workflow[step.propertyKey].bind(workflow), registration.workflowDataConstructor, step.messageWorkflowMapping);
};
this.handlerRegistry.register((m) => m.$name === messageName, Symbol.for(`node-ts/bus/workflow/${registration.workflowConstructor.name}-${messageName}-handles-proxy`), handler, step.messageConstructor);
});
}
};
WorkflowRegistry = tslib_1.__decorate([
inversify_1.injectable(),
tslib_1.__param(0, inversify_1.inject(bus_core_1.BUS_SYMBOLS.HandlerRegistry)),
tslib_1.__param(1, inversify_1.inject(bus_workflow_symbols_1.BUS_WORKFLOW_SYMBOLS.Persistence)),
tslib_1.__param(2, inversify_1.inject(bus_workflow_symbols_1.BUS_WORKFLOW_INTERNAL_SYMBOLS.StartedByProxy)),
tslib_1.__param(3, inversify_1.inject(bus_workflow_symbols_1.BUS_WORKFLOW_INTERNAL_SYMBOLS.HandlesProxy)),
tslib_1.__param(4, inversify_1.inject(logger_core_1.LOGGER_SYMBOLS.Logger)),
tslib_1.__metadata("design:paramtypes", [bus_core_1.HandlerRegistry, Object, Function, Function, Object])
], WorkflowRegistry);
exports.WorkflowRegistry = WorkflowRegistry;
//# sourceMappingURL=workflow-registry.js.map