@5minds/processcube_engine
Version:
The ProcessCube Engine. Stores and executes BPMNs.
331 lines • 16.9 kB
JavaScript
"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