UNPKG

@5minds/processcube_engine

Version:

The ProcessCube Engine. Stores and executes BPMNs.

354 lines • 20.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); 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 __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SubProcessInstanceHandler = void 0; const dayjs_1 = __importDefault(require("dayjs")); const inversify_1 = require("inversify"); const uuid = __importStar(require("uuid")); const processcube_engine_sdk_1 = require("@5minds/processcube_engine_sdk"); const index_1 = require("../../../Contracts/InternalDataModels/index"); const index_2 = require("../../../Contracts/InternalMessages/index"); const AbortablePromise_1 = require("../../../Tools/AbortablePromise"); const FlowNodeInstanceAdapter_1 = require("../../../Tools/DatabaseAdaptersSequelize/FlowNodeInstanceAdapter"); const ProcessInstanceAdapter_1 = require("../../../Tools/DatabaseAdaptersSequelize/ProcessInstanceAdapter"); const EventAggregator_1 = __importDefault(require("../../../Tools/EventAggregator")); const EventMiddlewareHandler_1 = require("../../../Tools/EventMiddlewareHandler"); const IdentityService_1 = require("../../../Tools/Iam/IdentityService"); const CoreAccessService_1 = require("../../CoreAccessService"); const ProcessInstance_1 = require("../../ProcessInstance"); const ActivityHandler_1 = require("./ActivityHandler"); const ActivityInstanceHandler_1 = require("./ActivityInstanceHandler"); let SubProcessInstanceHandler = class SubProcessInstanceHandler extends ActivityInstanceHandler_1.ActivityInstanceHandler { coreAccess; identityService; processInstanceDatabaseAdapter; processInstanceFactory; childProcessInstance; killedSubscription; endSubscription; erroredSubscription; loggerNamespace = 'sub_process'; constructor(coreAccess, eventMiddlewareHandler, flowNodeInstanceDatabaseAdapter, identityService, processInstanceDatabaseAdapter, processInstanceFactory, activityHandler, processInstance, previousFlowNodeInstanceId, instanceContext) { super(eventMiddlewareHandler, flowNodeInstanceDatabaseAdapter, activityHandler, processInstance, previousFlowNodeInstanceId, instanceContext); this.identityService = identityService; this.coreAccess = coreAccess; this.processInstanceDatabaseAdapter = processInstanceDatabaseAdapter; this.processInstanceFactory = processInstanceFactory; } get subProcess() { return this.flowNode; } async afterExecute() { await super.afterExecute(); EventAggregator_1.default.unsubscribe(this.erroredSubscription); EventAggregator_1.default.unsubscribe(this.endSubscription); EventAggregator_1.default.unsubscribe(this.killedSubscription); if (this.childProcessInstance && this.childProcessInstance.getState() === processcube_engine_sdk_1.ProcessInstanceState.running) { await this.coreAccess.killProcessInstance(this.processInstance.getOwner(), this.childProcessInstance.getProcessInstanceId()); } } async resumeAfterSuspend(subProcessInstance) { const handlerPromise = new AbortablePromise_1.AbortablePromise(async (resolve, reject) => { try { let subProcessResult; let subProcessInstanceData; if (subProcessInstance.childProcessInstanceId) { const subProcessInstances = await this.processInstanceDatabaseAdapter.getProcessInstances([subProcessInstance.childProcessInstanceId]); subProcessInstanceData = subProcessInstances[0]; } if (subProcessInstanceData != undefined) { subProcessResult = await this.resumeChildProcessInstance(subProcessInstance, subProcessInstanceData); } else { subProcessResult = await this.executeTargetProcess(subProcessInstance.childProcessInstanceId, this.processInstance.getProcessModelId(), subProcessInstance.startEventId ?? this.subProcess.flowNodes.find((node) => node.bpmnType === processcube_engine_sdk_1.BpmnType.startEvent)?.id, subProcessInstance.flowNodeId); } await this.postEventToEventMiddlewares({ correlationId: this.processInstance.getCorrelationId(), processModelId: this.processInstance.getProcessModelId(), embeddedProcessModelId: this.processInstance.getEmbeddedProcessModelId(), processModelName: this.processInstance.getProcessModelName(), processInstanceId: this.processInstance.getProcessInstanceId(), parentProcessInstanceId: this.processInstance.getParentProcessInstance()?.getProcessInstanceId(), processDefinitionId: this.processInstance.getProcessDefinitionId(), processDefinitionHash: this.processInstance.getProcessDefinitionHash(), flowNodeInstanceId: this.flowNodeInstanceId, flowNodeType: this.flowNode.bpmnType, flowNodeId: this.flowNode.id, logLevel: processcube_engine_sdk_1.LogLevel.debug, eventType: processcube_engine_sdk_1.EngineEventType.OnFlowNodeCallActivityResponseReceived, tokenPayload: this.currentToken, timestamp: dayjs_1.default.utc().toDate(), }); this.currentToken = this.createTokenFromSubProcessResult(subProcessResult); return resolve(); } catch (error) { this.currentToken = { error: error.message, additionalInformation: error.additionalInformation, }; const isTerminationEvent = error.additionalInformation?.processInstanceWasTerminated === true; if (isTerminationEvent) { this.logger.warn('The embedded SubProcess was terminated', { err: error, }); this.killProcessInstance(); } else { this.logger.error('Failed to execute embedded SubProcess', { err: error, }); } return reject(error); } }, { signal: this.activityHandler.abortSignal }); return handlerPromise; } async runHandler() { this.logger.debug(`Executing SubProcess instance.`); const handlerPromise = new AbortablePromise_1.AbortablePromise(async (resolve, reject) => { try { const startEvents = this.subProcess.flowNodes.filter((flowNode) => flowNode.bpmnType === processcube_engine_sdk_1.BpmnType.startEvent); if (!startEvents || startEvents.length === 0) { // This is not correct. Subprocesses (and also regular processes), are allowed to have no StartEvents. (See: Parallel Box SubProcess) // In this case, all Activities and Gateways without incoming SequenceFlows should be started parallel. // The current implementation of process instances does not support doing something like this. const errorToThrow = new processcube_engine_sdk_1.BadRequestError(`Embedded SubProcess \`${this.subProcess.id}\` has no Start Events.`, 'process'); errorToThrow.additionalInformation = { processInstanceId: this.processInstance.getProcessInstanceId(), correlationId: this.processInstance.getCorrelationId(), }; throw errorToThrow; } const startEventIdToUse = startEvents[0].id; const targetProcessModelId = this.processInstance.getProcessModelId(); const subProcessResult = await this.suspendHandlerAndExecuteTargetProcess(targetProcessModelId, startEventIdToUse); this.currentToken = this.createTokenFromSubProcessResult(subProcessResult); await this.postEventToEventMiddlewares({ correlationId: this.processInstance.getCorrelationId(), processModelId: this.processInstance.getProcessModelId(), embeddedProcessModelId: this.processInstance.getEmbeddedProcessModelId(), processModelName: this.processInstance.getProcessModelName(), processInstanceId: this.processInstance.getProcessInstanceId(), parentProcessInstanceId: this.processInstance.getParentProcessInstance()?.getProcessInstanceId(), processDefinitionId: this.processInstance.getProcessDefinitionId(), processDefinitionHash: this.processInstance.getProcessDefinitionHash(), flowNodeInstanceId: this.flowNodeInstanceId, flowNodeType: this.flowNode.bpmnType, flowNodeId: this.flowNode.id, logLevel: processcube_engine_sdk_1.LogLevel.debug, eventType: processcube_engine_sdk_1.EngineEventType.OnFlowNodeCallActivityResponseReceived, tokenPayload: this.currentToken, timestamp: dayjs_1.default.utc().toDate(), }); return resolve(); } catch (error) { this.currentToken = { error: error.message, additionalInformation: error.additionalInformation, }; const isTerminationEvent = error.additionalInformation?.processInstanceWasTerminated === true; if (isTerminationEvent) { this.logger.warn('The SubProcess was terminated', { err: error, }); this.killProcessInstance(); return reject(); } else { this.logger.error('Failed to execute SubProcess', { err: error, }); return reject(error); } } }, { signal: this.activityHandler.abortSignal }); return handlerPromise; } async suspendHandlerAndExecuteTargetProcess(processModelId, startEventId) { const childProcessInstanceId = uuid.v4(); await this.persistOnSuspend(this.currentToken, { type: index_1.FlowNodeInstanceDataTypes.subprocess, childProcessInstanceId: childProcessInstanceId, startEventId: startEventId, }); return this.executeTargetProcess(childProcessInstanceId, processModelId, startEventId, this.subProcess.id); } async executeTargetProcess(childProcessInstanceId, processModelId, startEventId, embeddedProcessModelId) { const options = { processModelId: processModelId, startEventId: startEventId, initialToken: this.currentToken, processInstanceId: childProcessInstanceId, correlationId: this.processInstance.getCorrelationId(), parentProcessInstanceId: this.processInstance.getProcessInstanceId(), embeddedProcessModelId: embeddedProcessModelId, parentDataObjectFacade: this.processInstance.getDataObjectFacade(), parentProcessModelFacade: this.processInstance.getProcessModelFacade(), triggeredByFlowNodeInstanceId: this.flowNodeInstanceId, }; this.childProcessInstance = this.processInstanceFactory({ processInstanceId: childProcessInstanceId, parentProcessInstance: this.processInstance }); return new Promise(async (resolve, reject) => { this.subscribeToChildProcessEvents(resolve, reject); try { this.childProcessInstance.execute(this.getOwnerIdentity(), options); } catch (error) { EventAggregator_1.default.unsubscribe(this.endSubscription); EventAggregator_1.default.unsubscribe(this.erroredSubscription); EventAggregator_1.default.unsubscribe(this.killedSubscription); reject(error); } }); } async resumeChildProcessInstance(subProcessInstance, childProcessInstanceData) { if (childProcessInstanceData.state === processcube_engine_sdk_1.ProcessInstanceState.finished) { this.logger.debug("Child Process Instance was already finished. Skipping resumption and continuing with the Child Process Instance's result."); const endEventList = await this.flowNodeInstanceDatabaseAdapter.query({ processInstanceId: childProcessInstanceData.processInstanceId, flowNodeType: processcube_engine_sdk_1.BpmnType.endEvent, }); const endEventOfChildProcessInstance = endEventList.flowNodeInstances[0]; return { flowNodeId: childProcessInstanceData.endEventId, flowNodeName: endEventOfChildProcessInstance?.flowNodeName, currentToken: childProcessInstanceData.endToken, }; } if (childProcessInstanceData.state === processcube_engine_sdk_1.ProcessInstanceState.error) { throw childProcessInstanceData.error; } if (childProcessInstanceData.state === processcube_engine_sdk_1.ProcessInstanceState.terminated) { const errorToThrow = childProcessInstanceData.error ?? new processcube_engine_sdk_1.BadRequestError('Process was terminated.'); errorToThrow.additionalInformation = { processInstanceWasTerminated: true, }; throw errorToThrow; } this.childProcessInstance = this.processInstanceFactory({ processInstanceId: subProcessInstance.childProcessInstanceId, parentProcessInstance: this.processInstance }); return new Promise(async (resolve, reject) => { this.subscribeToChildProcessEvents(resolve, reject); try { const flowNodeInstancesForChildProcess = await this.flowNodeInstanceDatabaseAdapter.query({ processInstanceId: subProcessInstance.childProcessInstanceId, }); this.childProcessInstance.resume(this.getOwnerIdentity(), childProcessInstanceData, flowNodeInstancesForChildProcess.flowNodeInstances, this.processInstance.getDataObjectFacade(), this.processInstance.getProcessModelFacade()); } catch (error) { EventAggregator_1.default.unsubscribe(this.endSubscription); EventAggregator_1.default.unsubscribe(this.erroredSubscription); EventAggregator_1.default.unsubscribe(this.killedSubscription); reject(error); } }); } subscribeToChildProcessEvents(resolveFunc, rejectFunc) { this.endSubscription = this.childProcessInstance.onProcessEnded((payload) => { EventAggregator_1.default.unsubscribe(this.erroredSubscription); EventAggregator_1.default.unsubscribe(this.killedSubscription); resolveFunc(payload); }); this.killedSubscription = this.childProcessInstance.onProcessKilled((payload) => { EventAggregator_1.default.unsubscribe(this.endSubscription); EventAggregator_1.default.unsubscribe(this.erroredSubscription); EventAggregator_1.default.unsubscribe(this.killedSubscription); rejectFunc(payload.currentToken); }); this.erroredSubscription = this.childProcessInstance.onProcessError((payload) => { EventAggregator_1.default.unsubscribe(this.killedSubscription); EventAggregator_1.default.unsubscribe(this.endSubscription); rejectFunc(payload.currentToken); }); } createTokenFromSubProcessResult(result) { if (!result) { return {}; } return { result: result.currentToken, endEventId: result.flowNodeId, endEventType: result.eventType, endEventName: result.flowNodeName, }; } killProcessInstance() { const eventName = index_2.eventAggregatorSettings.messagePaths.killProcessInstanceWithId.replace(index_2.eventAggregatorSettings.messageParams.processInstanceId, this.processInstance.getProcessInstanceId()); const payload = { 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, flowNodeInstanceId: this.flowNodeInstanceId, processInstanceOwner: this.processInstance.getOwner(), currentToken: this.currentToken, previousFlowNodeInstanceId: this.previousFlowNodeInstanceId, }; EventAggregator_1.default.publish(eventName, payload); } getOwnerIdentity() { // This ensures that requests against external services, like DB adapters, or newly created sub process instances, use the most up to date root access token. const identityToUse = this.processInstance.getStartedByRootAccessToken() === true ? this.identityService.getRootAccessIdentity() : this.processInstance.getOwner(); return identityToUse; } }; exports.SubProcessInstanceHandler = SubProcessInstanceHandler; exports.SubProcessInstanceHandler = SubProcessInstanceHandler = __decorate([ (0, inversify_1.injectable)(), __metadata("design:paramtypes", [CoreAccessService_1.CoreAccessService, EventMiddlewareHandler_1.EventMiddlewareHandler, FlowNodeInstanceAdapter_1.FlowNodeInstanceDatabaseAdapter, IdentityService_1.IdentityService, ProcessInstanceAdapter_1.ProcessInstanceDatabaseAdapter, Function, ActivityHandler_1.ActivityHandler, ProcessInstance_1.ProcessInstance, String, Object]) ], SubProcessInstanceHandler); //# sourceMappingURL=SubProcessInstanceHandler.js.map