@5minds/processcube_engine
Version:
The ProcessCube Engine. Stores and executes BPMNs.
952 lines • 54.2 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 __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 __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 };
};
var ProcessInstanceDatabaseAdapter_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProcessInstanceDatabaseAdapter = void 0;
const dayjs_1 = __importDefault(require("dayjs"));
const inversify_1 = require("inversify");
const sequelize_1 = require("sequelize");
const processcube_engine_sdk_1 = require("@5minds/processcube_engine_sdk");
const MonitoringManager_1 = require("../MonitoringManager");
const BaseDatabaseAdapter_1 = require("./BaseDatabaseAdapter");
const CorrelationAdapter_1 = require("./CorrelationAdapter");
const FlowNodeInstanceAdapter_1 = require("./FlowNodeInstanceAdapter");
const Compression_1 = require("./Lib/Compression");
const ProcessInstanceFactory_1 = require("./Lib/ProcessInstanceFactory");
const SearchQueryBuilder_1 = require("./Lib/SearchQueryBuilder");
const SequelizeConnectionManager_1 = require("./Lib/SequelizeConnectionManager");
const Models = __importStar(require("./Models/index"));
let ProcessInstanceDatabaseAdapter = ProcessInstanceDatabaseAdapter_1 = class ProcessInstanceDatabaseAdapter extends BaseDatabaseAdapter_1.DatabaseAdapter {
async initialize() {
await super.initialize('database_adapter:process_instances');
}
async create(request) {
return (0, SequelizeConnectionManager_1.executeWithRetry)(this.execCreate.bind(this, request), `create Process Instance for process model '${request.processModelId}'`);
}
async setMetadataValue(processInstanceId, metaDataKey, metaDataValue, flowNodeInstanceId) {
const processInstance = await (0, SequelizeConnectionManager_1.executeWithRetry)(async () => Models.ProcessInstanceModel.findOne({ where: { processInstanceId: processInstanceId, deleted: false } }), `find ProcessInstance with ID '${processInstanceId}'`);
if (!processInstance) {
throw new processcube_engine_sdk_1.NotFoundError(`ProcessInstance with ID \`${processInstanceId}\` not found.`);
}
await (0, SequelizeConnectionManager_1.executeWithRetry)(async () => {
await Models.ProcessInstanceMetadataModel.create({
processInstanceId: processInstanceId,
flowNodeInstanceId: flowNodeInstanceId,
key: metaDataKey,
value: metaDataValue,
});
}, `set metadata '${metaDataKey}' for Process Instance '${processInstanceId}'`);
}
async getMinimalRunningProcessInstances() {
const resumableProcessInstances = await (0, SequelizeConnectionManager_1.executeWithRetry)(async () => {
return Models.ProcessInstanceModel.findAll({
attributes: ['processInstanceId', 'state', 'parentProcessInstanceId'],
where: {
state: processcube_engine_sdk_1.ProcessInstanceState.running,
deleted: false,
},
});
}, 'get a trimmed down set of all running Process Instances');
return resumableProcessInstances.map((processInstance) => {
return {
processInstanceId: processInstance.processInstanceId,
state: processInstance.state,
parentProcessInstanceId: processInstance.parentProcessInstanceId,
};
});
}
async getProcessInstances(processInstanceIds) {
const whereClause = {
processInstanceId: {
[sequelize_1.Op.in]: processInstanceIds,
},
deleted: false,
};
return this.getUnorderedProcessInstanceList(whereClause);
}
async getDirectChildProcessInstanceIds(parentProcessInstanceId, parentFlowNodeType, authorizationQuery) {
let whereClause = {
parentProcessInstanceId: parentProcessInstanceId,
deleted: false,
};
const include = [];
if (parentFlowNodeType === processcube_engine_sdk_1.BpmnType.subProcess) {
whereClause['embeddedProcessModelId'] = {
[sequelize_1.Op.not]: null,
};
}
else if (parentFlowNodeType === processcube_engine_sdk_1.BpmnType.callActivity) {
whereClause['embeddedProcessModelId'] = {
[sequelize_1.Op.eq]: null,
};
}
if (authorizationQuery) {
whereClause[sequelize_1.Op.or] = this.buildAuthorizationFilters(authorizationQuery.allowedLanes, authorizationQuery.requestingUserId);
include.push({
model: Models.FlowNodeInstanceModel.scope(null),
as: 'flowNodeInstances',
attributes: [],
required: false,
});
}
const result = await this.getProcessInstanceIdsList(whereClause, include);
return result.map((pi) => pi.processInstanceId);
}
async getAllDescendantProcessInstanceIds(parentProcessInstanceId, parentFlowNodeType, authorizationQuery) {
const processInstance = await this.getProcessInstances([parentProcessInstanceId]);
if (processInstance.length === 0) {
return [];
}
const whereClause = {
correlationId: processInstance[0].correlationId,
createdAt: {
[sequelize_1.Op.gte]: processInstance[0].createdAt,
},
deleted: false,
};
const include = [];
if (parentFlowNodeType === processcube_engine_sdk_1.BpmnType.subProcess) {
whereClause['embeddedProcessModelId'] = {
[sequelize_1.Op.not]: null,
};
}
else if (parentFlowNodeType === processcube_engine_sdk_1.BpmnType.callActivity) {
whereClause['embeddedProcessModelId'] = {
[sequelize_1.Op.eq]: null,
};
}
if (authorizationQuery) {
whereClause[sequelize_1.Op.or] = this.buildAuthorizationFilters(authorizationQuery.allowedLanes, authorizationQuery.requestingUserId);
include.push({
model: Models.FlowNodeInstanceModel.scope(null),
as: 'flowNodeInstances',
attributes: [],
required: false,
});
}
let potentialDescendants = await this.getProcessInstanceIdsList(whereClause, include);
const childProcessInstanceIds = [];
let resultLength = null;
while (resultLength !== childProcessInstanceIds.length) {
resultLength = childProcessInstanceIds.length;
potentialDescendants = potentialDescendants.filter((pi) => {
if (pi.parentProcessInstanceId === parentProcessInstanceId || childProcessInstanceIds.includes(pi.parentProcessInstanceId)) {
childProcessInstanceIds.push(pi.processInstanceId);
return false;
}
return true;
});
}
return childProcessInstanceIds;
}
async getAncestorProcessInstanceIds(processInstanceId, onlyForEmbeddedProcesses, authorizationQuery) {
const processInstanceQuery = {
processInstanceId: processInstanceId,
};
if (authorizationQuery) {
processInstanceQuery.allowedLanes = authorizationQuery.allowedLanes;
processInstanceQuery.ownerId = authorizationQuery.requestingUserId;
}
const queryResult = await this.query(processInstanceQuery);
if (queryResult.processInstances.length === 0 ||
!queryResult.processInstances[0].parentProcessInstanceId ||
(onlyForEmbeddedProcesses && !queryResult.processInstances[0].embeddedProcessModelId)) {
return [];
}
const whereClause = {
correlationId: queryResult.processInstances[0].correlationId,
createdAt: {
[sequelize_1.Op.lte]: queryResult.processInstances[0].createdAt,
},
deleted: false,
};
const include = [];
if (onlyForEmbeddedProcesses) {
whereClause['embeddedProcessModelId'] = {
[sequelize_1.Op.not]: null,
};
}
if (authorizationQuery) {
whereClause[sequelize_1.Op.or] = this.buildAuthorizationFilters(authorizationQuery.allowedLanes, authorizationQuery.requestingUserId);
include.push({
model: Models.FlowNodeInstanceModel.scope(null),
as: 'flowNodeInstances',
attributes: [],
required: false,
});
}
let potentialAncestors = await this.getProcessInstanceIdsList(whereClause, include);
let resultLength = 0;
const result = [queryResult.processInstances[0].parentProcessInstanceId];
while (resultLength !== result.length) {
resultLength = result.length;
const ancestor = potentialAncestors.find((pi) => pi.processInstanceId === result[result.length - 1]);
if (ancestor && ancestor.parentProcessInstanceId) {
result.push(ancestor.parentProcessInstanceId);
}
}
return result.toReversed();
}
async getProcessInstancesByProcessDefinitionIdAndState(processDefinitionId, state) {
const whereClause = {
processDefinitionId: processDefinitionId,
state: state,
deleted: false,
};
return this.getUnorderedProcessInstanceList(whereClause);
}
async countRunningInstancesForModel(processModelId) {
return await (0, SequelizeConnectionManager_1.executeWithRetry)(async () => {
return Models.ProcessInstanceModel.count({
where: {
processModelId: processModelId,
state: processcube_engine_sdk_1.ProcessInstanceState.running,
deleted: false,
},
});
}, `Count running instances for process model ${processModelId}`);
}
async getRetryableChildProcessInstances(parentProcessInstanceIds) {
const whereClause = {
parentProcessInstanceId: {
[sequelize_1.Op.in]: parentProcessInstanceIds,
},
state: {
[sequelize_1.Op.or]: [processcube_engine_sdk_1.ProcessInstanceState.error, processcube_engine_sdk_1.ProcessInstanceState.terminated],
},
deleted: false,
};
return this.getUnorderedProcessInstanceList(whereClause);
}
async query(query, offset = 0, limit = 0, sort, includeXml = true, includeDeleted = false, includeCount = false) {
const { count, processInstanceIds } = await this.getProcessInstanceIdsToLoad(query, offset, limit, sort, includeDeleted, includeCount);
const functionReporter = MonitoringManager_1.MonitoringManager.getNewFunctionReporter('ProcessInstanceAdapter.query', {
queryKeys: Object.keys(query) ?? [],
offset,
limit,
includeXml,
includeDeleted,
includeCount,
}, { queryValues: Object.values(query) ?? [], processInstanceIdsCount: processInstanceIds.length });
const whereQueryForProcessInstanceIds = {
processInstanceId: {
[sequelize_1.Op.in]: processInstanceIds,
},
};
const request = {
where: whereQueryForProcessInstanceIds,
include: [
{
model: Models.CorrelationModel,
as: 'correlation',
attributes: ['correlationId'],
required: false,
include: [
{
model: Models.CorrelationMetadataModel,
as: 'correlationMetadata',
attributes: ['key', 'value', 'createdAt'],
required: false,
},
],
},
{
model: Models.ProcessInstanceMetadataModel,
as: 'processInstanceMetadata',
attributes: ['key', 'value', 'createdAt'],
required: false,
},
{
model: Models.FlowNodeInstanceModel,
as: 'triggeredByFlowNodeInstance',
required: false,
},
],
order: this.buildQueryOrderSettings(sort),
};
if (includeXml) {
request.include.push({
model: Models.ProcessDefinitionModel,
attributes: ['xml'],
required: false,
});
}
const results = await (0, SequelizeConnectionManager_1.executeWithRetry)(async () => Models.ProcessInstanceModel.findAll(request), 'query Process Instances');
const processInstances = [];
for (const rawProcessInstance of results) {
const processInstance = await (0, ProcessInstanceFactory_1.createProcessInstanceFromModel)(rawProcessInstance);
processInstances.push(processInstance);
}
functionReporter.finish();
return {
processInstances: processInstances,
totalCount: count,
};
}
async transferOwnership(processInstanceIds, newOwner) {
const transaction = await this.sequelizeInstance.transaction();
try {
await (0, SequelizeConnectionManager_1.executeWithRetry)(async () => Models.ProcessInstanceModel.update({
ownerId: newOwner.userId,
ownerToken: newOwner.token,
}, {
where: {
processInstanceId: { [sequelize_1.Op.in]: processInstanceIds },
deleted: false,
},
transaction: transaction,
}), `transfer ownership of Process Instances '${processInstanceIds.join(', ')}' to new owner ${newOwner.userId}`);
await (0, SequelizeConnectionManager_1.executeWithRetry)(async () => Models.FlowNodeInstanceModel.update({
ownerId: newOwner.userId,
}, {
where: {
processInstanceId: { [sequelize_1.Op.in]: processInstanceIds },
},
transaction: transaction,
}), `transfer ownership of Flow Node Instances belonging to Process Instances '${processInstanceIds.join(', ')}' to new owner ${newOwner.userId}`);
await transaction.commit();
}
catch (error) {
await transaction.rollback();
throw error;
}
}
async finish(processInstanceId, endEventData, includeFlowNodeInstances) {
const processInstance = await this.findProcessInstanceAndVerifyRunningState(processInstanceId);
const endTokenToEncode = endEventData?.endEventToken ?? {};
const encodedEndToken = this.config.compressProcessTokens ? undefined : JSON.stringify(endTokenToEncode);
const compressedEndToken = this.config.compressProcessTokens ? await (0, Compression_1.compressToken)(endTokenToEncode) : undefined;
processInstance.state = processcube_engine_sdk_1.ProcessInstanceState.finished;
processInstance.error = null; // This is important for retried Process Instances, which will have an error attached to them.
processInstance.endEventId = endEventData?.endEventId;
processInstance.endEventType = endEventData?.endEventType;
processInstance.endToken = encodedEndToken;
processInstance.endTokenCompressed = compressedEndToken;
this.calculateTotalRuntime(processInstance);
return (0, SequelizeConnectionManager_1.executeWithRetry)(async () => {
const transaction = await this.sequelizeInstance.transaction();
try {
await processInstance.save({ transaction });
if (includeFlowNodeInstances) {
await this.cleanupFlowNodeInstances(processInstanceId, transaction);
}
await transaction.commit();
}
catch (error) {
await transaction.rollback();
}
}, `finish Process Instance '${processInstanceId}'`);
}
async finishWithError(processInstanceId, error, endEventData, includeFlowNodeInstances) {
const processInstance = await this.findProcessInstanceAndVerifyRunningState(processInstanceId);
const endTokenToEncode = endEventData?.endEventToken ?? {};
const encodedEndToken = this.config.compressProcessTokens ? undefined : JSON.stringify(endTokenToEncode);
const compressedEndToken = this.config.compressProcessTokens ? await (0, Compression_1.compressToken)(endTokenToEncode) : undefined;
processInstance.state = processcube_engine_sdk_1.ProcessInstanceState.error;
processInstance.error = (0, processcube_engine_sdk_1.serializeJson)(error);
processInstance.endEventId = endEventData?.endEventId;
processInstance.endEventType = endEventData?.endEventType;
processInstance.endToken = encodedEndToken;
processInstance.endTokenCompressed = compressedEndToken;
this.calculateTotalRuntime(processInstance);
return (0, SequelizeConnectionManager_1.executeWithRetry)(async () => {
const transaction = await this.sequelizeInstance.transaction();
try {
await processInstance.save({ transaction });
if (includeFlowNodeInstances) {
await this.cleanupFlowNodeInstances(processInstanceId, transaction, processcube_engine_sdk_1.FlowNodeInstanceState.terminated, (0, processcube_engine_sdk_1.serializeJson)(error));
}
await transaction.commit();
}
catch (error) {
await transaction.rollback();
}
}, `finish Process Instance '${processInstanceId}' with error`);
}
async terminate(processInstanceId, error, endEventData, includeFlowNodeInstances) {
const processInstance = await this.findProcessInstanceAndVerifyRunningState(processInstanceId);
const endTokenToEncode = endEventData?.endEventToken ?? {};
const encodedEndToken = this.config.compressProcessTokens ? undefined : JSON.stringify(endTokenToEncode);
const compressedEndToken = this.config.compressProcessTokens ? await (0, Compression_1.compressToken)(endTokenToEncode) : undefined;
processInstance.state = processcube_engine_sdk_1.ProcessInstanceState.terminated;
processInstance.error = (0, processcube_engine_sdk_1.serializeJson)(error);
processInstance.terminatedByUserId = error?.additionalInformation?.killedBy?.userId;
processInstance.endEventId = endEventData?.endEventId;
processInstance.endEventType = endEventData?.endEventType;
processInstance.endToken = encodedEndToken;
processInstance.endTokenCompressed = compressedEndToken;
this.calculateTotalRuntime(processInstance);
return (0, SequelizeConnectionManager_1.executeWithRetry)(async () => {
const transaction = await this.sequelizeInstance.transaction();
try {
await processInstance.save({ transaction });
if (includeFlowNodeInstances) {
await this.cleanupFlowNodeInstances(processInstanceId, transaction, processcube_engine_sdk_1.FlowNodeInstanceState.terminated, (0, processcube_engine_sdk_1.serializeJson)(error));
}
await transaction.commit();
}
catch (error) {
await transaction.rollback();
}
}, `terminate Process Instance '${processInstanceId}'`);
}
async changeProcessDefinition(processInstanceId, newProcessModelHash) {
return (0, SequelizeConnectionManager_1.executeWithRetry)(async () => {
await Models.ProcessInstanceModel.update({ processModelHash: newProcessModelHash }, {
where: {
processInstanceId,
},
});
}, `change Process Model Hash of Process Instance '${processInstanceId}' to '${newProcessModelHash}'`);
}
async reset(processInstanceId, flowNodeInstanceIdsToReset, flowNodeInstanceIdsToDelete, processInstanceIdsToDelete) {
const queryParams = {
where: {
processInstanceId: processInstanceId,
deleted: false,
},
};
const processInstance = await (0, SequelizeConnectionManager_1.executeWithRetry)(async () => Models.ProcessInstanceModel.findOne(queryParams), `find process instance '${processInstanceId}'`);
if (!processInstance) {
throw new processcube_engine_sdk_1.NotFoundError(`ProcessInstance with ID \`${processInstanceId}\` not found.`);
}
if (processInstance.state !== processcube_engine_sdk_1.ProcessInstanceState.error && processInstance.state !== processcube_engine_sdk_1.ProcessInstanceState.terminated) {
throw new processcube_engine_sdk_1.BadRequestError(`ProcessInstance with ID \`${processInstanceId}\` is not in an error or terminated state.`);
}
const transaction = await this.sequelizeInstance.transaction();
try {
processInstance.state = processcube_engine_sdk_1.ProcessInstanceState.running;
processInstance.restartedAt = (0, dayjs_1.default)().toDate();
processInstance.finishedAt = null;
processInstance.error = null;
await (0, SequelizeConnectionManager_1.executeWithRetry)(async () => {
await processInstance.save({ transaction: transaction });
}, `reset process instance '${processInstanceId}'`);
if (flowNodeInstanceIdsToDelete?.length > 0) {
await FlowNodeInstanceAdapter_1.FlowNodeInstanceDatabaseAdapter.deleteByIds(flowNodeInstanceIdsToDelete, transaction);
await this.deleteMetadataValueByFlowNodeInstanceIds(flowNodeInstanceIdsToDelete, transaction);
await CorrelationAdapter_1.CorrelationDatabaseAdapter.deleteMetadataValueByFlowNodeInstanceIds(flowNodeInstanceIdsToDelete, transaction);
}
for (const flowNodeInstance of flowNodeInstanceIdsToReset) {
if (typeof flowNodeInstance === 'string') {
await FlowNodeInstanceAdapter_1.FlowNodeInstanceDatabaseAdapter.reset(flowNodeInstance, transaction);
}
else {
await FlowNodeInstanceAdapter_1.FlowNodeInstanceDatabaseAdapter.reset(flowNodeInstance.flowNodeInstanceId, transaction, flowNodeInstance.isUnstartedSubProcess);
}
}
if (processInstanceIdsToDelete?.length > 0) {
await ProcessInstanceDatabaseAdapter_1.deleteByIds(processInstanceIdsToDelete, transaction);
}
await transaction.commit();
}
catch (error) {
await transaction.rollback();
throw error;
}
}
async flagAsDeletedByIds(processInstanceIds) {
return (0, SequelizeConnectionManager_1.executeWithRetry)(async () => {
await Models.ProcessInstanceModel.update({ deleted: true }, {
where: {
processInstanceId: {
[sequelize_1.Op.in]: processInstanceIds,
},
},
});
}, `set deleted flag for process instances with IDs '${processInstanceIds.join(';')}'`);
}
async deleteByIds(processInstanceIds) {
return (0, SequelizeConnectionManager_1.executeWithRetry)(async () => {
const deleteTransaction = await this.sequelizeInstance.transaction();
try {
const deleteOptions = {
where: {
processInstanceId: {
[sequelize_1.Op.in]: processInstanceIds,
},
},
transaction: deleteTransaction,
};
await Models.ProcessInstanceModel.destroy(deleteOptions);
await Models.ProcessInstanceMetadataModel.destroy(deleteOptions);
await FlowNodeInstanceAdapter_1.FlowNodeInstanceDatabaseAdapter.deleteByProcessInstanceIds(processInstanceIds, deleteTransaction);
await ProcessInstanceDatabaseAdapter_1.deleteEmptyCorrelations(deleteTransaction);
await deleteTransaction.commit();
}
catch (error) {
await deleteTransaction.rollback();
}
}, `delete Process Instance with id '${processInstanceIds}'`);
}
static async deleteByIds(processInstanceIds, transaction) {
const deleteOptions = {
where: {
processInstanceId: {
[sequelize_1.Op.in]: processInstanceIds,
},
},
transaction: transaction,
};
await Models.ProcessInstanceModel.destroy(deleteOptions);
await Models.ProcessInstanceMetadataModel.destroy(deleteOptions);
await FlowNodeInstanceAdapter_1.FlowNodeInstanceDatabaseAdapter.deleteByProcessInstanceIds(processInstanceIds, transaction);
await ProcessInstanceDatabaseAdapter_1.deleteEmptyCorrelations(transaction);
}
async deleteByProcessDefinitionId(processDefinitionId) {
return (0, SequelizeConnectionManager_1.executeWithRetry)(async () => {
const deleteTransaction = await this.sequelizeInstance.transaction();
try {
await ProcessInstanceDatabaseAdapter_1.deleteByProcessDefinitionId(processDefinitionId, deleteTransaction);
await deleteTransaction.commit();
}
catch (error) {
await deleteTransaction.rollback();
}
}, `delete Process Instances by process definition id '${processDefinitionId}'`);
}
static async deleteByProcessDefinitionId(processDefinitionId, deleteTransaction) {
const processInstanceIdsToRemove = (await Models.ProcessInstanceModel.findAll({
where: {
processDefinitionId: processDefinitionId,
},
attributes: ['processInstanceId'],
transaction: deleteTransaction,
})).map((processInstance) => processInstance.processInstanceId);
const deleteChunkSize = 1000;
while (processInstanceIdsToRemove.length > 0) {
const processInstanceIdChunk = processInstanceIdsToRemove.splice(0, deleteChunkSize);
const deleteChunkOptions = {
where: {
processInstanceId: {
[sequelize_1.Op.in]: processInstanceIdChunk,
},
},
transaction: deleteTransaction,
};
await Models.ProcessInstanceModel.destroy(deleteChunkOptions);
await Models.ProcessInstanceMetadataModel.destroy(deleteChunkOptions);
}
await ProcessInstanceDatabaseAdapter_1.deleteEmptyCorrelations(deleteTransaction);
}
async execCreate(request) {
this.validateCreateRequest(request);
const existingProcessInstance = await Models.ProcessInstanceModel.findOne({
where: {
processInstanceId: request.processInstanceId,
},
});
if (existingProcessInstance && existingProcessInstance.deleted === false) {
return;
}
if (existingProcessInstance && existingProcessInstance.deleted === true) {
await this.deleteByIds([existingProcessInstance.processInstanceId]);
}
await this.storeCorrelation(request);
const startTokenToEncode = request.startToken ?? {};
const encodedStartToken = this.config.compressProcessTokens ? undefined : JSON.stringify(startTokenToEncode);
const compressedStartToken = this.config.compressProcessTokens ? await (0, Compression_1.compressToken)(startTokenToEncode) : undefined;
const createProcessInstanceParams = {
correlationId: request.correlationId,
processInstanceId: request.processInstanceId,
processDefinitionId: request.processDefinitionId,
processModelId: request.processModelId,
processModelName: request.processModelName,
processModelVersion: request.processModelVersion,
parentProcessInstanceId: request.parentProcessInstanceId,
processModelHash: request.processModelHash,
ownerId: request.identity.userId,
ownerToken: request.identity.token,
state: processcube_engine_sdk_1.ProcessInstanceState.running,
startEventId: request.startEventId,
startEventType: request.startEventType,
startToken: encodedStartToken,
startTokenCompressed: compressedStartToken,
startedByRootAccessToken: request.startedByRootAccessToken,
triggeredByFlowNodeInstanceId: request.triggeredByFlowNodeInstanceId,
embeddedProcessModelId: request.embeddedProcessModelId,
};
await Models.ProcessInstanceModel.create(createProcessInstanceParams);
}
async getProcessInstanceIdsList(whereClause, include) {
const request = {
where: whereClause,
include: include,
attributes: ['processInstanceId', 'parentProcessInstanceId'],
};
const result = await (0, SequelizeConnectionManager_1.executeWithRetry)(async () => Models.ProcessInstanceModel.findAll(request), 'get Process Instances ids');
return result;
}
async getUnorderedProcessInstanceList(whereClause) {
const request = {
where: whereClause,
include: [
{
model: Models.CorrelationModel,
as: 'correlation',
attributes: ['correlationId'],
required: false,
include: [
{
model: Models.CorrelationMetadataModel,
as: 'correlationMetadata',
attributes: ['key', 'value', 'createdAt'],
required: false,
},
],
},
{
model: Models.ProcessInstanceMetadataModel,
as: 'processInstanceMetadata',
attributes: ['key', 'value', 'createdAt'],
required: false,
},
{
model: Models.ProcessDefinitionModel,
attributes: ['xml'],
required: false,
},
],
};
const results = await (0, SequelizeConnectionManager_1.executeWithRetry)(async () => Models.ProcessInstanceModel.findAll(request), 'get Process Instances by id');
const processInstances = [];
for (const rawProcessInstance of results) {
const processInstance = await (0, ProcessInstanceFactory_1.createProcessInstanceFromModel)(rawProcessInstance);
processInstances.push(processInstance);
}
return processInstances;
}
async getProcessInstanceIdsToLoad(query, offset, limit, sortSettings, includeDeleted = false, includeCount = false) {
const sortByCorrelationMetadata = sortSettings?.sortBy === processcube_engine_sdk_1.ProcessInstanceSortableColumns.correlationMetadata;
const sortByProcessInstanceMetadata = sortSettings?.sortBy === processcube_engine_sdk_1.ProcessInstanceSortableColumns.processInstanceMetadata;
const sortByStartToken = sortSettings?.sortBy === processcube_engine_sdk_1.ProcessInstanceSortableColumns.startToken;
const sortByEndToken = sortSettings?.sortBy === processcube_engine_sdk_1.ProcessInstanceSortableColumns.endToken;
// MySQL requires this column to be present in the "GroupBy" clause.
const getOrderByColumnNameFromOrderSettings = () => {
if (!sortSettings?.sortBy) {
return ['ProcessInstance.createdAt'];
}
if (sortByStartToken) {
this.logger.warn('Sorting by start token is no longer supported. Using default sorting.');
return ['ProcessInstance.createdAt'];
}
if (sortByEndToken) {
this.logger.warn('Sorting by end token is no longer supported. Using default sorting.');
return ['ProcessInstance.createdAt'];
}
if (sortByCorrelationMetadata) {
return ['correlation.correlationMetadata.key', 'correlation.correlationMetadata.value'];
}
if (sortByProcessInstanceMetadata) {
return ['processInstanceMetadata.key', 'processInstanceMetadata.value'];
}
return [`ProcessInstance.${sortSettings.sortBy}`];
};
const orderByColumNames = getOrderByColumnNameFromOrderSettings();
const includes = this.getIncludesForPlainQueries(query, sortSettings);
const requiresGrouping = Object.keys(includes).length > 0;
const where = await this.buildProcessInstanceWhereClause(query, includeDeleted);
const options = {
where: where,
attributes: ['processInstanceId', ...orderByColumNames],
include: Object.values(includes),
...(0, SearchQueryBuilder_1.buildPagination)(offset, limit),
order: this.buildQueryOrderSettings(sortSettings),
subQuery: false,
group: requiresGrouping ? ['ProcessInstance.processInstanceId', ...orderByColumNames, 'ProcessInstance.id'] : undefined,
};
if (!includeCount) {
return this.getProcessInstancesWithoutCount(options);
}
return this.getProcessInstancesWithCount(options);
}
getIncludesForPlainQueries(query, sortSettings) {
const sortByCorrelationMetadata = sortSettings?.sortBy === processcube_engine_sdk_1.ProcessInstanceSortableColumns.correlationMetadata;
const sortByProcessInstanceMetadata = sortSettings?.sortBy === processcube_engine_sdk_1.ProcessInstanceSortableColumns.processInstanceMetadata;
const includes = {};
const includeCorrelationMetadata = sortByCorrelationMetadata || query.correlationMetadata != undefined;
if (includeCorrelationMetadata) {
includes[Models.CorrelationModel.name] = {
model: Models.CorrelationModel.scope(null),
attributes: [],
required: false,
include: [
{
model: Models.CorrelationMetadataModel.scope(null),
attributes: [],
required: false,
},
],
};
}
const includeProcessInstanceMetadata = sortByProcessInstanceMetadata || query.processInstanceMetadata != undefined;
if (includeProcessInstanceMetadata) {
includes[Models.CorrelationMetadataModel.name] = {
model: Models.ProcessInstanceMetadataModel.scope(null),
attributes: [],
required: false,
};
}
const includeFlowNodeInstances = query.allowedLanes != undefined || query.requestingUserId != undefined;
if (includeFlowNodeInstances) {
includes[Models.FlowNodeInstanceModel.name] = {
model: Models.FlowNodeInstanceModel.scope(null),
as: 'flowNodeInstances',
attributes: [],
required: false,
};
}
return includes;
}
async buildProcessInstanceWhereClause(query, includeDeleted = false) {
const whereClause = {};
if (!includeDeleted) {
whereClause.deleted = false;
}
whereClause.correlationId = (0, SearchQueryBuilder_1.buildSearchQueryFilter)(query.correlationId, 'ProcessInstance.correlationId', this.sequelizeInstance);
whereClause.processInstanceId = (0, SearchQueryBuilder_1.buildSearchQueryFilter)(query.processInstanceId, 'ProcessInstance.processInstanceId', this.sequelizeInstance, true, true);
whereClause.processDefinitionId = (0, SearchQueryBuilder_1.buildSearchQueryFilter)(query.processDefinitionId, 'ProcessInstance.processDefinitionId', this.sequelizeInstance);
whereClause.processModelId = (0, SearchQueryBuilder_1.buildSearchQueryFilter)(query.processModelId, 'ProcessInstance.processModelId', this.sequelizeInstance);
whereClause.processModelName = (0, SearchQueryBuilder_1.buildSearchQueryFilter)(query.processModelName, 'ProcessInstance.processModelName', this.sequelizeInstance);
whereClause.processModelVersion = (0, SearchQueryBuilder_1.buildSearchQueryFilter)(query.processModelVersion, 'ProcessInstance.processModelVersion', this.sequelizeInstance);
whereClause.processModelHash = (0, SearchQueryBuilder_1.buildSearchQueryFilter)(query.processModelHash, 'ProcessInstance.processModelHash', this.sequelizeInstance, true, true);
whereClause.embeddedProcessModelId = (0, SearchQueryBuilder_1.buildSearchQueryFilter)(query.embeddedProcessModelId, 'ProcessInstance.embeddedProcessModelId', this.sequelizeInstance);
whereClause.state = (0, SearchQueryBuilder_1.buildSearchQueryFilter)(query.state, 'ProcessInstance.state', this.sequelizeInstance, true, true);
whereClause.ownerId = (0, SearchQueryBuilder_1.buildSearchQueryFilter)(query.ownerId, 'ProcessInstance.ownerId', this.sequelizeInstance);
whereClause.startedByRootAccessToken = query.startedByRootAccessToken;
whereClause.parentProcessInstanceId = (0, SearchQueryBuilder_1.buildSearchQueryFilter)(query.parentProcessInstanceId, 'ProcessInstance.parentProcessInstanceId', this.sequelizeInstance, true, true);
whereClause.terminatedByUserId = (0, SearchQueryBuilder_1.buildSearchQueryFilter)(query.terminatedByUserId, 'ProcessInstance.terminatedByUserId', this.sequelizeInstance);
whereClause.triggeredByFlowNodeInstanceId = (0, SearchQueryBuilder_1.buildSearchQueryFilter)(query.triggeredByFlowNodeInstance, 'ProcessInstance.triggeredByFlowNodeInstanceId', this.sequelizeInstance, true, true);
whereClause.startEventId = (0, SearchQueryBuilder_1.buildSearchQueryFilter)(query.startEventId, 'ProcessInstance.startEventId', this.sequelizeInstance);
whereClause.startEventType = this.buildEventTypeFilter(query, 'ProcessInstance.startEventType');
if (query.startToken) {
this.logger.warn('The "startToken" Filter is no longer supported, because compressed tokens cannot be filtered.');
}
if (query.endToken) {
this.logger.warn('The "endToken" Filter is no longer supported, because compressed tokens cannot be filtered.');
}
whereClause.endEventType = this.buildEventTypeFilter(query, 'ProcessInstance.endEventType');
whereClause.endEventId = (0, SearchQueryBuilder_1.buildSearchQueryFilter)(query.endEventId, 'ProcessInstance.endEventId', this.sequelizeInstance);
whereClause.finishedAt = (0, SearchQueryBuilder_1.buildDateFilter)(query.finishedAt, query.finishedAfter, query.finishedBefore);
whereClause.createdAt = (0, SearchQueryBuilder_1.buildDateFilter)(query.createdAt, query.createdAfter, query.createdBefore);
whereClause.updatedAt = (0, SearchQueryBuilder_1.buildDateFilter)(query.updatedAt, query.updatedAfter, query.updatedBefore);
whereClause[sequelize_1.Op.and] = [];
if (query.durationInMillisecondsLessThan) {
whereClause[sequelize_1.Op.and].push({
durationInMilliseconds: { [sequelize_1.Op.lt]: query.durationInMillisecondsLessThan },
});
}
if (query.durationInMillisecondsGreaterThan) {
whereClause[sequelize_1.Op.and].push({
durationInMilliseconds: { [sequelize_1.Op.gte]: query.durationInMillisecondsGreaterThan },
});
}
const authorizationFilters = this.buildAuthorizationFilters(query.allowedLanes, query.requestingUserId);
if (authorizationFilters) {
whereClause[sequelize_1.Op.and].push({
[sequelize_1.Op.or]: authorizationFilters,
});
}
const searchQuery = Reflect.ownKeys(whereClause)
.filter((key) => whereClause[key] !== undefined)
.reduce((previousKeys, currentKey) => ({ ...previousKeys, [currentKey]: whereClause[currentKey] }), {});
return searchQuery;
}
async getProcessInstancesWithCount(options) {
const findAndCountAllResult = await (0, SequelizeConnectionManager_1.executeWithRetry)(async () => Models.ProcessInstanceModel.findAndCountAll(options), 'find and count Process Instances to query');
const rowCount = Array.isArray(findAndCountAllResult.count) ? findAndCountAllResult.count.length : findAndCountAllResult.count;
const processInstanceIds = findAndCountAllResult.rows.map((result) => result.processInstanceId);
return {
count: rowCount,
processInstanceIds: processInstanceIds,
};
}
async getProcessInstancesWithoutCount(options) {
const findAllResult = await (0, SequelizeConnectionManager_1.executeWithRetry)(async () => Models.ProcessInstanceModel.findAll(options), 'find Process Instance Ids to query without count');
const processInstanceIds = findAllResult.map((result) => result.processInstanceId);
return {
count: undefined,
processInstanceIds: processInstanceIds,
};
}
buildAuthorizationFilters(allowedLanes, requestingUserId) {
if (!allowedLanes && !requestingUserId) {
return undefined;
}
const query = [];
if (allowedLanes) {
query.push({
[sequelize_1.Op.or]: [
{ '$flowNodeInstances.flowNodeLane$': { [sequelize_1.Op.in]: allowedLanes } },
{
[sequelize_1.Op.and]: [{ '$flowNodeInstances.flowNodeLane$': { [sequelize_1.Op.eq]: null } }, { '$flowNodeInstances.id$': { [sequelize_1.Op.ne]: null } }],
},
],
});
}
if (requestingUserId) {
query.push({
ownerId: requestingUserId,
});
}
return query;
}
buildEventTypeFilter(query, eventTypeProperty) {
const eventTypeName = eventTypeProperty.replace('ProcessInstance.', '');
if (query[eventTypeName] === undefined) {
return undefined;
}
const eventTypeQuery = query[eventTypeName];
// A null value denotes a regular start event.
const isNullFilter = eventTypeQuery === null || (Array.isArray(eventTypeQuery) && eventTypeQuery.every((entry) => entry === null));
if (isNullFilter) {
return { [sequelize_1.Op.is]: null };
}
if (!Array.isArray(eventTypeQuery) || eventTypeQuery.length === 1) {
return (0, SearchQueryBuilder_1.buildSearchQueryFilter)(eventTypeQuery, eventTypeProperty, this.sequelizeInstance);
}
const filters = [];
const hasNullFilter = eventTypeQuery.some((entry) => entry == null);
if (hasNullFilter) {
filters.push({ [sequelize_1.Op.is]: null });
}
const filterWithoutNullValues = eventTypeQuery.filter((entry) => entry != null);
if (filterWithoutNullValues.length > 0) {
filters.push({ [sequelize_1.Op.in]: filterWithoutNullValues });
}
return {
[sequelize_1.Op.or]: filters,
};
}
async storeCorrelation(request) {
const existingCorrelation = await Models.CorrelationModel.findOne({
where: {
correlationId: request.correlationId,
},
});
if (!existingCorrelation) {
try {
await Models.CorrelationModel.create({ correlationId: request.correlationId });
}
catch (error) {
if (error.name === 'SequelizeUniqueConstraintError') {
// This happens if the correlation was already stored while this check is running. Can be ignored.
// Note that we must not use transactions here, because if the INSERT command encounters an error, it breaks the entire transaction on DB side.
return;
}
throw error;
}
}
}
static async deleteEmptyCorrelations(transaction) {
const emptyCorrelationsToRemove = (await Models.CorrelationModel.findAll({
attributes: ['correlationId'],
include: [
{
model: Models.ProcessInstanceModel,
as: 'processInstances',
attributes: ['correlationId'],
required: false,
},
],
where: sequelize_1.Sequelize.where(sequelize_1.Sequelize.col('processInstances.correlationId'), 'IS', null),
transaction: transaction,
})).map((correlation) => correlation.correlationId);
if (emptyCorrelationsToRemove.length > 0) {
await Models.CorrelationMetadataModel.destroy({
where: {
correlationId: {
[sequelize_1.Op.in]: emptyCorrelationsToRemove,
},
},
transaction: transaction,
});
await Models.CorrelationModel.destroy({
where: {
correlationId: {
[sequelize_1.Op.in]: emptyCorrelationsToRemove,
},
},
transaction: transaction,
});
}
}
async deleteMetadataValueByFlowNodeInstanceIds(flowNodeInstanceIds, transaction) {
await Models.ProcessInstanceMetadataModel.destroy({
where: {
flowNodeInstanceId: {
[sequelize_1.Op.in]: flowNodeInstanceIds,
},
},
transaction: transaction,
});
}
validateCreateRequest(request) {
if (!request.correlationId) {
throw new processcube_engine_sdk_1.BadRequestError('Cannot create ProcessInstance: `correlationId` is missing!');
}
if (!request.processInstanceId) {
throw new processcube_engine_sdk_1.BadRequestError('Cannot create ProcessInstance: `processInstanceId` is missing!');
}
if (!request.processDefinitionId) {
throw new processcube_engine_sdk_1.BadRequestError('Cannot create ProcessInstance: `processDefinitionId` is missing!');
}
if (!request.processModelId) {
throw new processcube_engine_sdk_1.BadRequestError('Cannot create ProcessInstance: `processModelId` is missing!');
}
if (!request.processModelHash) {
throw new processcube_engine_sdk_1.BadRequestError('Cannot create ProcessInstance: `processModelHash` is missing!');
}
if (!request.startEventId) {
throw new processcube_engine_sdk_1.BadRequestError('Cannot create ProcessInstance: `startEventId` is missing!');
}
}
async findProcessInstanceAndVerifyRunningState(processInstanceId) {
const queryParams = {
where: {
processInstanceId: processInstanceId,
deleted: false,
},
};
const processInstance = a