UNPKG

tuain-bpm-lib

Version:

Servicio de gestión de manejo de procesos de la plataforma Tuain

386 lines (364 loc) 14.3 kB
const { collections: { execution: { processes: processColl, gateways: gatewayColl } }, dbQueries: { execution: { process: processQueries } }, // schemas: { execution: instanceSchemas }, process: { elements: { gateway: GATEWAY }, gatewayTypes: { exclusive: EXCLUSIVE, inclusive: INCLUSIVE, parallel: PARALLEL }, elementStatus: { active: ACTIVE, complete: COMPLETE }, gatewayOutputStatus: { selected: OUTPUT_SELECTED, discarded: OUTPUT_DISCARDED }, gatewayInputStatus: { complete: INPUT_COMPLETE, pending: INPUT_PENDING }, resolutionTypes: { expression: EXPRESSION, callback: CALLBACK }, }, } = require('../../../config'); const modErrs = { getGateway: { notExist: ['01', 'Tarea no encontrada'], }, newGatewayInstance: { missingDefinition: ['01', 'No existe definición para el gateway especificado'], saveFailure: ['02', 'Error creando instancia de gateway en base de datos'], }, }; async function getGatewayInstance(gatewayId) { const gatewayCol = this.roPool.collection(gatewayColl); const gatewayInstance = await gatewayCol .findOne(processQueries.findGateway({ id: gatewayId, status: ACTIVE })); if (!gatewayInstance) { const errorDetail = `Gateway ${gatewayId} del proceso ${this.name} no se encontró`; const errorObj = this.errMgr.get(modErrs.getGatewayInstance.notFound, errorDetail); return [errorObj, null]; } return [null, gatewayInstance]; } async function startNewGateway(procId, name, origin) { this.logger.log({ level: 'silly', label: 'gatewayInstance', action: 'startNewGateway', message: `Se da inicio a la ejecución del gateway ${name} para el proceso ${procId} desde ${origin.name}`, }); const gatewayCol = this.roPool.collection(gatewayColl); const gatewayDef = this.processDefinition?.gateways?.[name]; if (gatewayDef.type === PARALLEL) { const gatewayInstance = await gatewayCol.findOne(processQueries .findGateway({ procId, name, status: ACTIVE })); const gatewayId = gatewayInstance?._id.toString() ?? null; this.logger.log({ level: 'silly', label: 'gatewayInstance', action: 'startNewGateway', message: `El gateway ${name} es de tipo PARALLEL, se valido instancia actual ${gatewayId}`, }); if (!gatewayId) { return this.createParallelGateway(procId, name, gatewayDef, origin); } return this.updateParallelGateway({ gatewayId, ...gatewayInstance }, name, gatewayDef, origin); } if (gatewayDef.type === EXCLUSIVE) { return this.createStandardGateway(procId, name, gatewayDef, origin); } if (gatewayDef.type === INCLUSIVE) { return this.createStandardGateway(procId, name, gatewayDef, origin); } return [null, null]; } async function createStandardGateway(procId, name, gatewayDef, origin) { const { destinations } = gatewayDef; this.logger.log({ level: 'silly', label: 'gatewayInstance', action: 'createStandardGateway', message: `Se inicia la creación de nueva instancia del gateway estandar ${name} para el proceso ${procId}`, }); const [nextErr, nextDestinations] = await this.resolveGatewayDestination(procId, name, gatewayDef); if (nextErr) { return [nextErr, null]; } this.logger.log({ level: 'silly', label: 'gatewayInstance', action: 'createStandardGateway', message: `Se evaluan las reglas del gateway ${name} para determinar los siguientes nodos a activar ${nextDestinations}`, }); const gatewayDestinations = {}; destinations.forEach((dest) => { gatewayDestinations[dest.name] = { creation: new Date(), type: dest.node?.type ?? null, status: (nextDestinations.includes(dest.name)) ? OUTPUT_SELECTED : OUTPUT_DISCARDED, }; }); const gatewayInstance = { ecosystem: this._ecosystem, procName: this.name, procId, name, sources: { [origin.name]: { ...origin, creation: new Date(), status: COMPLETE } }, destinations: gatewayDestinations, creation: new Date(), status: COMPLETE, }; this.logger.log({ level: 'debug', label: 'gatewayInstance', action: 'createStandardGateway', message: `Se almacena resultado de instancia del gateway ${name} ${JSON.stringify(gatewayInstance, null, 2)}`, }); const gatewayCol = this.rwPool.collection(gatewayColl); const actionResult = await gatewayCol.insertOne(gatewayInstance); const gatewayId = actionResult?.insertedId.toString(); if (!gatewayId) { this.logger.log({ level: 'error', label: 'gatewayInstance', action: 'createStandardGateway', message: `No fue posible la creación de una instancia del gateway ${name} para el proceso ${this.name}:${procId}`, }); const errorObj = this.errorObj.get(modErrs.newGatewayInstance.saveFailure); return [errorObj, null]; } for (let index = 0; index < nextDestinations.length; index++) { const nextName = nextDestinations[index]; const nodeDefinition = destinations.find(dest => dest.name === nextName); this.logger.log({ level: 'silly', label: 'gatewayInstance', action: 'createStandardGateway', message: `Se dispara la transicion ${nextName} del gateway ${name}:${gatewayId}`, }); if (!nodeDefinition) { this.logger.log({ level: 'error', label: 'gatewayInstance', action: 'createStandardGateway', message: `No se encontró nodo siguiente al gateway ${name} para el proceso ${this.name}:${procId}`, }); const errorObj = this.errorObj.get(modErrs.newGatewayInstance.saveFailure); return [errorObj, null]; } const { node: { name: nextNodeName, type: nextNodeType } } = nodeDefinition; this.logger.log({ level: 'silly', label: 'gatewayInstance', action: 'createStandardGateway', message: `Se inicia nodo ${nextNodeName} de tipo ${nextNodeType} desde gateway ${name}:${gatewayId}`, }); this.goToNode(procId, nextNodeName, nextNodeType, GATEWAY, name, gatewayId); } return [null, gatewayId]; } async function updateGatewayInstance(gatewayId, updateData) { const gatewayColRO = this.roPool.collection(gatewayColl); const gatewayColRW = this.rwPool.collection(gatewayColl); const gatewayInstance = await gatewayColRO.findOne(processQueries.findGateway(gatewayId)); if (!gatewayInstance) { const errorDetail = `Gateway ${gatewayId} del proceso ${this.name} no se encontró`; const errorObj = this.errMgr.get(modErrs.getGatewayInstance.notFound, errorDetail); return [errorObj, null]; } const actionResult = await gatewayColRW.updateOne(...processQueries .updateGateway(gatewayId, updateData)); const result = actionResult?.result?.nModified > 0; if (!result) { this.logger.log({ level: 'warn', label: 'gatewayInstance', action: 'updateTaskInstanceDB', message: `No se pudo efectuar modificación de la instancia de gateway ${gatewayId}`, }); } return [null, { result }]; } async function updateGatewayNextNode(procId, gatewayId, nextNodeId, nextNodeType, nextNodeName) { this.logger.log({ level: 'silly', label: 'gatewayInstance', action: 'updateGatewayNextNode', message: `Se actualiza el nodo siguiente al gateway ${gatewayId} del proceso ${procId}`, }); const updateData = { nextNodes: { [nextNodeName]: { id: nextNodeId, type: nextNodeType, }, }, }; return this.updateGatewayInstance(gatewayId, updateData); } async function createParallelGateway(procId, name, gatewayDef, origin) { this.logger.log({ level: 'silly', label: 'gatewayInstance', action: 'startNewGateway', message: `Se dispara la ejecución del gateway PARALLEL ${name} para la instacia ${procId}`, }); const { sources, destinations } = gatewayDef; const gatewaySources = {}; sources.forEach((src) => { const source = (src.name === origin.name) ? { ...origin, status: INPUT_COMPLETE, creation: new Date() } : { status: INPUT_PENDING, creation: new Date() }; gatewaySources[src.name] = source; }); const gatewayDestinations = {}; destinations.forEach((dest) => { gatewayDestinations[dest.name] = { creation: new Date(), type: dest.node?.type ?? null, status: OUTPUT_SELECTED, }; }); const gatewayInstance = { ecosystem: this._ecosystem, procName: this.name, procId, name, sources: gatewaySources, destinations: gatewayDestinations, creation: new Date(), status: ACTIVE, }; this.logger.log({ level: 'debug', label: 'gatewayInstance', action: 'createParallelGateway', message: `Se crea nuevo gateway ${name} con ${JSON.stringify(gatewayInstance, null, 2)}`, }); const gatewayCol = this.rwPool.collection(gatewayColl); const actionResult = await gatewayCol.insertOne(gatewayInstance); const gatewayId = actionResult?.insertedId.toString(); if (!gatewayId) { this.logger.log({ level: 'error', label: 'gatewayInstance', action: 'createGatewayInstanceDB', message: `No fue posible la creación de una instancia del gateway parallel ${name} para el proceso ${this.name}`, }); const errorObj = this.errorObj.get(modErrs.newGatewayInstance.saveFailure); return [errorObj, null]; } return [null, gatewayId]; } async function updateParallelGateway(gatewayInstance, name, gatewayDef, origin) { const { destinations } = gatewayDef; const { gatewayId, sources, procId } = gatewayInstance; this.logger.log({ level: 'silly', label: 'gatewayInstance', action: 'startNewGateway', message: `Se debe actualizar instancia existente del gateway PARALLEL ${name}:${gatewayId} para la instacia ${procId}`, }); const pendingSources = []; const sourcesNames = Object.keys(sources); for (let index = 0; index < sourcesNames.length; index++) { const sourceName = sourcesNames[index]; if (sources[sourceName].status === INPUT_PENDING) { pendingSources.push(sourceName); } } const gatewayCompleted = (pendingSources.length === 1 && pendingSources[0] === origin.name); const updateGateway = (gatewayCompleted) ? { status: COMPLETE, completion: new Date() } : null; const gatewayCol = this.rwPool.collection(gatewayColl); console.log(JSON.stringify(processQueries .updateGatewaySourceStatus(gatewayId, origin, INPUT_COMPLETE, updateGateway), null, 2)); const actionResult = await gatewayCol.updateOne(...processQueries .updateGatewaySourceStatus(gatewayId, origin, INPUT_COMPLETE, updateGateway)); const result = actionResult?.result?.nModified > 0; if (gatewayCompleted) { this.logger.log({ level: 'silly', label: 'gatewayInstance', action: 'startNewGateway', message: `Se completaron todas las entradas del gateway ${name}:${gatewayId} en la instacia ${procId}`, }); const { node: { name: nextNodeName, type: nextNodeType } } = destinations?.[0] ?? {}; this.goToNode(procId, nextNodeName, nextNodeType, GATEWAY, name, gatewayId); } return [null, { result }]; } async function resolveGatewayDestination(procId, name, gatewayDef) { this.logger.log({ level: 'silly', label: 'gatewayInstance', action: 'resolveGatewayDestination', message: `Inicio resolución destino gateway ${name} en proceso ${procId}`, }); const nextDestinations = []; const procCol = this.roPool.collection(processColl); const procInstance = await procCol.findOne(processQueries.findProcess(procId)); if (!procInstance) { this.logger.log({ level: 'error', label: 'gatewayInstance', action: 'resolveGatewayDestination', message: `No se localizó la instancia del proceso ${this.name}:${procId}`, }); const errorObj = this.errorObj.get(modErrs.newGatewayInstance.saveFailure); return [errorObj, null]; } const { destinations } = gatewayDef; for (let index = 0; index < destinations.length; index++) { const nextDestination = destinations[index]; const { name: destName, resolution, expression } = nextDestination; let execDestination = true; if (resolution === EXPRESSION) { const expressionToEval = expression.replace('$', 'procInstance.variables.'); execDestination = eval(expressionToEval); this.logger.log({ level: 'silly', label: 'gatewayInstance', action: 'resolveGatewayDestination', message: `Evaluacion de la expresión ${expression} asociada al destino ${destName} del gateway ${name} en la instacia de proceso ${procId} con resultado ${execDestination}`, }); } else if (resolution === CALLBACK) { const callback = this.getGatewayValudationFunction(name, destName); execDestination = await callback({ procId, ...procInstance }, name, destName); } if (execDestination) { nextDestinations.push(destName); if (gatewayDef.type === EXCLUSIVE) { this.logger.log({ level: 'silly', label: 'gatewayInstance', action: 'resolveGatewayDestination', message: `Validación exitos compuerta exclusiva para ${destName}`, }); return [null, nextDestinations]; } } } return [null, nextDestinations]; } async function getProcessGatewaysHistory(procId) { const gatewayCol = this.roPool.collection(gatewayColl); const gatewayList = await gatewayCol.find(processQueries .findGateway({ procId, status: COMPLETE })).toArray(); return [null, gatewayList]; } function defineGatewayDestinationCallback(name, destination, callback) { if (!this.gatewayDestinationValidations[name]) { this.gatewayDestinationValidations[name] = {}; } const gatewayValidations = this.gatewayDestinationValidations[name]; gatewayValidations[destination] = callback; } function getGatewayValudationFunction(name, destination) { return this.gatewayDestinationValidations?.[name]?.[destination] ?? null; } module.exports = { methods: { startNewGateway, getGatewayInstance, createStandardGateway, updateGatewayInstance, updateGatewayNextNode, createParallelGateway, updateParallelGateway, getProcessGatewaysHistory, resolveGatewayDestination, // Funciones adicionadas al cierre defineGatewayDestinationCallback, getGatewayValudationFunction, }, errors: modErrs, };