UNPKG

@5minds/processcube_engine

Version:

The ProcessCube Engine. Stores and executes BPMNs.

331 lines • 16.9 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 __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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BusinessRuleTaskInstanceHandler = void 0; 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 EventAggregator_1 = __importDefault(require("../../../Tools/EventAggregator")); const ActivityInstanceHandler_1 = require("./ActivityInstanceHandler"); /** * Basically a copy of the External Service Task Handler. * DMN functionality currently only exists in form of an extension, which uses the External Task Pattern to run DMNs through the Engine provided by that engine. */ class BusinessRuleTaskInstanceHandler extends ActivityInstanceHandler_1.ActivityInstanceHandler { loggerNamespace = 'business_rule_task_handler'; externalTaskDatabaseAdapter; externalTaskId; externalTaskFinishedSubscription; externalTaskExpiredSubscription; externalTaskFinishedEventId; get businessRuleTask() { return this.flowNode; } async persistOnExit(dataObjectValues) { await super.persistOnExit(dataObjectValues, this.getTypeDataForStateChange()); } async persistOnError(error) { await super.persistOnError(error, this.getTypeDataForStateChange()); } async persistOnTerminate() { await super.persistOnTerminate(this.getTypeDataForStateChange()); } async afterExecute() { await super.afterExecute(); if (this.externalTaskFinishedEventId) { EventAggregator_1.default.acknowledgeEvent(this.externalTaskFinishedEventId); } EventAggregator_1.default.unsubscribe(this.externalTaskExpiredSubscription); EventAggregator_1.default.unsubscribe(this.externalTaskFinishedSubscription); } async resumeAfterSuspend(flowNodeInstance) { this.logger.addToDefaultLogObject('externalTaskId', this.externalTaskId); this.logger.debug(`Executing BusinessRuleTask instance.`); const resumerPromise = new AbortablePromise_1.AbortablePromise(async (resolve, reject) => { const externalTask = await this.externalTaskDatabaseAdapter.findByFlowNodeInstanceId(flowNodeInstance.flowNodeInstanceId); if (!externalTask) { try { // No ExternalTask has been created yet. The handler is executed normally. const result = await this.executeExternalServiceTask(); this.currentToken = result; const nextFlowNode = this.processInstance.getProcessModelFacade().getNextFlowNodesFor(this.businessRuleTask); return resolve(nextFlowNode); } catch (error) { return reject(error); } } this.externalTaskId = externalTask.id; this.logger.addToDefaultLogObject('externalTaskId', this.externalTaskId); const processExternalTaskResult = async (payload) => { this.externalTaskFinishedEventId = payload.eventId; const errorFromPayload = payload.error; if (errorFromPayload) { this.logger.error('Failure in process: The external worker failed to process the BusinessRuleTask!', { err: errorFromPayload, }); this.currentToken = { errorMessage: errorFromPayload.message, errorCode: errorFromPayload.code, }; const sanitizedError = new processcube_engine_sdk_1.BpmnError(errorFromPayload.name, errorFromPayload.code, errorFromPayload.message); sanitizedError.additionalInformation = { errorDetails: errorFromPayload.errorDetails, }; sanitizedError.category = 'process'; reject(sanitizedError); } else { this.logger.debug('External processing of the BusinessRuleTask finished successfully.'); this.currentToken = payload.result ?? {}; return resolve(); } }; if (externalTask.state === processcube_engine_sdk_1.ExternalTaskState.finished) { processExternalTaskResult({ error: externalTask.error, result: externalTask.result, eventId: undefined }); } else { this.waitForExternalTaskResult(processExternalTaskResult); } }, { signal: this.activityHandler.abortSignal }); return resumerPromise; } async runHandler() { const handlerPromise = new AbortablePromise_1.AbortablePromise(async (resolve, reject) => { try { this.logger.debug('Executing external BusinessRuleTask'); await this.configureExternalTask(); await this.persistOnSuspend(this.currentToken, { ...this.getTypeDataForStateChange(), state: processcube_engine_sdk_1.ExternalTaskState.pending, }); const result = await this.executeExternalServiceTask(); this.currentToken = result; return resolve(); } catch (error) { return reject(error); } }, { signal: this.activityHandler.abortSignal }); return handlerPromise; } async configureExternalTask() { const hasNoTopic = !(this.businessRuleTask.topic?.length > 0); if (hasNoTopic) { const noTopicError = new processcube_engine_sdk_1.UnprocessableEntityError(`BusinessRule task ${this.businessRuleTask.id} has no topic!`, 'process'); noTopicError.additionalInformation = { serviceTask: this.businessRuleTask, processInstanceId: this.processInstance.getProcessInstanceId(), correlationId: this.processInstance.getCorrelationId(), }; throw noTopicError; } await this.parseExternalTaskTopic(); await this.parseExternalTaskPayload(); this.externalTaskId = uuid.v4(); } async parseExternalTaskTopic() { try { this.businessRuleTask.topic = await this.executeExpression(`\`${this.businessRuleTask.topic}\``, true); } catch (error) { const errorMessage = `BusinessRule topic '${this.businessRuleTask.topic}' is invalid!`; const invalidTopicError = new processcube_engine_sdk_1.InternalServerError(errorMessage, error.category); this.logger.error(invalidTopicError.message, { err: error, }); invalidTopicError.additionalInformation = { ...this.getDataForErrors(), originalError: error.message, }; throw invalidTopicError; } } async parseExternalTaskPayload() { if (!this.businessRuleTask.payload || this.businessRuleTask.payload.trim().length === 0) { return; } try { this.currentToken = await this.executeExpression(this.businessRuleTask.payload, false); } catch (error) { const errorMessage = `BusinessRuleTask payload configuration '${this.businessRuleTask.payload}' is invalid!`; const invalidPayloadError = new processcube_engine_sdk_1.InternalServerError(errorMessage, error.category); this.logger.error(invalidPayloadError.message, { err: error, }); invalidPayloadError.additionalInformation = { ...this.getDataForErrors(), originalError: error.message, }; throw invalidPayloadError; } } getTypeDataForStateChange() { if (!this.externalTaskId) { return; } return { type: index_1.FlowNodeInstanceDataTypes.externalServiceTask, externalTaskId: this.externalTaskId, topic: this.businessRuleTask.topic, isSingleTry: false, state: processcube_engine_sdk_1.ExternalTaskState.finished, }; } async executeExternalServiceTask() { return new AbortablePromise_1.AbortablePromise(async (resolve, reject) => { try { const externalTaskFinishedCallback = async (payload) => { this.externalTaskFinishedEventId = payload.eventId; const errorFromPayload = payload.error; if (errorFromPayload) { this.logger.error('Failure in process: The external worker failed to process the BusinessRuleTask!', { err: errorFromPayload, }); this.currentToken = { errorMessage: errorFromPayload.message, errorCode: errorFromPayload.code, }; const sanitizedError = new processcube_engine_sdk_1.BpmnError(errorFromPayload.name, errorFromPayload.code, errorFromPayload.message); sanitizedError.additionalInformation = { errorDetails: errorFromPayload.errorDetails, }; sanitizedError.category = 'process'; reject(sanitizedError); } else { const result = payload.result; const resultIsAnObject = result != undefined && typeof result === 'object' && (result.toString() === '[object Object]' || Array.isArray(result)); if (!resultIsAnObject) { const error = new processcube_engine_sdk_1.BadRequestError('The result returned by the External Task Worker uses an unexpected format. Results must be provided as JSON Object or Array.', 'process'); error.additionalInformation = { ...this.getDataForErrors(), externalTaskResult: result, }; this.logger.error(error.message, { err: error, }); return reject(error); } this.logger.debug('The external worker successfully finished processing the BusinessRuleTask.'); this.currentToken = result; resolve(result); } }; this.waitForExternalTaskResult(externalTaskFinishedCallback); this.publishExternalTaskCreatedNotification(); this.logger.debug('Waiting for external BusinessRuleTask to be finished by an external worker.'); } catch (error) { this.logger.error('Failure in process: Failed to execute external BusinessRuleTask!', { err: error, }); reject(error); } }, { signal: this.activityHandler.abortSignal }); } waitForExternalTaskResult(resolveFunc) { const externalTaskExpiredMessage = index_2.eventAggregatorSettings.messagePaths.externalTaskExpired; const externalTaskFinishedEventName = index_2.eventAggregatorSettings.messagePaths.externalTaskFinished.replace(index_2.eventAggregatorSettings.messageParams.externalTaskId, this.externalTaskId); this.externalTaskExpiredSubscription = EventAggregator_1.default.subscribe(externalTaskExpiredMessage, async (message) => { if (message.externalTaskId !== this.externalTaskId) { return; } EventAggregator_1.default.unsubscribe(this.externalTaskExpiredSubscription); EventAggregator_1.default.unsubscribe(this.externalTaskFinishedSubscription); const externalTaskExpiredError = new processcube_engine_sdk_1.RequestTimeoutError('BusinessRuleTask has expired.'); externalTaskExpiredError.additionalInformation = { ...this.getDataForErrors(), externalTaskId: this.externalTaskId, }; resolveFunc({ error: externalTaskExpiredError }); }); this.externalTaskFinishedSubscription = EventAggregator_1.default.subscribeOnce(externalTaskFinishedEventName, async (message) => { EventAggregator_1.default.unsubscribe(this.externalTaskExpiredSubscription); EventAggregator_1.default.unsubscribe(this.externalTaskFinishedSubscription); resolveFunc(message); }); } async executeExpression(expression, allowNonObjectResults) { return this.executeRuntimeExpressionOnInstanceContext({ expression: expression, currentFlowNode: this.businessRuleTask, currentToken: this.currentToken, previousFlowNode: this.processInstance.getProcessModelFacade().getPreviousFlowNodesFor(this.flowNode)?.pop(), allowNonObjectResults: allowNonObjectResults, }); } publishExternalTaskCreatedNotification() { const externalTaskCreatedEventName = index_2.eventAggregatorSettings.messagePaths.externalTaskCreated; const payload = { correlationId: this.processInstance.getCorrelationId(), processDefinitionId: this.processInstance.getProcessDefinitionId(), processModelId: this.processInstance.getProcessModelId(), processModelName: this.processInstance.getProcessModelName(), processInstanceId: this.processInstance.getProcessInstanceId(), embeddedProcessModelId: this.processInstance.getEmbeddedProcessModelId(), parentProcessInstanceId: this.processInstance.getParentProcessInstance()?.getProcessInstanceId(), flowNodeId: this.flowNode.id, flowNodeName: this.flowNode.name, flowNodeInstanceId: this.flowNodeInstanceId, processInstanceOwner: this.processInstance.getOwner(), currentToken: this.currentToken ?? {}, externalTaskId: this.externalTaskId, topic: this.businessRuleTask.topic, previousFlowNodeInstanceId: this.previousFlowNodeInstanceId, isSingleTry: false, }; EventAggregator_1.default.publish(externalTaskCreatedEventName, payload); } getDataForErrors() { return { flowNodeId: this.flowNode.id, flowNodeName: this.flowNode.name, flowNodeInstanceId: this.flowNodeInstanceId, processModelId: this.processInstance.getProcessModelId(), embeddedProcessModelId: this.processInstance.getEmbeddedProcessModelId(), processInstanceId: this.processInstance.getProcessInstanceId(), correlationId: this.processInstance.getCorrelationId(), }; } } exports.BusinessRuleTaskInstanceHandler = BusinessRuleTaskInstanceHandler; //# sourceMappingURL=BusinessRuleTaskInstanceHandler.js.map