@5minds/processcube_engine
Version:
The ProcessCube Engine. Stores and executes BPMNs.
193 lines • 10.9 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GatewayHandler = void 0;
const async_lock_1 = __importDefault(require("async-lock"));
const processcube_engine_sdk_1 = require("@5minds/processcube_engine_sdk");
const Contracts_1 = require("../../../Contracts");
const EventAggregator_1 = __importDefault(require("../../../Tools/EventAggregator"));
const FlowNodeHandler_1 = require("../FlowNodeHandler");
const lock = new async_lock_1.default();
class GatewayHandler extends FlowNodeHandler_1.FlowNodeHandler {
flowNodeInstancesArrivedAtGateway = [];
unresolved = [];
async beforeExecute(resolveFunction, rejectFunction, resumedFlowNodeInstances) {
await super.beforeExecute();
this.processEventSubscriptions.push(this.subscribeToProcessKilledEvent(rejectFunction));
this.processEventSubscriptions.push(this.subscribeToTerminateEndEventReached(resolveFunction));
this.processEventSubscriptions.push(this.subscribeToProcessError(rejectFunction));
}
async afterExecute() {
await super.afterExecute();
this.publishGatewayFinishedNotification();
}
async execute(previousFlowNodeInstanceId, previousResult) {
const processInstanceState = this.processInstance.getState();
if (processInstanceState && processInstanceState !== processcube_engine_sdk_1.ProcessInstanceState.running) {
return undefined;
}
return new Promise(async (resolve, reject) => {
try {
let nextFlowNodes;
await lock.acquire(this.flowNodeInstanceId, async () => {
if (this.state === processcube_engine_sdk_1.FlowNodeInstanceState.finished) {
return resolve();
}
this.previousFlowNodeInstanceId = previousFlowNodeInstanceId;
this.processToken = previousResult ?? {};
await this.beforeExecute(resolve, reject);
await this.persistOnEnter(this.flowNodeInstancesArrivedAtGateway);
this.publishGatewayReachedNotification();
const gatewayTypeIsNotSupported = this.flowNode.gatewayDirection === processcube_engine_sdk_1.Model.Gateways.GatewayDirection.Unspecified || this.flowNode.gatewayDirection === processcube_engine_sdk_1.Model.Gateways.GatewayDirection.Mixed;
if (gatewayTypeIsNotSupported) {
const unsupportedErrorMessage = `Gateway ${this.flowNode.id} is not connected correctly.\n` +
'Only Split Gateways (one incoming and multiple outgoing Sequence Flows) and ' +
'Join Gateways (multiple incoming and one outgoing Sequence Flow) are supported.';
this.logger.error(unsupportedErrorMessage);
const errorToThrow = new processcube_engine_sdk_1.UnprocessableEntityError(unsupportedErrorMessage, 'process');
throw errorToThrow;
}
nextFlowNodes = await this.startExecution();
await this.persistOnExit();
await this.afterExecute();
});
if (nextFlowNodes == null || nextFlowNodes.length === 0) {
this.unresolved.push(resolve);
return;
}
const nextFlowNodeExecutionPromises = [];
for (const nextFlowNode of nextFlowNodes) {
const handleNextFlowNodePromise = this.handleNextFlowNode(nextFlowNode);
nextFlowNodeExecutionPromises.push(handleNextFlowNodePromise);
}
await Promise.all(nextFlowNodeExecutionPromises);
this.unresolved.forEach((r) => r());
return resolve();
}
catch (error) {
return this.handleError(error, resolve, reject);
}
});
}
async resume(flowNodeInstanceForHandler, allFlowNodeInstances) {
this.state = flowNodeInstanceForHandler.state;
this.startedAt = flowNodeInstanceForHandler.startedAt;
return new Promise(async (resolve, reject) => {
try {
let nextFlowNodes;
await lock.acquire(this.flowNodeInstanceId, async () => {
this.previousFlowNodeInstanceId = flowNodeInstanceForHandler.previousFlowNodeInstanceId;
this.flowNodeInstanceId = flowNodeInstanceForHandler.flowNodeInstanceId;
await this.beforeExecute(resolve, reject, allFlowNodeInstances);
this.publishGatewayReachedNotification();
nextFlowNodes = await this.resumeFromState(flowNodeInstanceForHandler, allFlowNodeInstances);
await this.afterExecute();
});
if (nextFlowNodes == null || nextFlowNodes.length === 0) {
return resolve();
}
const nextFlowNodeExecutionPromises = [];
for (const nextFlowNode of nextFlowNodes) {
const handleNextFlowNodePromise = this.handleNextFlowNode(nextFlowNode, allFlowNodeInstances);
nextFlowNodeExecutionPromises.push(handleNextFlowNodePromise);
}
await Promise.all(nextFlowNodeExecutionPromises);
return resolve();
}
catch (error) {
return this.handleError(error, resolve, reject);
}
});
}
async resumeFromState(flowNodeInstance, processFlowNodeInstances) {
this.logger.debug(`Resuming FlowNodeInstance.`);
switch (flowNodeInstance.state) {
case processcube_engine_sdk_1.FlowNodeInstanceState.running:
this.logger.debug('Resuming Gateway.');
this.processToken = flowNodeInstance.startToken;
const nextFlowNodes = await this.executeHandler();
await this.persistOnExit();
return nextFlowNodes;
case processcube_engine_sdk_1.FlowNodeInstanceState.finished:
this.logger.debug('Gateway was already finished. Skipping ahead.');
this.processToken = flowNodeInstance.endToken;
return this.resumeAfterExit(processFlowNodeInstances);
case processcube_engine_sdk_1.FlowNodeInstanceState.error:
this.logger.error(`Cannot resume Gateway, because it previously exited with an error!`, {
err: flowNodeInstance.error,
});
throw flowNodeInstance.error;
case processcube_engine_sdk_1.FlowNodeInstanceState.terminated:
const terminatedError = new processcube_engine_sdk_1.InternalServerError(`Cannot resume Gateway, because it was terminated!`);
terminatedError.additionalInformation = {
processInstanceId: this.processInstance.getProcessInstanceId(),
correlationId: this.processInstance.getCorrelationId(),
};
this.logger.error(terminatedError.message);
throw terminatedError;
case processcube_engine_sdk_1.FlowNodeInstanceState.canceled:
this.logger.debug(`Cannot resume Gateway, because it was canceled.`);
return [];
default:
const invalidStateError = new processcube_engine_sdk_1.InternalServerError(`Cannot resume Gateway, because its state cannot be determined!`);
invalidStateError.additionalInformation = {
processInstanceId: this.processInstance.getProcessInstanceId(),
correlationId: this.processInstance.getCorrelationId(),
};
this.logger.error(invalidStateError.message);
throw invalidStateError;
}
}
async resumeAfterExit(allFlowNodeInstances) {
return this.processInstance.getProcessModelFacade().getNextFlowNodesFor(this.flowNode);
}
async persistOnTerminate(typeData) {
this.unresolved.forEach((r) => r());
await super.persistOnTerminate(typeData);
}
async persistOnError(error, typeData) {
this.unresolved.forEach((r) => r());
await super.persistOnError(error, typeData);
}
publishGatewayReachedNotification() {
const message = {
correlationId: this.processInstance.getCorrelationId(),
processDefinitionId: this.processInstance.getProcessDefinitionId(),
processModelId: this.processInstance.getProcessModelId(),
processModelName: this.processInstance.getProcessModelName(),
processInstanceId: this.processInstance.getProcessInstanceId(),
parentProcessInstanceId: this.processInstance.getParentProcessInstance()?.getProcessInstanceId(),
flowNodeId: this.flowNode.id,
flowNodeName: this.flowNode.name,
flowNodeType: this.flowNode.bpmnType,
flowNodeInstanceId: this.flowNodeInstanceId,
processInstanceOwner: this.processInstance.getOwner(),
currentToken: this.processToken,
previousFlowNodeInstanceId: this.previousFlowNodeInstanceId,
};
EventAggregator_1.default.publish(Contracts_1.eventAggregatorSettings.messagePaths.gatewayReached, message);
}
publishGatewayFinishedNotification() {
const message = {
correlationId: this.processInstance.getCorrelationId(),
processDefinitionId: this.processInstance.getProcessDefinitionId(),
processModelId: this.processInstance.getProcessModelId(),
embeddedProcessModelId: this.processInstance.getEmbeddedProcessModelId(),
processModelName: this.processInstance.getProcessModelName(),
processInstanceId: this.processInstance.getProcessInstanceId(),
parentProcessInstanceId: this.processInstance.getParentProcessInstance()?.getProcessInstanceId(),
flowNodeId: this.flowNode.id,
flowNodeName: this.flowNode.name,
flowNodeType: this.flowNode.bpmnType,
flowNodeInstanceId: this.flowNodeInstanceId,
processInstanceOwner: this.processInstance.getOwner(),
currentToken: this.processToken,
previousFlowNodeInstanceId: this.previousFlowNodeInstanceId,
};
EventAggregator_1.default.publish(Contracts_1.eventAggregatorSettings.messagePaths.gatewayFinished, message);
}
}
exports.GatewayHandler = GatewayHandler;
//# sourceMappingURL=GatewayHandler.js.map