tuain-bpm-lib
Version:
Servicio de gestión de manejo de procesos de la plataforma Tuain
386 lines (364 loc) • 14.3 kB
JavaScript
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,
};