UNPKG

@5minds/processcube_engine

Version:

The ProcessCube Engine. Stores and executes BPMNs.

334 lines • 17.1 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.ExternalServiceTaskInstanceHandler = 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"); class ExternalServiceTaskInstanceHandler extends ActivityInstanceHandler_1.ActivityInstanceHandler { externalTaskDatabaseAdapter; externalTaskId; externalTaskFinishedSubscription; externalTaskExpiredSubscription; externalTaskFinishedEventId; loggerNamespace = 'external_service_task'; constructor(eventMiddlewareHandler, externalTaskDatabaseAdapter, flowNodeInstanceDatabaseAdapter, activityHandler, processInstance, previousFlowNodeInstanceId, instanceContext) { super(eventMiddlewareHandler, flowNodeInstanceDatabaseAdapter, activityHandler, processInstance, previousFlowNodeInstanceId, instanceContext); this.externalTaskDatabaseAdapter = externalTaskDatabaseAdapter; } get serviceTask() { 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) { const resumerPromise = new AbortablePromise_1.AbortablePromise(async (resolve, reject) => { let externalTask; try { await this.parseExternalTaskTopic(); externalTask = await this.externalTaskDatabaseAdapter.ensureExternalTaskForFlowNodeInstanceIdIsCreated({ externalTaskId: this.externalTaskId, topic: this.serviceTask.topic, isSingleTry: this.serviceTask.isSingleTry, state: processcube_engine_sdk_1.ExternalTaskState.pending, flowNodeInstanceId: flowNodeInstance.flowNodeInstanceId, }); } catch (err) { reject(err); return; } 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 ExternalTask!', { 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 ServiceTask 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() { this.logger.addToDefaultLogObject('externalTaskId', this.externalTaskId); this.logger.debug(`Executing external ServiceTask instance.`); const handlerPromise = new AbortablePromise_1.AbortablePromise(async (resolve, reject) => { try { this.logger.debug('Executing external ServiceTask'); 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.serviceTask.topic?.length > 0); if (hasNoTopic) { const noTopicError = new processcube_engine_sdk_1.UnprocessableEntityError(`External task ${this.serviceTask.id} has no topic!`, 'process'); noTopicError.additionalInformation = { serviceTask: this.serviceTask, processInstanceId: this.processInstance.getProcessInstanceId(), correlationId: this.processInstance.getCorrelationId(), }; throw noTopicError; } await this.parseExternalTaskTopic(); await this.parseExternalTaskPayload(); this.externalTaskId = uuid.v4(); } async parseExternalTaskTopic() { try { this.serviceTask.topic = await this.executeExpression(`\`${this.serviceTask.topic}\``, true); } catch (error) { const errorMessage = `ExternalTask topic '${this.serviceTask.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.serviceTask.payload || this.serviceTask.payload.trim().length === 0) { return; } try { this.currentToken = await this.executeExpression(this.serviceTask.payload, false, this.serviceTask.dataContract); } catch (error) { const errorMessage = `ExternalTask payload configuration '${this.serviceTask.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, }; throw invalidPayloadError; } } getTypeDataForStateChange() { if (!this.externalTaskId) { return; } return { type: index_1.FlowNodeInstanceDataTypes.externalServiceTask, externalTaskId: this.externalTaskId, topic: this.serviceTask.topic, isSingleTry: this.serviceTask.isSingleTry, 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 ExternalTask!', { 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 ExternalTask.'); this.currentToken = result; resolve(result); } }; this.waitForExternalTaskResult(externalTaskFinishedCallback); this.publishExternalTaskCreatedNotification(); this.logger.debug('Waiting for external ServiceTask to be finished by an external worker.'); } catch (error) { this.logger.error('Failure in process: Failed to execute external ServiceTask!', { 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('External Task 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, dataContract) { return this.executeRuntimeExpressionOnInstanceContext({ expression: expression, dataContract: dataContract, currentFlowNode: this.serviceTask, 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(), 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 ?? {}, externalTaskId: this.externalTaskId, topic: this.serviceTask.topic, previousFlowNodeInstanceId: this.previousFlowNodeInstanceId, isSingleTry: this.serviceTask.isSingleTry, }; 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.ExternalServiceTaskInstanceHandler = ExternalServiceTaskInstanceHandler; //# sourceMappingURL=ExternalServiceTaskInstanceHandler.js.map