@5minds/processcube_engine
Version:
The ProcessCube Engine. Stores and executes BPMNs.
386 lines • 24.5 kB
JavaScript
;
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 __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProcessInstanceServiceProxyConfiguration = exports.ProcessInstanceService = void 0;
const inversify_1 = require("inversify");
const processcube_engine_sdk_1 = require("@5minds/processcube_engine_sdk");
const EventAggregatorSettings_1 = require("../../Contracts/InternalMessages/EventAggregatorSettings");
const IocRegistrations_1 = require("../../Contracts/IocRegistrations");
const index_1 = require("../../Core/index");
const Configurator_1 = __importDefault(require("../../Tools/Configurator"));
const DatabaseAdaptersSequelize_1 = require("../../Tools/DatabaseAdaptersSequelize");
const EventAggregator_1 = __importDefault(require("../../Tools/EventAggregator"));
const index_2 = require("../../Tools/Iam/index");
const MonitoringManager_1 = require("../../Tools/MonitoringManager");
const ProcessDefinitionMediator_1 = require("../../Tools/ProcessDefinitionMediator");
const ProcessDefinitionMapper_1 = __importStar(require("./lib/ProcessDefinitionMapper"));
let ProcessInstanceService = class ProcessInstanceService {
coreAccess;
iamService;
identityService;
logger;
processDefinitionMediator;
processInstanceDatabaseAdapter;
config;
constructor(coreAccess, iamService, identityService, processDefinitionMediator, processInstanceDatabaseAdapter) {
this.coreAccess = coreAccess;
this.iamService = iamService;
this.identityService = identityService;
this.logger = new processcube_engine_sdk_1.Logger('api:process_instance_service');
this.processDefinitionMediator = processDefinitionMediator;
this.processInstanceDatabaseAdapter = processInstanceDatabaseAdapter;
this.config = Configurator_1.default.application();
}
async query(identity, query, offset, limit, sortSettings, includeXml = false, includeCount = true) {
const functionReporter = MonitoringManager_1.MonitoringManager.getNewFunctionReporter('ProcessInstanceService.query', {
queryKeys: Object.keys(query) ?? [],
offset,
limit,
sortSettings,
includeXml,
includeCount,
}, {
values: Object.values(query) ?? [],
});
if (!limit || limit <= 0) {
limit = this.config.defaultResponseLimit;
}
const isAdminOrObserver = this.iamService.checkIfUserIsSuperAdmin(identity) || this.iamService.checkIfUserIsObserver(identity);
if (isAdminOrObserver) {
const processInstanceInternalList = await this.processInstanceDatabaseAdapter.query(query, offset, limit, sortSettings, includeXml, false, includeCount);
functionReporter.finish();
return {
processInstances: processInstanceInternalList.processInstances.map(this.sanitizeProcessInstance),
totalCount: processInstanceInternalList.totalCount,
};
}
const restrictedQuery = {
...query,
...this.getAuthorizationQueryForIdentity(identity),
};
const results = await this.processInstanceDatabaseAdapter.query(restrictedQuery, offset, limit, sortSettings, includeXml, false, includeCount);
const mappedResults = results.processInstances.map(this.sanitizeProcessInstance);
functionReporter.finish();
return {
processInstances: mappedResults,
totalCount: results.totalCount,
};
}
async getChildProcessInstanceIds(identity, processInstanceId, parentFlowNodeType, includeNested) {
const authorizationQuery = this.getAuthorizationQueryForIdentity(identity);
if (!includeNested) {
const childProcessInstanceIds = await this.processInstanceDatabaseAdapter.getDirectChildProcessInstanceIds(processInstanceId, parentFlowNodeType, authorizationQuery);
return childProcessInstanceIds;
}
const descendantProcessInstanceIds = await this.processInstanceDatabaseAdapter.getAllDescendantProcessInstanceIds(processInstanceId, parentFlowNodeType, authorizationQuery);
return descendantProcessInstanceIds;
}
async getAncestorProcessInstanceIds(identity, processInstanceId, onlyForEmbeddedProcesses) {
const authorizationQuery = this.getAuthorizationQueryForIdentity(identity);
const ancestorProcessInstancesIds = await this.processInstanceDatabaseAdapter.getAncestorProcessInstanceIds(processInstanceId, onlyForEmbeddedProcesses, authorizationQuery);
return ancestorProcessInstancesIds;
}
async getProcessDefinition(identity, processInstanceId, includeXml = false) {
const processInstance = await this.getProcessInstanceById(identity, processInstanceId);
const processDefinition = await this.processDefinitionMediator.getByHash(identity, processInstance.hash);
const publicProcessDefinition = (0, ProcessDefinitionMapper_1.default)(processDefinition, includeXml);
return publicProcessDefinition;
}
async getProcessModel(identity, processInstanceId) {
const processInstance = await this.getProcessInstanceById(identity, processInstanceId);
const processDefinition = await this.processDefinitionMediator.getByHash(identity, processInstance.hash);
const processModelForProcessInstance = processDefinition.processes.find((processModel) => processModel.id === processInstance.processModelId);
const publicProcessModel = (0, ProcessDefinitionMapper_1.mapProcessModelToPublicType)(processModelForProcessInstance, processDefinition);
return publicProcessModel;
}
async transferOwnership(identity, processInstanceId, newOwner) {
this.logger.trace(`Transfer Process Instance Ownership called`, { processInstanceId: processInstanceId });
this.logger.trace('Checking if Process Instance exists', { processInstanceId: processInstanceId });
const result = await this.query(identity, { processInstanceId: processInstanceId });
if (result.processInstances.length === 0) {
throw new processcube_engine_sdk_1.NotFoundError(`ProcessInstance with ID \`${processInstanceId}\` not found!`);
}
const processInstance = result.processInstances[0];
this.logger.trace('Checking if user is allowed to transfer Process Instance Ownership', { processInstanceId: processInstanceId });
const processInstanceBelongsToAnotherUser = this.processInstanceBelongsToDifferentUser(identity, processInstance);
if (processInstanceBelongsToAnotherUser) {
throw new processcube_engine_sdk_1.ForbiddenError("Cannot transfer ownership of another user's ProcessInstance.");
}
let rootProcessInstance = processInstance;
if (processInstance.parentProcessInstanceId) {
this.logger.trace('Process Instance is a Child Process. Retrieving Root Process Instance', { processInstanceId: processInstanceId });
rootProcessInstance = await this.getRootProcessOfProcessInstanceTree(processInstance);
this.logger.trace(`Found Root Process Instance: ${processInstance.processInstanceId}`, {
processInstanceId: processInstanceId,
rootProcessInstanceId: rootProcessInstance.processInstanceId,
});
}
// The only relevant Process Instance for this check is the Root Process Instance, because we have to change ownerships for the entire Tree.
// As long as the root is still active, the entire Tree is considered active.
this.logger.trace('Checking if Process Instance is still active or retryable', { processInstanceId: processInstanceId });
if (rootProcessInstance.state === processcube_engine_sdk_1.ProcessInstanceState.finished) {
throw new processcube_engine_sdk_1.GoneError(`Cannot change owner of Process Instance ${processInstanceId}, because it is already finished.`);
}
this.logger.trace('Retrieving Child Process Instances for Process Instance', { processInstanceId: rootProcessInstance.processInstanceId });
const childProcessInstances = await this.processInstanceDatabaseAdapter.getAllDescendantProcessInstanceIds(rootProcessInstance.processInstanceId);
const processInstancesToTransfer = [...childProcessInstances, rootProcessInstance.processInstanceId];
this.logger.trace(`Transfering Ownership of Process Instance Tree, starting at Root Process Instance ${rootProcessInstance.processInstanceId}`, {
processInstanceId: processInstanceId,
newOwner: newOwner.userId,
processInstancesToTransfer: processInstancesToTransfer,
});
await this.processInstanceDatabaseAdapter.transferOwnership(processInstancesToTransfer, newOwner);
await Promise.all(processInstancesToTransfer.map(async (processInstanceToTransfer) => {
await this.coreAccess.transferOwnership(processInstanceToTransfer, newOwner);
}));
this.logger.info(`Transfered Ownership of Process Instance Tree, starting at Root Process Instance ${rootProcessInstance.processInstanceId}`, {
processInstanceId: processInstanceId,
newOwner: newOwner.userId,
processInstancesToTransfer: processInstancesToTransfer,
});
}
async terminateProcessInstance(identity, processInstanceId) {
this.logger.trace(`Terminate Process Instance called`, { processInstanceId: processInstanceId });
this.logger.trace('Checking if Process Instance exists', { processInstanceId: processInstanceId });
const result = await this.query(identity, { processInstanceId: processInstanceId });
if (result.processInstances.length === 0) {
throw new processcube_engine_sdk_1.NotFoundError(`ProcessInstance with ID \`${processInstanceId}\` not found!`);
}
const processInstance = result.processInstances[0];
await this.ensureUserIsAllowedToTerminateProcessInstance(identity, processInstance);
if (processInstance.state !== processcube_engine_sdk_1.ProcessInstanceState.running) {
throw new processcube_engine_sdk_1.BadRequestError(`Cannot terminate ProcessInstance with ID \`${processInstanceId}\`, because it is not running.`);
}
this.logger.trace(`Terminating Process Instance ${processInstanceId}`, { processInstanceId: processInstanceId });
await this.coreAccess.killProcessInstance(identity, processInstanceId);
this.logger.info(`Terminated Process Instance ${processInstanceId}`, { processInstanceId: processInstanceId });
}
async ensureUserIsAllowedToTerminateProcessInstance(identity, processInstance) {
this.logger.trace('Checking if user is allowed to terminate Process Instance', { processInstanceId: processInstance.processInstanceId });
const isPerClaimAuthorized = this.iamService.checkIfIdentityHasClaim(identity, processcube_engine_sdk_1.claims.canTerminateProcess);
if (isPerClaimAuthorized) {
return;
}
const processInstanceBelongsToAnotherUser = this.processInstanceBelongsToDifferentUser(identity, processInstance);
if (processInstanceBelongsToAnotherUser) {
throw new processcube_engine_sdk_1.ForbiddenError("Cannot terminate another user's ProcessInstance!");
}
}
async retryProcessInstance(identity, processInstanceId, flowNodeInstanceId, newStartToken, updateProcessModel = false) {
this.logger.trace(`Retry Process Instance called`, {
processInstanceId: processInstanceId,
flowNodeInstanceId: flowNodeInstanceId,
});
this.logger.trace('Checking if Process Instance exists', { processInstanceId: processInstanceId });
const result = await this.query(identity, { processInstanceId: processInstanceId });
const processInstance = result.processInstances.length == 1 ? result.processInstances[0] : undefined;
if (!processInstance) {
throw new processcube_engine_sdk_1.NotFoundError(`ProcessInstance with ID \`${processInstanceId}\` not found!`);
}
const processModel = await this.getProcessModel(identity, processInstanceId);
if (processModel.isSingleton) {
const instancesRunning = await this.processInstanceDatabaseAdapter.countRunningInstancesForModel(processModel.processModelId);
if (instancesRunning > 0) {
throw new processcube_engine_sdk_1.BadRequestError(`Cannot retry Process Instance \`${processInstanceId}\`, since \`${processModel.processModelId}\` is a Singleton process and already running in instance \`${instancesRunning[0]}\`.`);
}
}
this.logger.trace('Checking if user is allowed to retry Process Instance', { processInstanceId: processInstanceId });
const processInstanceBelongsToAnotherUser = this.processInstanceBelongsToDifferentUser(identity, processInstance);
if (processInstanceBelongsToAnotherUser) {
throw new processcube_engine_sdk_1.ForbiddenError("Cannot retry another user's ProcessInstance!");
}
this.ensureHasClaim(identity, processcube_engine_sdk_1.claims.canRetryProcessInstance);
let processDefinitionHash = undefined;
if (updateProcessModel) {
this.logger.trace('Checking if updating the Process Definition is possible', { processInstanceId: processInstanceId });
const processDefinition = await this.processDefinitionMediator.getByName(identity, processInstance.processDefinitionId);
if (processDefinition.hash === processInstance.hash) {
this.logger.debug(`Process Instance \`${processInstanceId}\` already uses the BPMN's latest version.`);
}
else {
processDefinitionHash = processDefinition.hash;
}
}
this.logger.trace(`Retrying Process Instance ${processInstanceId}`, { processInstanceId: processInstanceId });
await this.coreAccess.retryProcessInstance(identity, processInstanceId, processDefinitionHash, flowNodeInstanceId, newStartToken);
this.logger.info(`Retried Process Instance ${processInstanceId}`, { processInstanceId: processInstanceId });
}
async deleteProcessInstances(identity, processInstanceQuery, deleteAllRelatedData = false) {
this.logger.trace(`Delete Process Instance called`, {
processInstanceIds: processInstanceQuery.processInstanceId,
processModelId: processInstanceQuery.processModelId,
finishedBefore: processInstanceQuery.finishedBefore,
finishedAfter: processInstanceQuery.finishedAfter,
deleteAllRelatedData: deleteAllRelatedData,
});
if (!processInstanceQuery.processInstanceId && !processInstanceQuery.processModelId && !processInstanceQuery.finishedBefore && !processInstanceQuery.finishedAfter) {
throw new processcube_engine_sdk_1.BadRequestError('At least one of the following query parameter is required for a delete request: \`processInstanceId\`, \`processModelId\`, \`finishedBefore\`, or \`finishedAt\`');
}
this.logger.trace('Checking if Process Instances exist', {
processInstanceIds: processInstanceQuery.processInstanceId,
processModelId: processInstanceQuery.processModelId,
finishedBefore: processInstanceQuery.finishedBefore,
finishedAfter: processInstanceQuery.finishedAfter,
});
const result = await this.processInstanceDatabaseAdapter.query(processInstanceQuery);
if (result.processInstances.length === 0) {
return;
}
const processInstanceIds = result.processInstances.map((processInstance) => processInstance.processInstanceId);
this.logger.trace('Checking if user is allowed to delete the Process Instances', {
processInstanceIds: processInstanceIds,
});
this.ensureProcessInstanceSetCanBeDeleted(result.processInstances, identity);
this.logger.trace(`Deleting Process Instances`, {
processInstanceIds: processInstanceIds,
deleteAllRelatedData: deleteAllRelatedData,
});
if (deleteAllRelatedData) {
await this.processInstanceDatabaseAdapter.deleteByIds(processInstanceIds);
}
else {
await this.processInstanceDatabaseAdapter.flagAsDeletedByIds(processInstanceIds);
}
const processInstancesDeletedMessage = { processInstanceIds: processInstanceIds };
EventAggregator_1.default.publish(EventAggregatorSettings_1.eventAggregatorSettings.messagePaths.processInstancesDeleted, processInstancesDeletedMessage);
this.logger.info(`Deleted Process Instances`, {
processInstanceIds: processInstanceIds,
deleteAllRelatedData: deleteAllRelatedData,
});
}
getAuthorizationQueryForIdentity(identity) {
const isAdminOrObserver = this.iamService.checkIfUserIsSuperAdmin(identity) || this.iamService.checkIfUserIsObserver(identity);
if (isAdminOrObserver) {
return undefined;
}
return {
allowedLanes: this.identityService.getAvailableLaneClaims(identity),
requestingUserId: identity.userId,
};
}
async getProcessInstanceById(identity, processInstanceId) {
const queryResult = await this.query(identity, { processInstanceId: processInstanceId });
if (queryResult.processInstances.length === 0) {
throw new processcube_engine_sdk_1.NotFoundError(`ProcessInstance with ID \`${processInstanceId}\` not found`);
}
return queryResult.processInstances[0];
}
async getRootProcessOfProcessInstanceTree(processInstance) {
if (!processInstance.parentProcessInstanceId) {
return processInstance;
}
const parentProcessInstances = await this.processInstanceDatabaseAdapter.getProcessInstances([processInstance.parentProcessInstanceId]);
if (parentProcessInstances?.length === 0) {
throw new processcube_engine_sdk_1.NotFoundError(`Parent ProcessInstance \`${processInstance.parentProcessInstanceId}\` of child \`${processInstance.processInstanceId}\` not found.`);
}
const parentProcessInstance = parentProcessInstances[0];
return this.getRootProcessOfProcessInstanceTree(parentProcessInstance);
}
ensureProcessInstanceSetCanBeDeleted(processInstances, identity) {
const isSuperAdmin = this.iamService.checkIfUserIsSuperAdmin(identity);
if (!isSuperAdmin) {
const processInstancesOfOtherUsers = processInstances.filter((processInstance) => {
const userIdDoesNotMatch = processInstance.ownerId !== identity.userId;
return userIdDoesNotMatch;
});
if (processInstancesOfOtherUsers.length > 0) {
const accessDeniedError = new processcube_engine_sdk_1.ForbiddenError("Cannot delete another user's ProcessInstance!");
accessDeniedError.additionalInformation = {
processInstancesOfOtherUsers: processInstancesOfOtherUsers.map((processInstance) => processInstance.processInstanceId),
};
throw accessDeniedError;
}
}
const runningProcessInstances = processInstances.filter((processInstance) => processInstance.state === processcube_engine_sdk_1.ProcessInstanceState.running);
if (runningProcessInstances.length > 0) {
const runningProcessInstancesError = new processcube_engine_sdk_1.BadRequestError('Cannot delete ProcessInstances because some of them are still running.');
runningProcessInstancesError.additionalInformation = {
runningProcessInstances: runningProcessInstances.map((processInstance) => processInstance.processInstanceId),
};
throw runningProcessInstancesError;
}
}
processInstanceBelongsToDifferentUser(identity, processInstance) {
const isSuperAdmin = this.iamService.checkIfUserIsSuperAdmin(identity);
const ownerIdDoesNotMatch = processInstance.ownerId !== identity.userId;
return !isSuperAdmin && ownerIdDoesNotMatch;
}
sanitizeProcessInstance(processInstance) {
delete processInstance.ownerToken;
return processInstance;
}
ensureHasClaim(identity, claimName) {
const isSuperAdmin = this.iamService.checkIfUserIsSuperAdmin(identity);
if (isSuperAdmin) {
return;
}
this.iamService.ensureHasClaim(identity, claimName);
}
};
exports.ProcessInstanceService = ProcessInstanceService;
exports.ProcessInstanceService = ProcessInstanceService = __decorate([
(0, inversify_1.injectable)(),
__param(0, (0, inversify_1.inject)(IocRegistrations_1.IocRegistrationKeys.core.services.CoreAccess)),
__param(1, (0, inversify_1.inject)(IocRegistrations_1.IocRegistrationKeys.internal.IamService)),
__param(2, (0, inversify_1.inject)(IocRegistrations_1.IocRegistrationKeys.internal.IdentityService)),
__param(3, (0, inversify_1.inject)(IocRegistrations_1.IocRegistrationKeys.internal.ProcessDefinitionMediator)),
__param(4, (0, inversify_1.inject)(IocRegistrations_1.IocRegistrationKeys.internal.ProcessInstanceDatabaseAdapter)),
__metadata("design:paramtypes", [index_1.CoreAccessService,
index_2.IamService,
index_2.IdentityService,
ProcessDefinitionMediator_1.ProcessDefinitionMediator,
DatabaseAdaptersSequelize_1.ProcessInstanceDatabaseAdapter])
], ProcessInstanceService);
exports.ProcessInstanceServiceProxyConfiguration = {
proxyKey: IocRegistrations_1.IocRegistrationKeys.api.services.ProcessInstanceServiceProxy,
targetKey: IocRegistrations_1.IocRegistrationKeys.api.services.ProcessInstanceService,
target: ProcessInstanceService,
};
//# sourceMappingURL=ProcessInstanceService.js.map