UNPKG

@5minds/processcube_engine

Version:

The ProcessCube Engine. Stores and executes BPMNs.

386 lines • 24.5 kB
"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 __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