UNPKG

@5minds/processcube_engine

Version:

The ProcessCube Engine. Stores and executes BPMNs.

352 lines • 14.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getCleanupOrphanedFlowNodeInstancesQuery = getCleanupOrphanedFlowNodeInstancesQuery; exports.getCleanupOrphanedExternalTasksQuery = getCleanupOrphanedExternalTasksQuery; exports.buildWhereClause = buildWhereClause; exports.buildPagination = buildPagination; exports.getProcessDefinitionUpdatedAtMaxFilter = getProcessDefinitionUpdatedAtMaxFilter; exports.buildDateFilter = buildDateFilter; exports.buildSearchQueryFilter = buildSearchQueryFilter; exports.buildFlowNodeLaneQuery = buildFlowNodeLaneQuery; const sequelize_1 = require("sequelize"); const processcube_engine_sdk_1 = require("@5minds/processcube_engine_sdk"); const dynamicSequelize = sequelize_1.Sequelize; function getCleanupOrphanedFlowNodeInstancesQuery(dialect, schema) { if (dialect === 'postgres') { const tableNamePrefix = schema ? `"${schema}".` : '"public".'; return ` UPDATE ${tableNamePrefix}"FlowNodeInstances" SET state = pi.state FROM ${tableNamePrefix}"ProcessInstances" AS pi WHERE ${tableNamePrefix}"FlowNodeInstances"."processInstanceId" = pi."processInstanceId" AND ${tableNamePrefix}"FlowNodeInstances".state IN ('suspended', 'running') AND pi.state != 'running'; `; } if (dialect === 'mssql') { const tableNamePrefix = schema ? `${schema}.` : ''; return ` UPDATE ${tableNamePrefix}FlowNodeInstances SET FlowNodeInstances.state = processInstance.state FROM ${tableNamePrefix}FlowNodeInstances AS FlowNodeInstance INNER JOIN ${tableNamePrefix}ProcessInstances AS ProcessInstance ON FlowNodeInstance.processInstanceId = ProcessInstance.processInstanceId WHERE FlowNodeInstance.state IN (N'suspended', N'running') AND ProcessInstance.state != N'running';`; } const tableNamePrefix = schema ? `${schema}.` : ''; return ` UPDATE \`${tableNamePrefix}FlowNodeInstances\` SET state = ( SELECT state FROM \`${tableNamePrefix}ProcessInstances\` as pi1 WHERE pi1.processInstanceId = \`${tableNamePrefix}FlowNodeInstances\`.processInstanceId LIMIT 1 ) WHERE state IN ('suspended', 'running') AND EXISTS ( SELECT 1 FROM \`${tableNamePrefix}ProcessInstances\` as pi2 WHERE pi2.processInstanceId = \`${tableNamePrefix}FlowNodeInstances\`.processInstanceId AND pi2.state != 'running' ); `; } function getCleanupOrphanedExternalTasksQuery(dialect, schema) { if (dialect === 'postgres') { const tableNamePrefix = schema ? `"${schema}".` : '"public".'; return ` UPDATE ${tableNamePrefix}"ExternalTasks" SET state = 'finished' FROM ${tableNamePrefix}"FlowNodeInstances" AS fni WHERE ${tableNamePrefix}"ExternalTasks"."flowNodeInstanceId" = fni."flowNodeInstanceId" AND ${tableNamePrefix}"ExternalTasks".state = 'pending' AND fni.state != 'suspended'; `; } if (dialect === 'mssql') { const tableNamePrefix = schema ? `${schema}.` : ''; return ` UPDATE ${tableNamePrefix}ExternalTasks SET state = 'finished' FROM ${tableNamePrefix}ExternalTasks AS et INNER JOIN ${tableNamePrefix}FlowNodeInstances AS fni ON et.flowNodeInstanceId = fni.flowNodeInstanceId WHERE et.state = N'pending' AND fni.state != N'suspended';`; } const tableNamePrefix = schema ? `${schema}.` : ''; return ` UPDATE \`${tableNamePrefix}ExternalTasks\` SET state = 'finished' WHERE state = 'pending' AND EXISTS ( SELECT 1 FROM \`${tableNamePrefix}FlowNodeInstances\` as fni WHERE fni.flowNodeInstanceId = \`${tableNamePrefix}ExternalTasks\`.flowNodeInstanceId AND fni.state != 'suspended' ); `; } function buildWhereClause(query) { const whereClause = {}; const fieldsToQuery = Object.keys(query); for (const fieldName of fieldsToQuery) { const fieldValue = query[fieldName]; // null should be allowed, because it is a legitimate db value if (fieldValue === undefined) { continue; } if (Array.isArray(fieldValue)) { whereClause[fieldName] = { [sequelize_1.Op.in]: fieldValue.map(parseValue), }; } else { whereClause[fieldName] = parseValue(fieldValue); } } return whereClause; } function parseValue(value) { // "typeof null" and "typeof Date" return "object", so these must be handled separately. if (value === null || value instanceof Date) { return value; } if (typeof value === 'object') { return JSON.stringify(value); } return value; } function buildPagination(offset, limit) { const pagination = {}; if (offset > 0) { pagination.offset = offset; } if (limit > 0) { pagination.limit = limit; } return pagination; } function getProcessDefinitionUpdatedAtMaxFilter(dialect, schema) { if (dialect === 'postgres') { const tableNamePrefix = schema ? `"${schema}".` : '"public".'; return `( SELECT max("updatedAt") FROM ${tableNamePrefix}"ProcessDefinitions" AS pd2 WHERE "ProcessDefinition".name = pd2.name )`; } if (dialect === 'mssql') { const tableNamePrefix = schema ? `${schema}.` : ''; return `( SELECT max(updatedAt) FROM ${tableNamePrefix}ProcessDefinitions AS pd2 WHERE ProcessDefinition.name = pd2.name )`; } const tableNamePrefix = schema ? `${schema}.` : ''; return `( SELECT max(updatedAt) FROM \`${tableNamePrefix}ProcessDefinitions\` AS pd2 WHERE ProcessDefinition.name = pd2.name )`; } function buildDateFilter(equals, after, before) { if (!equals && !before && !after) { return undefined; } if (equals && !Array.isArray(equals)) { equals = [equals]; } const dateFilter = {}; if (equals) { dateFilter[sequelize_1.Op.in] = equals; } if (before) { dateFilter[sequelize_1.Op.lt] = before; } if (after) { dateFilter[sequelize_1.Op.gte] = after; } return dateFilter; } function buildSearchQueryFilter(searchQuery, column, sequelizeInstance, lowerCaseSearchValue = false, forceMostPerformantCasingOption = false) { if (searchQuery === undefined || searchQuery === null) { return searchQuery; } const whereObject = {}; let params; let operator; let filter; if (typeof searchQuery === 'string') { params = [searchQuery]; operator = 'or'; } else if (Array.isArray(searchQuery)) { params = searchQuery; operator = 'or'; } else { params = searchQuery.searchParams; operator = searchQuery.operator; } if (isSearchQueryArray(params)) { filter = params.map((param) => buildSearchQueryFilter(param, column, sequelizeInstance, lowerCaseSearchValue, forceMostPerformantCasingOption)); } else { filter = buildSearchParamsFilter(params, column, sequelizeInstance, lowerCaseSearchValue, forceMostPerformantCasingOption); } if (filter === undefined) { return undefined; } if (operator == 'and') { whereObject[sequelize_1.Op.and] = filter; } else { whereObject[sequelize_1.Op.or] = filter; } return whereObject; } function buildFlowNodeLaneQuery(query) { const flowNodeLanes = query.flowNodeLane; const requestingUserId = query.requestingUserId; delete query.flowNodeLane; delete query.requestingUserEmail; delete query.requestingUserId; delete query.requestingUserName; if (query.processModelName) { query['$processInstance.processModelName$'] = query['processModelName']; delete query['processModelName']; } const whereClause = buildWhereClause(query); const lanesAsArray = Array.isArray(flowNodeLanes) ? flowNodeLanes : [flowNodeLanes]; const shouldQueryAllFlowNodesForSpecificLanes = requestingUserId != undefined; if (shouldQueryAllFlowNodesForSpecificLanes) { whereClause['flowNodeLane'] = { [sequelize_1.Op.in]: lanesAsArray }; } return whereClause; } function buildSearchParamsFilter(searchParams, column, sequelizeInstance, lowerCaseSearchValue = false, forceMostPerformantCasingOption = false) { if (!searchParams || searchParams.length === 0) { return undefined; } if (isStringArray(searchParams)) { searchParams = searchParams.map((searchString) => { return { partialMatch: false, caseSensitive: true, searchString: lowerCaseSearchValue ? searchString?.toLowerCase() : searchString, }; }); } const queryArray = []; for (const searchParam of searchParams) { const caseSensitive = searchParam.caseSensitive !== false; const partialMatch = searchParam.partialMatch === true; if (!searchParam.searchString || searchParam.searchString.length === 0) { continue; } queryArray.push(buildDialectSpecificQuery(lowerCaseSearchValue ? searchParam.searchString?.toLowerCase() : searchParam.searchString, caseSensitive, partialMatch, column, sequelizeInstance, forceMostPerformantCasingOption)); } return queryArray; } function buildDialectSpecificQuery(searchString, caseSensitive, partialMatch, column, sequelizeInstance, forceMostPerformantCasingOption = false) { switch (sequelizeInstance.getDialect()) { case 'sqlite': return buildSQLiteQuery(searchString, caseSensitive, partialMatch, column, forceMostPerformantCasingOption); case 'postgres': return buildPostgresQuery(searchString, caseSensitive, partialMatch, column, forceMostPerformantCasingOption); case 'mysql': return buildMySQLQuery(searchString, caseSensitive, partialMatch, column, sequelizeInstance, forceMostPerformantCasingOption); case 'mssql': return buildMSSQLQuery(searchString, caseSensitive, partialMatch, column, forceMostPerformantCasingOption); default: throw new processcube_engine_sdk_1.InternalServerError(`Unable to build search query for unknown dialect: \`${sequelizeInstance.getDialect()}\`.`, 'setup'); } } function buildSQLiteQuery(searchString, caseSensitive, partialMatch, column, forceMostPerformantCasingOption = false) { if (forceMostPerformantCasingOption) { caseSensitive = true; } if (caseSensitive && partialMatch) { return dynamicSequelize.where(sequelize_1.Sequelize.fn('instr', sequelize_1.Sequelize.col(column), searchString), sequelize_1.Op.gt, 0); } if (caseSensitive && !partialMatch) { return { [sequelize_1.Op.eq]: searchString }; } if (!caseSensitive && partialMatch) { return dynamicSequelize.where(sequelize_1.Sequelize.fn('lower', sequelize_1.Sequelize.col(column)), sequelize_1.Op.substring, sequelize_1.Sequelize.fn('lower', `%${searchString}%`)); } // !caseSensitive && !partialMatch return dynamicSequelize.where(sequelize_1.Sequelize.fn('lower', sequelize_1.Sequelize.col(column)), sequelize_1.Op.eq, sequelize_1.Sequelize.fn('lower', searchString)); } function buildPostgresQuery(searchString, caseSensitive, partialMatch, column, forceMostPerformantCasingOption = false) { if (forceMostPerformantCasingOption) { caseSensitive = true; } if (caseSensitive && partialMatch) { return { [sequelize_1.Op.substring]: `%${searchString}%` }; } if (caseSensitive && !partialMatch) { return { [sequelize_1.Op.eq]: searchString }; } if (!caseSensitive && partialMatch) { return dynamicSequelize.where(sequelize_1.Sequelize.fn('lower', sequelize_1.Sequelize.col(column)), sequelize_1.Op.substring, sequelize_1.Sequelize.fn('lower', `%${searchString}%`)); } // !caseSensitive && !partialMatch return dynamicSequelize.where(sequelize_1.Sequelize.fn('lower', sequelize_1.Sequelize.col(column)), sequelize_1.Op.eq, sequelize_1.Sequelize.fn('lower', searchString)); } function buildMySQLQuery(searchString, caseSensitive, partialMatch, column, sequelizeInstance, forceMostPerformantCasingOption = false) { if (forceMostPerformantCasingOption) { caseSensitive = true; } if (caseSensitive && partialMatch) { return dynamicSequelize.literal(`${formatColumnForMySQL(column)} like BINARY ${sequelizeInstance.escape(`%${searchString}%`)}`); } if (caseSensitive && !partialMatch) { return dynamicSequelize.literal(`${formatColumnForMySQL(column)} = BINARY ${sequelizeInstance.escape(`${searchString}`)}`); } if (!caseSensitive && partialMatch) { return dynamicSequelize.where(sequelize_1.Sequelize.fn('lower', sequelize_1.Sequelize.col(column)), sequelize_1.Op.substring, sequelize_1.Sequelize.fn('lower', `%${searchString}%`)); } // !caseSensitive && !partialMatch return dynamicSequelize.where(sequelize_1.Sequelize.fn('lower', sequelize_1.Sequelize.col(column)), sequelize_1.Op.eq, sequelize_1.Sequelize.fn('lower', searchString)); } function formatColumnForMySQL(column) { return column.split('.').reduce((prev, curr) => { if (prev) { return `${prev}.\`${curr}\``; } return `\`${curr}\``; }); } function buildMSSQLQuery(searchString, caseSensitive, partialMatch, column, forceMostPerformantCasingOption = false) { if (forceMostPerformantCasingOption) { caseSensitive = false; } if (caseSensitive && partialMatch) { return dynamicSequelize.literal(`${formatColumnForMSSQL(column)} LIKE '%${searchString}%' COLLATE Latin1_General_CS_AS`); } if (caseSensitive && !partialMatch) { return dynamicSequelize.literal(`${formatColumnForMSSQL(column)} = '${searchString}' COLLATE Latin1_General_CS_AS`); } if (!caseSensitive && partialMatch) { return { [sequelize_1.Op.substring]: `%${searchString}%` }; } // !caseSensitive && !partialMatch return { [sequelize_1.Op.eq]: searchString }; } function formatColumnForMSSQL(column) { return column.split('.').reduce((prev, curr) => { if (prev) { return `${prev}.[${curr}]`; } return `[${curr}]`; }); } function isSearchQueryArray(objectToCheck) { return Array.isArray(objectToCheck) && objectToCheck.length > 0 && typeof objectToCheck[0] !== 'string' && 'operator' in objectToCheck[0] && 'searchParams' in objectToCheck[0]; } function isStringArray(objectToCheck) { return Array.isArray(objectToCheck) && objectToCheck.length > 0 && typeof objectToCheck[0] === 'string'; } //# sourceMappingURL=SearchQueryBuilder.js.map