@5minds/processcube_engine
Version:
The ProcessCube Engine. Stores and executes BPMNs.
644 lines • 35.7 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.FlowNodeHandler = void 0;
const dayjs_1 = __importDefault(require("dayjs"));
const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep"));
const uuid = __importStar(require("uuid"));
const processcube_engine_sdk_1 = require("@5minds/processcube_engine_sdk");
const index_1 = require("../../Contracts/InternalMessages/index");
const EventAggregator_1 = __importDefault(require("../../Tools/EventAggregator"));
class FlowNodeHandler {
flowNode;
processInstance;
set flowNodeInstanceId(value) {
this.loggingMetaData.flowNodeInstanceId = value;
this._flowNodeInstanceId = value;
}
get flowNodeInstanceId() {
return this._flowNodeInstanceId;
}
previousFlowNodeInstanceId;
state;
processToken;
processEventSubscriptions = [];
flowNodeHandlerFactory;
flowNodeInstanceAdapter;
eventMiddlewareHandler;
logger;
startedAt;
finishedAt;
_flowNodeInstanceId = undefined;
loggingMetaData = {};
interruptionCallback = () => { };
constructor(eventMiddlewareHandler, flowNodeHandlerFactory, flowNodeInstanceAdapter, flowNode, processInstance, namespace) {
this.flowNode = flowNode;
this.eventMiddlewareHandler = eventMiddlewareHandler;
this.flowNodeHandlerFactory = flowNodeHandlerFactory;
this.flowNodeInstanceAdapter = flowNodeInstanceAdapter;
this.flowNodeInstanceId = uuid.v4();
this.processToken = {};
this.state = processcube_engine_sdk_1.FlowNodeInstanceState.running;
this.processInstance = processInstance;
this.loggingMetaData.correlationId = this.processInstance.getCorrelationId();
this.loggingMetaData.processInstanceId = this.processInstance.getProcessInstanceId();
this.loggingMetaData.flowNodeId = this.flowNode.id;
this.loggingMetaData.flowNodeInstanceId = this.flowNodeInstanceId;
this.logger = new processcube_engine_sdk_1.Logger(namespace, this.loggingMetaData);
}
/**
* Gets the callback that gets called when the Flow Node was interrupted.
* This happens, when an interrupting Boundary Event was triggered, or the Process Instance gets terminated.
*/
get onInterruptedCallback() {
return this.interruptionCallback;
}
set onInterruptedCallback(value) {
this.interruptionCallback = value;
}
getInstanceId() {
return this.flowNodeInstanceId;
}
getFlowNode() {
return this.flowNode;
}
async cancel(processToken) {
await this.onInterruptedCallback(processToken);
await this.persistOnCancel();
await this.afterExecute();
}
async beforeExecute() {
this.subscribeToMiddlewareEvent();
}
async afterExecute() {
this.processEventSubscriptions.forEach((subscription) => EventAggregator_1.default.unsubscribe(subscription));
this.processEventSubscriptions = [];
}
/**
* Hook for resuming a Flow Node Handler that is currently in a suspended state.
*/
async resumeAfterSuspend(flowNodeInstance, resumptionCanceledCallback) {
return this.processInstance.getProcessModelFacade().getNextFlowNodesFor(this.flowNode);
}
/**
* Main hook for executing and a FlowNode type specific handler.
*/
async executeHandler(resumptionCanceledCallback) {
return this.processInstance.getProcessModelFacade().getNextFlowNodesFor(this.flowNode);
}
async persistOnEnter(previousFlowNodeInstanceIds, typeData) {
const previousFlowNodeInstanceId = previousFlowNodeInstanceIds && previousFlowNodeInstanceIds.length > 0 ? previousFlowNodeInstanceIds.join(';') : this.previousFlowNodeInstanceId;
const flowNodeLane = this.processInstance.getProcessModelFacade().getLaneForFlowNode(this.flowNode.id);
const createPersistOnEnterRequest = () => {
return {
flowNodeInstanceId: this.flowNodeInstanceId,
flowNodeId: this.flowNode.id,
flowNodeType: this.flowNode.bpmnType,
flowNodeName: this.flowNode.name,
flowNodeLane: flowNodeLane?.name ?? '',
eventType: this.flowNode.eventType,
correlationId: this.processInstance.getCorrelationId(),
processDefinitionId: this.processInstance.getProcessDefinitionId(),
processModelId: this.processInstance.getProcessModelId(),
embeddedProcessModelId: this.processInstance.getEmbeddedProcessModelId(),
processInstanceId: this.processInstance.getProcessInstanceId(),
ownerId: this.processInstance.getOwner().userId,
currentToken: this.processToken,
previousFlowNodeInstanceId: previousFlowNodeInstanceId,
parentProcessInstanceId: this.processInstance.getParentProcessInstance()?.getProcessInstanceId(),
flowNodeInstanceTypeData: typeData,
triggeredByFlowNodeInstanceId: typeData?.triggeredByFlowNodeInstanceId,
};
};
try {
await this.runPreScript();
const persistOnEnterRequest = createPersistOnEnterRequest();
await this.flowNodeInstanceAdapter.persistOnEnter(persistOnEnterRequest);
this.startedAt = dayjs_1.default.utc().toDate();
const metadata = await this.executeRuntimePropertyExpressionsOnFlowNode();
await this.logFlowNodePersistenceEvent(processcube_engine_sdk_1.EngineEventType.OnFlowNodeEntered, processcube_engine_sdk_1.LogLevel.debug, {
writtenCorrelationMetadata: metadata.correlationMetdata,
writtenProcessInstanceMetadata: metadata.processInstanceMetadata,
triggeredByFlowNodeInstanceId: typeData?.triggeredByFlowNodeInstanceId ?? undefined,
});
}
catch (error) {
// Note that we can only persist an error state, if the Flow Node Instance was already persisted through "persistOnEnter".
// Running this multiple times is safe, because a second persistOnEnter will do nothing but update the "previousFlowNodeInstanceId" property,
// which is a workaround for the Parallel Join Gateway.
const persistOnEnterRequest = createPersistOnEnterRequest();
await this.flowNodeInstanceAdapter.persistOnEnter(persistOnEnterRequest);
throw error;
}
}
async persistOnSuspend(updatedTokenPayload, typeData) {
this.state = processcube_engine_sdk_1.FlowNodeInstanceState.suspended;
const data = {
tokenPayload: updatedTokenPayload,
typeData: typeData,
};
await this.flowNodeInstanceAdapter.persistOnSuspend(this.flowNodeInstanceId, data);
await this.logFlowNodePersistenceEvent(processcube_engine_sdk_1.EngineEventType.OnFlowNodeSuspended, processcube_engine_sdk_1.LogLevel.debug);
}
async persistOnExit(typeData) {
this.state = processcube_engine_sdk_1.FlowNodeInstanceState.finished;
try {
await this.runPostScript();
const dataObjectValues = await this.runOutgoingDataObjectExpressions();
this.finishedAt = dayjs_1.default.utc().toDate();
const data = {
finishedAt: this.finishedAt,
tokenPayload: this.processToken,
typeData: typeData,
dataObjectValues: dataObjectValues,
triggeredByFlowNodeInstanceId: typeData?.triggeredByFlowNodeInstanceId,
};
await this.flowNodeInstanceAdapter.persistOnExit(this.flowNodeInstanceId, data);
await this.logFlowNodePersistenceEvent(processcube_engine_sdk_1.EngineEventType.OnFlowNodeExited, processcube_engine_sdk_1.LogLevel.debug, {
writtenDataObjectValues: dataObjectValues,
triggeredByFlowNodeInstanceId: typeData?.triggeredByFlowNodeInstanceId,
});
}
catch (error) {
this.state = processcube_engine_sdk_1.FlowNodeInstanceState.error;
// Errors during "persistOnExit" usually occur with post-script, or data object expression execution.
error.additionalInformation = {
processInstanceId: this.processInstance.getProcessInstanceId(),
correlationId: this.processInstance.getCorrelationId(),
};
const data = {
tokenPayload: this.processToken,
typeData: typeData,
error: error,
};
await this.flowNodeInstanceAdapter.persistOnError(this.flowNodeInstanceId, data);
await this.logFlowNodePersistenceEvent(processcube_engine_sdk_1.EngineEventType.OnFlowNodeError, processcube_engine_sdk_1.LogLevel.error);
throw error;
}
}
async persistOnCancel(typeData) {
this.state = processcube_engine_sdk_1.FlowNodeInstanceState.canceled;
this.finishedAt = dayjs_1.default.utc().toDate();
const data = {
finishedAt: this.finishedAt,
tokenPayload: this.processToken,
typeData: typeData,
};
await this.flowNodeInstanceAdapter.persistOnCancel(this.flowNodeInstanceId, data);
await this.logFlowNodePersistenceEvent(processcube_engine_sdk_1.EngineEventType.OnFlowNodeCanceled, processcube_engine_sdk_1.LogLevel.debug);
}
async persistOnTerminate(typeData) {
// This check is necessary, in case the Promise-Chain was broken further down the road.
if (!(this.state === processcube_engine_sdk_1.FlowNodeInstanceState.running || this.state === processcube_engine_sdk_1.FlowNodeInstanceState.suspended)) {
return;
}
this.state = processcube_engine_sdk_1.FlowNodeInstanceState.terminated;
this.finishedAt = dayjs_1.default.utc().toDate();
const data = {
finishedAt: this.finishedAt,
tokenPayload: this.processToken,
typeData: typeData,
};
await this.flowNodeInstanceAdapter.persistOnTerminate(this.flowNodeInstanceId, data);
await this.logFlowNodePersistenceEvent(processcube_engine_sdk_1.EngineEventType.OnFlowNodeTerminated, processcube_engine_sdk_1.LogLevel.error);
}
async persistOnError(error, typeData) {
// This check is necessary, in case the Promise-Chain was broken further down the road.
if (!(this.state === processcube_engine_sdk_1.FlowNodeInstanceState.running || this.state === processcube_engine_sdk_1.FlowNodeInstanceState.suspended)) {
return;
}
this.state = processcube_engine_sdk_1.FlowNodeInstanceState.error;
this.finishedAt = dayjs_1.default.utc().toDate();
const data = {
finishedAt: this.finishedAt,
tokenPayload: this.processToken,
error: error,
typeData: typeData,
};
await this.flowNodeInstanceAdapter.persistOnError(this.flowNodeInstanceId, data);
EventAggregator_1.default.publish(index_1.eventAggregatorSettings.messagePaths.activityError, {
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,
flowNodeLane: this.processInstance.getProcessModelFacade().getLaneForFlowNode(this.flowNode.id)?.name,
flowNodeInstanceId: this.flowNodeInstanceId,
processInstanceOwner: this.processInstance.getOwner(),
currentToken: this.processToken,
previousFlowNodeInstanceId: this.previousFlowNodeInstanceId,
error: {
name: error.name,
code: error.code,
message: error.message,
category: error.category,
fatal: error.fatal,
},
});
await this.logFlowNodePersistenceEvent(processcube_engine_sdk_1.EngineEventType.OnFlowNodeError, processcube_engine_sdk_1.LogLevel.error);
}
subscribeToProcessKilledEvent(rejectionFunction) {
return this.processInstance.onKillProcess(async (message) => {
const killerId = message?.killedBy?.userId ?? undefined;
const errorMsg = `Process was killed by user \`${killerId}\``;
await this.persistOnTerminate();
await this.onInterruptedCallback(this.processToken);
await this.afterExecute();
const processKilledError = new processcube_engine_sdk_1.GoneError(errorMsg);
processKilledError.additionalInformation = {
processInstanceWasTerminated: true,
killedBy: message.killedBy,
};
return rejectionFunction(processKilledError);
});
}
subscribeToProcessError(rejectionFunction) {
return this.processInstance.onProcessError(async (message) => {
const payloadIsDefined = message != undefined;
this.processToken = payloadIsDefined ? message.currentToken : {};
const error = new processcube_engine_sdk_1.InternalServerError('ProcessInstance encountered an error!');
error.additionalInformation = message.currentToken;
await this.onInterruptedCallback(this.processToken);
await this.persistOnTerminate();
await this.afterExecute();
return rejectionFunction(error);
});
}
subscribeToTerminateEndEventReached(resolveFunction) {
return this.processInstance.onTerminateEndEventReached(async (message) => {
this.processToken = message.currentToken ?? {};
this.logger.debug('Cancelling Flow Node Execution, because a Terminate End Event has been reached.');
await this.onInterruptedCallback(this.processToken);
await this.persistOnCancel();
await this.afterExecute();
return resolveFunction();
});
}
subscribeToMiddlewareEvent() {
const tokenPayloadChangeMessagePath = index_1.eventAggregatorSettings.messagePaths.tokenPayloadChange.replace(index_1.eventAggregatorSettings.messageParams.flowNodeInstanceId, this.flowNodeInstanceId);
const tokenPayloadChangeFinishedMessagePath = index_1.eventAggregatorSettings.messagePaths.tokenPayloadChangeFinished.replace(index_1.eventAggregatorSettings.messageParams.flowNodeInstanceId, this.flowNodeInstanceId);
this.processEventSubscriptions.push(EventAggregator_1.default.subscribe(tokenPayloadChangeMessagePath, async (tokenChangeEvent) => {
this.processToken = tokenChangeEvent.tokenPayload ? { ...tokenChangeEvent.tokenPayload } : {};
const tokenTypeToChange = tokenChangeEvent.eventType === processcube_engine_sdk_1.EngineEventType.OnFlowNodeExited ||
tokenChangeEvent.eventType === processcube_engine_sdk_1.EngineEventType.OnFlowNodeError ||
tokenChangeEvent.eventType === processcube_engine_sdk_1.EngineEventType.OnFlowNodeCanceled ||
tokenChangeEvent.eventType === processcube_engine_sdk_1.EngineEventType.OnFlowNodeTerminated
? 'endToken'
: 'startToken';
await this.flowNodeInstanceAdapter.changeProcessTokenPayload(this.flowNodeInstanceId, tokenTypeToChange, this.processToken);
EventAggregator_1.default.publish(tokenPayloadChangeFinishedMessagePath);
}));
}
async handleNextFlowNode(nextFlowNode, allFlowNodeInstancesToResume) {
const nextFlowNodeHandler = this.flowNodeHandlerFactory.create(nextFlowNode, this.processInstance);
const flowNodeInstanceToResume = allFlowNodeInstancesToResume ? this.findNextFlowNodeInstance(allFlowNodeInstancesToResume, nextFlowNode.id) : undefined;
if (flowNodeInstanceToResume) {
return nextFlowNodeHandler.resume(flowNodeInstanceToResume, allFlowNodeInstancesToResume);
}
return nextFlowNodeHandler.execute(this.flowNodeInstanceId, this.processToken);
}
findNextFlowNodeInstance(allFlowNodeInstances, nextFlowNodeId) {
return allFlowNodeInstances.find((instance) => {
// ParallelJoinGateways always have multiple "previousFlowNodeInstanceIds", separated by ";" (i.e.: ID1;ID2;ID3 etc)
const instanceFollowedCurrentFlowNode = instance.previousFlowNodeInstanceId?.indexOf(this.flowNodeInstanceId) > -1;
const flowNodeIdsMatch = instance.flowNodeId === nextFlowNodeId;
return instanceFollowedCurrentFlowNode && flowNodeIdsMatch;
});
}
async handleError(error, resolveCallback, rejectCallback) {
// This happens, when trying to suspend a Flow Node Instance that was already finished (in whatever way).
// We must not perform another state transition to "error", because that would just apply a different unintended state to it.
const isStateTransitionError = new RegExp(`^Cannot change state of Flow Node Instance \`${this.flowNodeInstanceId}\``, 'gi').test(error.message);
if (isStateTransitionError) {
this.logger.debug(error.message, { err: error });
return resolveCallback();
}
const isTerminationEvent = error.additionalInformation?.processInstanceWasTerminated === true;
if (isTerminationEvent) {
await this.persistOnTerminate();
}
else {
await this.persistOnError(error);
}
await this.afterExecute();
return rejectCallback(error);
}
ensureHasClaim() {
const processModelHasNoLanes = !this.processInstance.getProcessModelFacade().getProcessModelHasLanes();
const processWasStartedByRootAccessToken = this.processInstance.getStartedByRootAccessToken();
if (processModelHasNoLanes || processWasStartedByRootAccessToken) {
return;
}
const laneForFlowNode = this.processInstance.getProcessModelFacade().getLaneForFlowNode(this.flowNode.id);
const claimName = laneForFlowNode.name;
if (claimName) {
this.processInstance.ensureOwnerHasClaim(claimName);
}
}
/**
* Executes all expressions for updating some meta property on the containing ProcessInstance.
* For example "engine.setProcessInstanceMetadata.propertyName" or "engine.setCorrelationMetadata.propertyName".
*
* These expressions can be found within the FlowNode's extension properties.
*
* @async
*/
async executeRuntimePropertyExpressionsOnFlowNode() {
const correlationMetdata = await this.setCorrelationMetadata();
const processInstanceMetadata = await this.setProcessInstanceMetadata();
return {
correlationMetdata,
processInstanceMetadata,
};
}
async postEventToEventMiddlewares(payload) {
const runtimeExpressionParameters = this.processInstance.buildRuntimeExpressionDataForFlowNodeInstance({
currentFlowNode: this.flowNode,
currentToken: this.processToken,
previousFlowNode: this.processInstance.getProcessModelFacade().getPreviousFlowNodesFor(this.flowNode)?.pop(),
allowNonObjectResults: false,
});
await this.eventMiddlewareHandler.triggerEvent((0, lodash_clonedeep_1.default)(payload), runtimeExpressionParameters);
}
async setCorrelationMetadata() {
const correlationMetadataProperties = this.flowNode.extensionElements?.camundaExtensionProperties?.filter((property) => property.name.startsWith('engine.setCorrelationMetadata.')) ?? [];
if (!correlationMetadataProperties || correlationMetadataProperties.length == 0) {
return;
}
const changedMetaData = {};
for (const property of correlationMetadataProperties) {
if (!property.value || property.value.trim().length === 0) {
continue;
}
const metadataKey = property.name.replace('engine.setCorrelationMetadata.', '').trim();
if (!metadataKey) {
const correlationPropertyNameMissing = new processcube_engine_sdk_1.BadRequestError("Must provide a name with 'engine.setCorrelationMetadata' expressions!", 'process');
correlationPropertyNameMissing.additionalInformation = {
processInstanceId: this.processInstance.getProcessInstanceId(),
correlationId: this.processInstance.getCorrelationId(),
flowNodeId: this.flowNode.id,
flowNodeName: this.flowNode.name,
metaDataKey: metadataKey,
};
throw correlationPropertyNameMissing;
}
changedMetaData[metadataKey] = await this.processInstance.setCorrelationMetadataValue(metadataKey, property.value, this.flowNode, this.flowNodeInstanceId, this.processToken);
}
await this.publishCorrelationMetadataChangedNotification(changedMetaData);
return changedMetaData;
}
async setProcessInstanceMetadata() {
const processMetadataProperties = this.flowNode.extensionElements?.camundaExtensionProperties?.filter((property) => property.name.startsWith('engine.setProcessInstanceMetadata.')) ?? [];
if (!processMetadataProperties || processMetadataProperties.length == 0) {
return;
}
const changedMetaData = {};
for (const property of processMetadataProperties) {
if (!property.value || property.value.trim().length === 0) {
continue;
}
const metadataKey = property.name.replace('engine.setProcessInstanceMetadata.', '').trim();
if (!metadataKey) {
const processInstancePropertyNameMissing = new processcube_engine_sdk_1.BadRequestError("Must provide a name with 'engine.setProcessInstanceMetadata' expressions!", 'process');
processInstancePropertyNameMissing.additionalInformation = {
processInstanceId: this.processInstance.getProcessInstanceId(),
correlationId: this.processInstance.getCorrelationId(),
flowNodeId: this.flowNode.id,
flowNodeName: this.flowNode.name,
metaDataKey: metadataKey,
};
throw processInstancePropertyNameMissing;
}
changedMetaData[metadataKey] = await this.processInstance.setProcessInstanceMetadataValue(metadataKey, property.value, this.flowNode, this.flowNodeInstanceId, this.processToken);
}
this.publishProcessInstanceMetadataChangedNotification(changedMetaData);
return changedMetaData;
}
async runPreScript() {
const preScript = this.flowNode.extensionElements?.camundaExtensionProperties?.find((prop) => prop.name === 'engine.runPreScript')?.value;
if (!preScript) {
return;
}
try {
const result = await this.processInstance.executeRuntimeExpression({
expression: preScript,
currentFlowNode: this.flowNode,
currentToken: this.processToken,
previousFlowNode: this.processInstance.getProcessModelFacade().getPreviousFlowNodesFor(this.flowNode)?.pop(),
allowNonObjectResults: false,
});
this.processToken = result ?? {};
}
catch (error) {
const errorCategoryPrefix = error.category ? `Failure in ${error.category}: ` : '';
this.logger.error(`${errorCategoryPrefix}Error in Pre Script`, {
err: (0, processcube_engine_sdk_1.serializeJson)(error),
correlationId: this.processInstance.getCorrelationId(),
processInstanceId: this.processInstance.getProcessInstanceId(),
processModelId: this.processInstance.getProcessModelId(),
embeddedProcessModelId: this.processInstance.getEmbeddedProcessModelId(),
flowNodeId: this.flowNode.id,
flowNodeInstanceId: this.flowNodeInstanceId,
});
const sanitizedMessage = error.message.replace(errorCategoryPrefix, '');
const preScriptError = new processcube_engine_sdk_1.BadRequestError(`Failure in process: Error in Pre Script: ${sanitizedMessage}`, error.category);
preScriptError.additionalInformation = {
originalError: error.message,
correlationId: this.processInstance.getCorrelationId(),
processInstanceId: this.processInstance.getProcessInstanceId(),
processModelId: this.processInstance.getProcessModelId(),
embeddedProcessModelId: this.processInstance.getEmbeddedProcessModelId(),
flowNodeId: this.flowNode.id,
flowNodeInstanceId: this.flowNodeInstanceId,
};
throw preScriptError;
}
}
async runPostScript() {
const postScript = this.flowNode.extensionElements?.camundaExtensionProperties?.find((prop) => prop.name === 'engine.runPostScript')?.value;
if (!postScript) {
return;
}
try {
const result = await this.processInstance.executeRuntimeExpression({
expression: postScript,
currentFlowNode: this.flowNode,
currentToken: this.processToken,
previousFlowNode: this.processInstance.getProcessModelFacade().getPreviousFlowNodesFor(this.flowNode)?.pop(),
allowNonObjectResults: false,
});
this.processToken = result ?? {};
}
catch (error) {
const errorCategoryPrefix = error.category ? `Failure in ${error.category}: ` : '';
this.logger.error(`${errorCategoryPrefix}Error in Post Script`, {
err: (0, processcube_engine_sdk_1.serializeJson)(error),
correlationId: this.processInstance.getCorrelationId(),
processInstanceId: this.processInstance.getProcessInstanceId(),
processModelId: this.processInstance.getProcessModelId(),
embeddedProcessModelId: this.processInstance.getEmbeddedProcessModelId(),
flowNodeId: this.flowNode.id,
flowNodeInstanceId: this.flowNodeInstanceId,
});
const sanitizedMessage = error.message.replace(errorCategoryPrefix, '');
const postScriptError = new processcube_engine_sdk_1.BadRequestError(`Failure in process: Error in Post Script: ${sanitizedMessage}`, error.category);
postScriptError.additionalInformation = {
originalError: error.message,
correlationId: this.processInstance.getCorrelationId(),
processInstanceId: this.processInstance.getProcessInstanceId(),
processModelId: this.processInstance.getProcessModelId(),
embeddedProcessModelId: this.processInstance.getEmbeddedProcessModelId(),
flowNodeId: this.flowNode.id,
flowNodeInstanceId: this.flowNodeInstanceId,
};
throw postScriptError;
}
}
async runOutgoingDataObjectExpressions() {
const dataObjectOutputReferences = this.processInstance.getProcessModelFacade().getOutputDataObjectReferencesForFlowNode(this.flowNode.id);
if (dataObjectOutputReferences.length === 0) {
return;
}
const dataObjectValueCollection = {};
for (const dataObjectReference of dataObjectOutputReferences) {
const dataOutputAssociation = this.flowNode.dataOutputAssociations.find((entry) => entry.targetRef === dataObjectReference.id);
const dataObjectValue = dataOutputAssociation.dataSource?.length > 0 ? await this.parseDataObjectExpression(dataOutputAssociation.dataSource, dataObjectReference.id) : this.processToken;
dataObjectValueCollection[dataObjectReference.id] = dataObjectValue;
this.processInstance.getDataObjectFacade().storeValue(dataObjectReference.id, dataObjectValue);
}
return dataObjectValueCollection;
}
async parseDataObjectExpression(expression, dataObjectId) {
try {
const payload = await this.processInstance.executeRuntimeExpression({
expression: expression,
currentFlowNode: this.flowNode,
currentToken: this.processToken,
dataObjectId: dataObjectId,
allowNonObjectResults: false,
});
return payload;
}
catch (error) {
const parserError = new processcube_engine_sdk_1.BadRequestError(`Failed to execute DataObject expression!`, error.category);
const additionalInformation = {
expression: expression,
dataObjectId: dataObjectId,
currentDataObjectValue: this.processInstance.getDataObjectFacade().getValue(dataObjectId) ?? {},
correlationId: this.processInstance.getCorrelationId(),
flowNodeInstanceId: this.flowNodeInstanceId,
flowNodeId: this.flowNode.id,
processInstanceId: this.processInstance.getProcessInstanceId(),
originalErrorMessage: error.message,
};
parserError.additionalInformation = additionalInformation;
this.logger.error(parserError.message, additionalInformation);
throw parserError;
}
}
async logFlowNodePersistenceEvent(eventType, logLevel, additionalData = {}) {
const now = dayjs_1.default.utc().toDate();
const currentFlowNodeMetadata = processcube_engine_sdk_1.FlowNodeViewModelFactory.getViewModel(this.flowNode, {
processModelId: this.processInstance.getProcessModelId(),
processModelName: this.processInstance.getProcessModelName(),
});
await this.postEventToEventMiddlewares({
correlationId: this.processInstance.getCorrelationId(),
eventType: eventType,
flowNodeId: this.flowNode.id,
flowNodeInstanceId: this.flowNodeInstanceId,
flowNodeType: this.flowNode.bpmnType,
currentFlowNode: currentFlowNodeMetadata,
logLevel: logLevel,
processInstanceId: this.processInstance.getProcessInstanceId(),
parentProcessInstanceId: this.processInstance.getParentProcessInstance()?.getProcessInstanceId(),
processDefinitionId: this.processInstance.getProcessDefinitionId(),
processDefinitionHash: this.processInstance.getProcessDefinitionHash(),
processModelId: this.processInstance.getProcessModelId(),
embeddedProcessModelId: this.processInstance.getEmbeddedProcessModelId(),
processModelName: this.processInstance.getProcessModelName(),
timestamp: now,
flowNodeEnteredAt: this.startedAt,
flowNodeExitedAt: this.finishedAt,
processStartedAt: this.processInstance.getStartedAt(),
tokenPayload: this.processToken,
...additionalData,
});
}
publishProcessInstanceMetadataChangedNotification(changedMetadata) {
const message = {
correlationId: this.processInstance.getCorrelationId(),
processDefinitionId: this.processInstance.getProcessDefinitionId(),
processModelId: this.processInstance.getProcessModelId(),
processModelName: this.processInstance.getProcessModelName(),
embeddedProcessModelId: this.processInstance.getEmbeddedProcessModelId(),
processInstanceId: this.processInstance.getProcessInstanceId(),
flowNodeInstanceId: this.flowNodeInstanceId,
processInstanceOwner: this.processInstance.getOwner(),
currentToken: this.processToken ?? {},
changedMetadata: changedMetadata,
};
EventAggregator_1.default.publish(index_1.eventAggregatorSettings.messagePaths.processInstanceMetadataChanged, message);
}
async publishCorrelationMetadataChangedNotification(changedMetadata) {
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(),
flowNodeInstanceId: this.flowNodeInstanceId,
processInstanceOwner: this.processInstance.getOwner(),
currentToken: this.processToken ?? {},
changedMetadata: changedMetadata,
};
await new Promise((resolve) => {
const publishMetadataChangesToProcessInstances = index_1.eventAggregatorSettings.messagePaths.changeCorrelationMetadata.replace(index_1.eventAggregatorSettings.messageParams.correlationId, this.processInstance.getCorrelationId());
EventAggregator_1.default.publish(publishMetadataChangesToProcessInstances, message, resolve);
});
EventAggregator_1.default.publish(index_1.eventAggregatorSettings.messagePaths.correlationMetadataChanged, message);
}
}
exports.FlowNodeHandler = FlowNodeHandler;
//# sourceMappingURL=FlowNodeHandler.js.map