UNPKG

@5minds/processcube_engine

Version:

The ProcessCube Engine. Stores and executes BPMNs.

460 lines • 26.4 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.CallActivityInstanceHandler = 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 ProcessDefinitionMediator_1 = require("../../../Tools/ProcessDefinitionMediator"); const CoreAccessService_1 = require("../../CoreAccessService"); const ProcessInstance_1 = require("../../ProcessInstance"); const ActivityHandler_1 = require("./ActivityHandler"); const ActivityInstanceHandler_1 = require("./ActivityInstanceHandler"); let CallActivityInstanceHandler = class CallActivityInstanceHandler extends ActivityInstanceHandler_1.ActivityInstanceHandler { identityService; coreAccess; processDefinitionMediator; processInstanceDatabaseAdapter; processInstanceFactory; childProcessInstance; killedSubscription; endSubscription; erroredSubscription; loggerNamespace = 'call_activity'; constructor(coreAccess, eventMiddlewareHandler, flowNodeInstanceDatabaseAdapter, identityService, processDefinitionMediator, processInstanceDatabaseAdapter, processInstanceFactory, activityHandler, processInstance, previousFlowNodeInstanceId, instanceContext) { super(eventMiddlewareHandler, flowNodeInstanceDatabaseAdapter, activityHandler, processInstance, previousFlowNodeInstanceId, instanceContext); this.identityService = identityService; this.coreAccess = coreAccess; this.processDefinitionMediator = processDefinitionMediator; this.processInstanceDatabaseAdapter = processInstanceDatabaseAdapter; this.processInstanceFactory = processInstanceFactory; } get callActivity() { 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(callActivityInstance) { const handlerPromise = new AbortablePromise_1.AbortablePromise(async (resolve, reject) => { try { let callActivityResult; let childProcessInstanceData; if (callActivityInstance.childProcessInstanceId) { const childProcessInstances = await this.processInstanceDatabaseAdapter.getProcessInstances([callActivityInstance.childProcessInstanceId]); childProcessInstanceData = childProcessInstances[0]; } if (childProcessInstanceData != undefined) { callActivityResult = await this.resumeChildProcessInstance(callActivityInstance, childProcessInstanceData); } else { callActivityResult = await this.executeTargetProcess(callActivityInstance.childProcessInstanceId, callActivityInstance.targetProcessModelId ?? this.callActivity.targetProcessModelId, callActivityInstance.startEventId ?? this.callActivity.startEventId); } 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(), flowNodeId: this.flowNode.id, flowNodeName: this.flowNode.name, flowNodeType: this.flowNode.bpmnType, flowNodeLane: this.processInstance.getProcessModelFacade().getLaneForFlowNode(this.flowNode.id)?.name, flowNodeInstanceId: this.flowNodeInstanceId, 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.createTokenFromCallActivityResult(callActivityResult); return resolve(); } catch (error) { this.currentToken = { error: error.message, additionalInformation: error.additionalInformation, }; const isTerminationEvent = error.additionalInformation?.processInstanceWasTerminated === true; if (isTerminationEvent) { this.logger.warn('The child Process was terminated', { err: error, }); this.terminateProcessInstance(error); } else { this.logger.error('Failed to execute child Process', { err: error, }); } return reject(error); } }, { signal: this.activityHandler.abortSignal }); return handlerPromise; } async runHandler() { this.logger.debug(`Executing CallActivity instance.`); const handlerPromise = new AbortablePromise_1.AbortablePromise(async (resolve, reject) => { try { const targetProcessModelId = await this.getTargetProcessModelId(); const processDefinition = await this.processDefinitionMediator.getByProcessModelId(this.identityService.getInternalIdentity(), targetProcessModelId); const processModel = processDefinition?.processes?.find((process) => process.id === targetProcessModelId); if (!processModel) { const errorToThrow = new processcube_engine_sdk_1.NotFoundError(`Process Model \`${targetProcessModelId}\` not found.`, 'process'); errorToThrow.additionalInformation = { processInstanceId: this.processInstance.getProcessInstanceId(), correlationId: this.processInstance.getCorrelationId(), }; throw errorToThrow; } if (!processModel.isExecutable) { const errorToThrow = new processcube_engine_sdk_1.BadRequestError(`Process Model \`${processModel.id}\` is not executable.`, 'process'); errorToThrow.additionalInformation = { processInstanceId: this.processInstance.getProcessInstanceId(), correlationId: this.processInstance.getCorrelationId(), }; throw errorToThrow; } const startEvents = processModel.getStartEvents(); if (!startEvents || startEvents.length === 0) { const errorToThrow = new processcube_engine_sdk_1.BadRequestError(`Process Model \`${processModel.id}\` has no Start Events.`, 'process'); errorToThrow.additionalInformation = { processInstanceId: this.processInstance.getProcessInstanceId(), correlationId: this.processInstance.getCorrelationId(), }; throw errorToThrow; } const startEventIdToUse = await this.getTargetStartEventId(processModel); const noMatchingStartEvent = !startEvents.some((flowNode) => flowNode.id === startEventIdToUse); if (noMatchingStartEvent) { const errorToThrow = new processcube_engine_sdk_1.NotFoundError(`Start Event with ID \`${startEventIdToUse}\` not found on Process Model \`${processModel.id}\`.`, 'process'); errorToThrow.additionalInformation = { processInstanceId: this.processInstance.getProcessInstanceId(), correlationId: this.processInstance.getCorrelationId(), }; throw errorToThrow; } const callActivityResult = await this.suspendHandlerAndExecuteTargetProcess(targetProcessModelId, startEventIdToUse); this.currentToken = this.createTokenFromCallActivityResult(callActivityResult); 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(), flowNodeId: this.flowNode.id, flowNodeName: this.flowNode.name, flowNodeType: this.flowNode.bpmnType, flowNodeLane: this.processInstance.getProcessModelFacade().getLaneForFlowNode(this.flowNode.id)?.name, flowNodeInstanceId: this.flowNodeInstanceId, 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 child Process was terminated', { err: error, }); this.terminateProcessInstance(error); } else { this.logger.error('Failed to execute child Process', { err: error, }); } return reject(error); } }, { signal: this.activityHandler.abortSignal }); return handlerPromise; } async suspendHandlerAndExecuteTargetProcess(processModelId, startEventId) { const childProcessInstanceId = uuid.v4(); await this.createStartTokenForChildProcess(); await this.persistOnSuspend(this.currentToken, { type: index_1.FlowNodeInstanceDataTypes.callActivity, childProcessInstanceId: childProcessInstanceId, targetProcessModelId: processModelId, startEventId: startEventId, }); return this.executeTargetProcess(childProcessInstanceId, processModelId, startEventId); } async executeTargetProcess(childProcessInstanceId, processModelId, startEventId) { const options = { processModelId: processModelId, startEventId: startEventId, initialToken: this.currentToken, processInstanceId: childProcessInstanceId, correlationId: this.processInstance.getCorrelationId(), parentProcessInstanceId: this.processInstance.getProcessInstanceId(), }; 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(callActivityInstance, 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: callActivityInstance.childProcessInstanceId, parentProcessInstance: this.processInstance }); return new Promise(async (resolve, reject) => { this.subscribeToChildProcessEvents(resolve, reject); try { const flowNodeInstancesForChildProcess = await this.flowNodeInstanceDatabaseAdapter.query({ processInstanceId: callActivityInstance.childProcessInstanceId, }); this.childProcessInstance.resume(this.getOwnerIdentity(), childProcessInstanceData, flowNodeInstancesForChildProcess.flowNodeInstances); } catch (error) { EventAggregator_1.default.unsubscribe(this.endSubscription); EventAggregator_1.default.unsubscribe(this.erroredSubscription); EventAggregator_1.default.unsubscribe(this.killedSubscription); reject(error); } }); } async getTargetProcessModelId() { const calledReferenceIsNotExecutableExpression = !this.processInstance .getProcessTokenService() .getAvailableParametersForExpressions() .some((param) => this.callActivity.targetProcessModelId?.includes(param)); if (calledReferenceIsNotExecutableExpression) { return this.callActivity.targetProcessModelId; } try { return await this.executeExpression(`\`${this.callActivity.targetProcessModelId}\``, true); } catch (error) { this.createAndThrowExpressionError(`CallActivity calledReference Configuration '${this.callActivity.targetProcessModelId}' is invalid.`, error); } } async getTargetStartEventId(processModel) { if (!this.callActivity.startEventId) { const startEvents = processModel.getStartEvents(); if (startEvents.length > 1) { const error = new processcube_engine_sdk_1.BadRequestError('The target process has multiple start events, but none was selected. A Start Event must be chosen so that the Call Activity can start the process correctly.'); throw error; } return startEvents[0]?.id; } const startEventIdIsNotExecutableExpression = !this.processInstance .getProcessTokenService() .getAvailableParametersForExpressions() .some((param) => this.callActivity.startEventId.includes(param)); if (startEventIdIsNotExecutableExpression) { return this.callActivity.startEventId; } try { return await this.executeExpression(`\`${this.callActivity.startEventId}\``, true); } catch (error) { this.createAndThrowExpressionError(`CallActivity startEventId Configuration '${this.callActivity.startEventId}' is invalid.`, error); } } async createStartTokenForChildProcess() { if (!this.callActivity.payload || this.callActivity.payload.trim().length === 0) { return; } try { this.currentToken = await this.executeExpression(this.callActivity.payload, false, this.callActivity.dataContract); } catch (error) { this.createAndThrowExpressionError(`CallActivity payload configuration '${this.callActivity.payload}' is invalid.`, error); } } async executeExpression(expression, allowNonObjectResults, dataContract) { return this.executeRuntimeExpressionOnInstanceContext({ expression: expression, dataContract: dataContract, currentFlowNode: this.callActivity, currentToken: this.currentToken, previousFlowNode: this.processInstance.getProcessModelFacade().getPreviousFlowNodesFor(this.flowNode)?.pop(), allowNonObjectResults: allowNonObjectResults, }); } createAndThrowExpressionError(message, originalError) { const badExpressionError = new processcube_engine_sdk_1.BadRequestError(message, originalError.category); this.logger.error(badExpressionError.message, { err: originalError, }); badExpressionError.additionalInformation = { flowNodeId: this.flowNode.id, flowNodeName: this.flowNode.name, flowNodeInstanceId: this.flowNodeInstanceId, processModelId: this.processInstance.getProcessModelId(), embeddedProcessModelId: this.processInstance.getEmbeddedProcessModelId(), processInstance: this.processInstance.getProcessInstanceId(), correlationId: this.processInstance.getCorrelationId(), originalError: originalError, }; throw badExpressionError; } 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); }); } createTokenFromCallActivityResult(result) { if (!result) { return {}; } return { result: result.currentToken, endEventId: result.flowNodeId, endEventType: result.eventType, endEventName: result.flowNodeName, }; } terminateProcessInstance(error) { 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, killedBy: error?.additionalInformation?.killedBy ?? undefined, }; EventAggregator_1.default.publish(eventName, payload); } getOwnerIdentity() { // This ensures that requests against external services, like DB adapters, or newly created child 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.CallActivityInstanceHandler = CallActivityInstanceHandler; exports.CallActivityInstanceHandler = CallActivityInstanceHandler = __decorate([ (0, inversify_1.injectable)(), __metadata("design:paramtypes", [CoreAccessService_1.CoreAccessService, EventMiddlewareHandler_1.EventMiddlewareHandler, FlowNodeInstanceAdapter_1.FlowNodeInstanceDatabaseAdapter, IdentityService_1.IdentityService, ProcessDefinitionMediator_1.ProcessDefinitionMediator, ProcessInstanceAdapter_1.ProcessInstanceDatabaseAdapter, Function, ActivityHandler_1.ActivityHandler, ProcessInstance_1.ProcessInstance, String, Object]) ], CallActivityInstanceHandler); //# sourceMappingURL=CallActivityInstanceHandler.js.map