@5minds/processcube_engine
Version:
The ProcessCube Engine. Stores and executes BPMNs.
332 lines • 17.4 kB
JavaScript
"use strict";
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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserTaskInstanceHandler = void 0;
const inversify_1 = require("inversify");
const processcube_engine_sdk_1 = require("@5minds/processcube_engine_sdk");
const index_1 = require("../../../Contracts/index");
const AbortablePromise_1 = require("../../../Tools/AbortablePromise");
const EventAggregator_1 = __importDefault(require("../../../Tools/EventAggregator"));
const ActivityInstanceHandler_1 = require("./ActivityInstanceHandler");
let UserTaskInstanceHandler = class UserTaskInstanceHandler extends ActivityInstanceHandler_1.ActivityInstanceHandler {
finishedByUserId;
userTaskSubscription;
receivedFinishTaskEventId;
loggerNamespace = 'user_task';
get userTask() {
return this.flowNode;
}
async afterExecute() {
await super.afterExecute();
EventAggregator_1.default.unsubscribe(this.userTaskSubscription);
if (this.receivedFinishTaskEventId) {
EventAggregator_1.default.acknowledgeEvent(this.receivedFinishTaskEventId);
}
}
async persistOnExit(dataObjectValues) {
await super.persistOnExit(dataObjectValues, {
type: index_1.FlowNodeInstanceDataTypes.userTask,
finishedByUserId: this.finishedByUserId,
});
this.publishUserTaskFinishedNotification();
}
async runHandler() {
this.logger.debug(`Executing UserTask instance.`);
const handlerPromise = new AbortablePromise_1.AbortablePromise(async (resolve, reject) => {
try {
const finishUserTaskMessage = await this.suspendAndWaitForUserTaskResult();
this.currentToken = finishUserTaskMessage?.userTaskResult ?? {};
this.logger.debug('Resuming UserTask instance with received input: ', {
payload: this.currentToken,
});
this.finishedByUserId = finishUserTaskMessage.finishedBy.userId;
const nextFlowNodeInfo = this.processInstance.getProcessModelFacade().getNextFlowNodesFor(this.userTask);
return resolve(nextFlowNodeInfo);
}
catch (error) {
this.logger.error('Failed to execute UserTask!', {
err: error,
});
return reject(error);
}
}, { signal: this.activityHandler.abortSignal });
return handlerPromise;
}
async resumeAfterSuspend() {
const handlerPromise = new AbortablePromise_1.AbortablePromise(async (resolve) => {
const waitForMessagePromise = this.waitForUserTaskResult();
this.publishUserTaskReachedNotification();
const finishUserTaskMessage = await waitForMessagePromise;
this.currentToken = finishUserTaskMessage?.userTaskResult ?? {};
this.logger.debug('Resuming UserTask instance with received input: ', {
payload: this.currentToken,
});
this.finishedByUserId = finishUserTaskMessage.finishedBy.userId;
const nextFlowNodeInfo = this.processInstance.getProcessModelFacade().getNextFlowNodesFor(this.userTask);
return resolve(nextFlowNodeInfo);
}, { signal: this.activityHandler.abortSignal });
return handlerPromise;
}
async suspendAndWaitForUserTaskResult() {
const userTaskConfig = await this.evaluateUserTaskConfig();
const waitForUserTaskResultPromise = this.waitForUserTaskResult();
await this.persistOnSuspend(undefined, {
type: index_1.FlowNodeInstanceDataTypes.userTask,
userTaskConfigModel: this.getUserTaskConfigModel(),
userTaskConfig: userTaskConfig,
assignedUserIds: userTaskConfig?.assignedUserIds,
});
this.publishUserTaskReachedNotification();
return waitForUserTaskResultPromise;
}
async evaluateAssignedUserIds() {
const definedAssignedUserIds = await this.parseDefinedAssignedUserIds();
if (this.isCustomAssignmentResolverRegistered()) {
return this.evaluateAssignedUserIdsWithCustomAssignmentResolver();
}
return definedAssignedUserIds;
}
async parseDefinedAssignedUserIds() {
const assignUserIdsExpression = this.userTask.assignedUsersConfig;
if (!assignUserIdsExpression) {
return;
}
const result = await this.evaluateAssignedUsersExpression(assignUserIdsExpression);
const resultAsArray = Array.isArray(result) ? result : [result];
return resultAsArray;
}
isCustomAssignmentResolverRegistered() {
return this.processInstance.getContainer().isBound(index_1.userTaskAssignmentResolverTag);
}
evaluateAssignedUserIdsWithCustomAssignmentResolver() {
const userTaskInstance = {
startToken: this.currentToken,
userTaskConfigModel: this.getUserTaskConfigModel(),
userTaskConfig: undefined,
correlationId: this.processInstance.getCorrelationId(),
flowNodeId: this.flowNode.id,
flowNodeInstanceId: this.flowNodeInstanceId,
flowNodeLane: this.flowNodeLane?.name,
flowNodeType: this.flowNode.bpmnType,
previousFlowNodeInstanceId: this.previousFlowNodeInstanceId,
processInstanceId: this.processInstance.getProcessInstanceId(),
parentProcessInstanceId: this.processInstance.getParentProcessInstance()?.getProcessInstanceId(),
processDefinitionId: this.processInstance.getProcessDefinitionId(),
processModelId: this.processInstance.getProcessModelId(),
embeddedProcessModelId: this.processInstance.getEmbeddedProcessModelId(),
ownerId: this.owner.userId,
state: processcube_engine_sdk_1.FlowNodeInstanceState[this.state],
flowNodeName: this.flowNode.name,
};
const resolver = this.processInstance.getContainer().get(index_1.userTaskAssignmentResolverTag);
return resolver(userTaskInstance);
}
async evaluateUserTaskFormFieldConfigurations() {
const parsedFormFields = [];
for (const formField of this.userTask.formFields) {
try {
const label = await this.evaluateFormFieldExpression(formField.label);
const defaultValue = await this.evaluateFormFieldExpression(formField.defaultValue);
const customForm = await this.evaluateFormFieldCustomForm(formField);
let parsedFormField = Object.assign({}, formField);
parsedFormField.label = label;
parsedFormField.defaultValue = defaultValue;
parsedFormField.customForm = customForm;
parsedFormFields.push(parsedFormField);
}
catch (error) {
const invalidFormFieldError = new processcube_engine_sdk_1.InternalServerError(`The configuration for FormField ${formField.id} is invalid.`, 'process');
invalidFormFieldError.additionalInformation = {
processModelId: this.processInstance.getProcessModelId(),
embeddedProcessModelId: this.processInstance.getEmbeddedProcessModelId(),
processInstanceId: this.processInstance.getProcessInstanceId(),
correlationId: this.processInstance.getCorrelationId(),
userTaskId: this.userTask.id,
userTaskInstanceId: this.flowNodeInstanceId,
invalidFormFieldId: formField.id,
currentToken: this.currentToken,
validationError: error.message,
originalError: error.additionalInformation?.originalError ?? error.message,
};
this.logger.error(invalidFormFieldError.message, { err: invalidFormFieldError });
throw invalidFormFieldError;
}
}
return parsedFormFields;
}
getUserTaskConfigModel() {
const rawUserTaskConfig = {
assignedUserIds: this.userTask.assignedUsersConfig,
formFields: this.userTask.formFields,
description: this.userTask.description,
finishedMessage: this.userTask.finishedMessage,
customForm: this.userTask.customForm,
};
return rawUserTaskConfig;
}
async evaluateUserTaskConfig() {
const assignedUserIds = await this.evaluateAssignedUserIds();
const customFormValue = await this.evaluateFormFieldExpression(this.userTask.customForm);
const userTaskFormFields = await this.evaluateUserTaskFormFieldConfigurations();
const convertedUserTaskConfig = {
assignedUserIds: assignedUserIds,
formFields: userTaskFormFields,
description: this.userTask.description,
finishedMessage: this.userTask.finishedMessage,
customForm: customFormValue,
};
return convertedUserTaskConfig;
}
async evaluateFormFieldExpression(expression) {
if (!expression) {
return;
}
try {
// this is necessary for text-based Form Fields, which can use ` characters.
const escapedExpression = expression.replace(/`/g, `\\\``);
return await this.executeExpression(`\`${escapedExpression}\``);
}
catch (error) {
const invalidExpressionError = new processcube_engine_sdk_1.InternalServerError(`Failed to execute expression \`${expression}\`.`, error.category);
this.logger.error(invalidExpressionError.message, { err: error });
invalidExpressionError.additionalInformation = {
originalError: error.message,
processInstanceId: this.processInstance.getProcessInstanceId(),
correlationId: this.processInstance.getCorrelationId(),
};
throw invalidExpressionError;
}
}
async evaluateFormFieldCustomForm(formField) {
if (!formField.customForm) {
return;
}
try {
const sanitizedCustomForm = {};
await Promise.all(Object.entries(formField.parsedCustomForm).map(async ([key, value]) => {
if (key === 'entries' && value.length > 0) {
sanitizedCustomForm[key] = await Promise.all(value.map(async (entry) => {
return {
key: entry.key.replace(/`/g, `\\\``),
value: await this.executeExpression(`\`${entry.value.replace(/`/g, `\\\``)}\``),
};
}));
}
else if (typeof value === 'string') {
sanitizedCustomForm[key] = await this.executeExpression(`\`${value.replace(/`/g, `\\\``)}\``);
}
else {
sanitizedCustomForm[key] = value;
}
}));
return JSON.stringify(sanitizedCustomForm);
}
catch (error) {
const invalidExpressionError = new processcube_engine_sdk_1.InternalServerError(`Failed to execute expression \`${formField.customForm}\`.`, error.category);
this.logger.error(invalidExpressionError.message, { err: error });
invalidExpressionError.additionalInformation = {
originalError: error.message,
processInstanceId: this.processInstance.getProcessInstanceId(),
correlationId: this.processInstance.getCorrelationId(),
};
throw invalidExpressionError;
}
}
async evaluateAssignedUsersExpression(expression) {
if (!expression) {
return;
}
try {
const assignedUsers = await this.executeExpression(expression);
if (Array.isArray(assignedUsers)) {
const filteredAssignedUsers = assignedUsers.filter((user) => user && typeof user === 'string');
return filteredAssignedUsers;
}
return assignedUsers;
}
catch (error) {
const invalidExpressionError = new processcube_engine_sdk_1.InternalServerError(`Failed to execute expression \`${expression}\`.`, error.category);
invalidExpressionError.additionalInformation = {
originalError: error.message,
processInstanceId: this.processInstance.getProcessInstanceId(),
correlationId: this.processInstance.getCorrelationId(),
};
this.logger.error(invalidExpressionError.message, { err: error });
throw invalidExpressionError;
}
}
async executeExpression(expression) {
return this.executeRuntimeExpressionOnInstanceContext({
expression: expression,
currentFlowNode: this.userTask,
currentToken: this.currentToken,
previousFlowNode: this.processInstance.getProcessModelFacade().getPreviousFlowNodesFor(this.flowNode)?.pop(),
allowNonObjectResults: true,
});
}
waitForUserTaskResult() {
return new Promise(async (resolve) => {
const finishUserTaskEvent = index_1.eventAggregatorSettings.messagePaths.finishUserTask.replace(index_1.eventAggregatorSettings.messageParams.flowNodeInstanceId, this.flowNodeInstanceId);
this.userTaskSubscription = EventAggregator_1.default.subscribeOnce(finishUserTaskEvent, (event) => {
this.receivedFinishTaskEventId = event.eventId;
resolve(event);
});
});
}
publishUserTaskReachedNotification() {
const message = {
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,
flowNodeType: this.flowNode.bpmnType,
flowNodeInstanceId: this.flowNodeInstanceId,
processInstanceOwner: this.processInstance.getOwner(),
currentToken: this.currentToken,
previousFlowNodeInstanceId: this.previousFlowNodeInstanceId,
};
EventAggregator_1.default.publish(index_1.eventAggregatorSettings.messagePaths.userTaskReached, message);
}
publishUserTaskFinishedNotification() {
const message = {
userTaskResult: this.currentToken,
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,
flowNodeType: this.flowNode.bpmnType,
flowNodeInstanceId: this.flowNodeInstanceId,
processInstanceOwner: this.processInstance.getOwner(),
currentToken: this.currentToken,
previousFlowNodeInstanceId: this.previousFlowNodeInstanceId,
};
// FlowNode-specific notification
const userTaskFinishedEvent = index_1.eventAggregatorSettings.messagePaths.userTaskWithInstanceIdFinished.replace(index_1.eventAggregatorSettings.messageParams.flowNodeInstanceId, this.flowNodeInstanceId);
EventAggregator_1.default.publish(userTaskFinishedEvent, message);
// Global notification
EventAggregator_1.default.publish(index_1.eventAggregatorSettings.messagePaths.userTaskFinished, message);
}
};
exports.UserTaskInstanceHandler = UserTaskInstanceHandler;
exports.UserTaskInstanceHandler = UserTaskInstanceHandler = __decorate([
(0, inversify_1.injectable)()
], UserTaskInstanceHandler);
//# sourceMappingURL=UserTaskInstanceHandler.js.map