UNPKG

@5minds/processcube_engine

Version:

The ProcessCube Engine. Stores and executes BPMNs.

332 lines • 17.4 kB
"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