tuain-bpm-lib
Version:
Servicio de gestión de manejo de procesos de la plataforma Tuain
345 lines (324 loc) • 15.2 kB
JavaScript
const InstanceEventData = require('./instance-event');
const {
collections: {
execution: { tasks: tasksColl, processes: procsCol },
},
dbQueries: {
execution: { process: processQueries },
},
process: {
elements: { task: TASK },
// taskTypes: { manual: MANUAL, sync: SYNC, async: ASYNC },
elementStatus: { active: ACTIVE, complete: COMPLETE, closed: CLOSED },
emmiterTypes: { taskStart: TASKSTART, taskComplete: TASKCOMPLETE, taskClose: TASKCLOSE },
},
} = require('../../../config');
const modErrs = {
startNewTask: {
notDefined: ['01', 'Tarea no existe en la definición del proceso'],
notAssigned: ['02', 'Solicitud de tarea manual sin asignación'],
},
newTaskInstance: {
saveFailure: ['01', 'Error registrando la instancia de la tarea en base de datos'],
},
getTaskInstance: {
notFound: ['01', 'No se encotró la instancia de la tarea solicitada'],
},
updateProc: {
notFound: ['01', 'No se encotró la instancia de la tarea solicitada'],
},
completeTask: {
missingRequiredVariable: ['01', 'Variable requerida para completar tarea no definida'],
},
};
async function getTaskInstance(taskId, procId = null, name = null, status = null) {
let message = `[BPM] : [getTaskInstance]: Inicio obtención tarea ${name}/${taskId} sobre ${procId} en estado ${status}`;
this.logger.log({ level: 'degug', label: 'task', action: 'getTaskInstance', message });
const taskCol = this.roPool.collection(tasksColl);
const findTaskQuery = processQueries.findTaskInstance(taskId, procId, name, status);
message = JSON.stringify(findTaskQuery);
this.logger.log({ level: 'degug', label: 'task', action: 'getTaskInstance', message });
const taskInstance = await taskCol.findOne(findTaskQuery);
if (!taskInstance) {
const errorDetail = `Tarea ${taskId} del proceso ${this.name} no se encontró`;
const errorObj = this.errMgr.get(modErrs.getTaskInstance.notFound, errorDetail);
return [errorObj, null];
}
return [null, taskInstance];
}
async function createTaskInstance(procId, name, origin, userRole, groupRole, userId, groupId) {
let message = `[BPM] : [createTaskInstance]: Inicio creación nueva tarea ${name} sobre ${procId} desde ${origin.name}`;
this.logger.log({ level: 'silly', label: 'gatewayInstance', action: 'startNewTask', message });
const taskDef = this.processDefinition?.tasks?.[name];
if (!taskDef) {
const message = `[BPM] : La tarea ${name} no se encuentra en la definición del proceso`;
this.logger.log({ level: 'error', label: 'instanceTask', action: 'startNewTask', message });
const errorObj = this.errMgr.get(modErrs.startNewTask.notDefined, message);
return [errorObj, null];
}
const [, existTaskRes] = await this.getTaskInstance(null, procId, name, ACTIVE);
if (existTaskRes) {
let message = `[BPM] : Existe tarea activa ${name} sobre ${procId} con id: ${existTaskRes._id}`;
this.logger.log({ level: 'debug', label: 'gatewayInstance', action: 'startNewTask', message });
return [null, existTaskRes._id];
}
const taskCol = this.rwPool.collection(tasksColl);
const taskVarNames = Object.keys(taskDef.variables ?? {});
let userAssigned = null;
let groupAssigned = null;
let userTypeAssigned = null;
let groupTypeAssigned = null;
if (userId || userRole) {
userAssigned = userId ?? userRole;
userTypeAssigned = userId ? 'user' : 'role';
}
if (groupId || groupRole) {
groupAssigned = groupId ?? groupRole;
groupTypeAssigned = groupId ? 'group' : 'role';
}
const newTaskInstance = {
ecosystem: this._ecosystem,
procName: this.name,
procId,
name,
source: origin,
variables: {},
creation: new Date(),
status: ACTIVE,
userAssigned,
groupAssigned,
userTypeAssigned,
groupTypeAssigned,
};
for (let index = 0; index < taskVarNames.length; index++) {
const eventVarName = taskVarNames[index];
newTaskInstance.variables[eventVarName] = null;
}
message = `[BPM] : Se almacena tarea ${name} sobre ${this.name}/${procId} ${JSON.stringify(newTaskInstance, null, 2)}`;
this.logger.log({ level: 'silly', label: 'taskInstance', action: 'createTaskInstanceDB', message });
const actionResult = await taskCol.insertOne(newTaskInstance);
const taskId = actionResult?.insertedId.toString();
if (!taskId) {
message = `[BPM] : No fue posible la creación de una instancia de la tarea ${name} para el proceso ${this.name}`;
this.logger.log({ level: 'error', label: 'taskInstance', action: 'createTaskInstanceDB', message });
const errorObj = this.errorObj.get(modErrs.newTaskInstance.saveFailure);
return [errorObj, null];
}
return [null, taskId];
}
async function updateTaskNextNode(procId, taskId, nextNodeId, nextNodeType, nextNodeName) {
let message = `[BPM] : Se actualiza nodo siguiente a la tarea ${taskId}/${procId} como id:${nextNodeId} type:${nextNodeType} name:${nextNodeName}`;
this.logger.log({ level: 'silly', label: 'taskInstance', action: 'updateTaskInstanceDB', message });
const updateData = {
nextNodes: {
[nextNodeName]: {
id: nextNodeId,
type: nextNodeType,
},
},
};
return this.updateTaskInstance(taskId, updateData);
}
async function updateTaskInstance(taskId, updateData) {
const taskCol = this.rwPool.collection(tasksColl);
const taskInstance = await taskCol.findOne(processQueries.findTaskInstance(taskId));
if (!taskInstance) {
const errorDetail = `Instancia ${taskId} del proceso ${this.name} no existe`;
const errorObj = this.errMgr.get(modErrs.updateProc.notFound, errorDetail);
return [errorObj, null];
}
const actionResult = await taskCol.updateOne(...processQueries.updateTask(taskId, updateData));
const result = actionResult?.result?.nModified > 0;
if (!result) {
let message = `[BPM] : No se pudo efectuar modificación de la instancia de tarea ${taskId}`;
this.logger.log({ level: 'silly', label: 'taskInstance', action: 'updateTaskInstanceDB', message });
}
return [null, { result }];
}
async function startNewTask(procId, name, origin, userRole = null, groupRole = null, userId = null, groupId = null) {
let message = `[BPM] : [startNewTask]: Se inicia nueva tarea ${name}/${procId} desde ${origin}`;
this.logger.log({ level: 'silly', label: 'instanceTask', action: 'startNewTask', message });
const taskDef = this.processDefinition?.tasks?.[name];
if (!taskDef) {
message = `[BPM] : La tarea ${name} no se encuentra en la definición del proceso`;
this.logger.log({ level: 'error', label: 'instanceTask', action: 'startNewTask', message });
const errorObj = this.errMgr.get(modErrs.startNewTask.notDefined, message);
return [errorObj, null];
}
const [eventErr, taskId] = await this.createTaskInstance(procId, name, origin, userRole, groupRole, userId, groupId);
if (eventErr) {
return [eventErr, null];
}
message = `[BPM] : Nueva tarea ${name}/${taskId} en proceso ${this.name}/${procId}`;
this.logger.log({ level: 'info', label: 'instanceTask', action: 'startNewTask', message });
const taskEvent = new InstanceEventData(this.name, procId, taskId, name);
const callbakResult = (await this.execCustomCallback(TASKSTART, name, taskEvent)) ?? {};
return [null, taskId];
}
async function cancelActiveTasks(procId) {
const taskCol = this.rwPool.collection(tasksColl);
let result = false;
if (procId) {
const actionResult = await taskCol.updateMany({ procId, status: 'active' }, { $set: { status: 'canceled' } });
result = actionResult?.result?.nModified > 0;
}
return [null, { result }];
}
// async function checkCustomResult() {}
async function assignTaskToUser(taskId, procId, name, userId) {
const taskCol = this.rwPool.collection(tasksColl);
const locateInfo = taskId || { procId, name };
const actionResult = await taskCol.updateOne(...processQueries.updateTask(locateInfo, { userAssigned: userId }));
const result = actionResult?.result?.nModified > 0;
return [null, { result }];
}
async function stopTask(inputTaskId, procId, name) {
const [getTaskErr, taskInstance] = await this.getTaskInstance(inputTaskId, procId, name, ACTIVE);
if (getTaskErr) {
return [getTaskErr, null];
}
const taskId = taskInstance._id.toString();
return this.updateTaskInstance(taskId, { status: CLOSED });
}
async function assignTaskToGroup(taskId, procId, name, groupId) {
const taskCol = this.rwPool.collection(tasksColl);
const locateInfo = taskId || { procId, name };
const actionResult = await taskCol.updateOne(...processQueries.updateTask(locateInfo, { groupAssigned: groupId }));
const result = actionResult?.result?.nModified > 0;
return [null, { result }];
}
async function completeTask(inputTaskId, inputVars = {}, processId = null, taskName = null) {
const [getTaskErr, taskInstance] = await this.getTaskInstance(inputTaskId, processId, taskName, ACTIVE);
if (getTaskErr) {
return [getTaskErr, null];
}
const { name, procId, procName, variables: taskCurrentVars } = taskInstance;
const taskId = taskInstance._id.toString();
let message = `[BPM] : [completeTask]: Inicio completar tarea ${name}/${taskId} sobre ${procId}`;
this.logger.log({ level: 'silly', label: 'taskInstance', action: 'completeTask', message });
const taskVariables = { ...taskCurrentVars, ...inputVars };
if (procName !== this.name) {
message = `[BPM] : La tarea ${name} asociada a la instancia ${taskId} no pertenece a una instancia del proceso ${this.name}`;
const errorObj = this.errMgr.get(modErrs.completeTask.notDefined, message);
this.logger.log({ level: 'error', label: 'taskInstance', action: 'completeTask', message });
return [errorObj, null];
}
const taskDef = this.processDefinition?.tasks?.[name];
if (!taskDef) {
message = `[BPM] : La tarea ${name} asociada a la instancia ${taskId} no está definida para el proceso ${this.name}`;
const errorObj = this.errMgr.get(modErrs.completeTask.notDefined, message);
this.logger.log({ level: 'error', label: 'taskInstance', action: 'completeTask', message });
return [errorObj, null];
}
const { variables = {}, next } = taskDef;
const variableNames = Object.keys(variables);
for (let index = 0; index < variableNames.length; index++) {
const variableName = variableNames[index];
const { required } = variables[variableName];
if (required && (typeof taskVariables?.[variableName] === 'undefined' || taskVariables?.[variableName] === null)) {
message = `[BPM] : Variable ${variableName} requerida para finalizar la tarea ${name}`;
this.logger.log({ level: 'error', label: 'taskInstance', action: 'completeTask', message });
const errorObj = this.errMgr.get(modErrs.completeTask.missingRequiredVariable, message);
return [errorObj, null];
}
}
const taskVarNames = Object.keys(taskVariables);
for (let index = 0; index < taskVarNames.length; index++) {
const taskVarName = taskVarNames[index];
if (!variables[taskVarName]) {
delete taskVariables[taskVarName];
}
}
const taskEvent = new InstanceEventData(this.name, procId, taskId, name, taskVariables);
const completeResult = (await this.execCustomCallback(TASKCOMPLETE, name, taskEvent)) ?? {};
const closeResult = (await this.execCustomCallback(TASKCLOSE, name, taskEvent)) ?? {};
this.updateTaskInstance(taskId, { completion: new Date(), status: COMPLETE, variables: taskVariables });
this.goToNode(procId, next.name, next.type, TASK, name, taskId);
return [null, { ...completeResult, ...closeResult }];
}
async function getProcessActiveTasks(procId, name, status = ACTIVE) {
const taskCol = this.roPool.collection(tasksColl);
const userTask = await taskCol.findOne(processQueries.findTask({ name, procId, status }));
return [null, userTask];
}
async function getUserProcess(userType, userId, status = ACTIVE, name = null) {
const procCol = this.roPool.collection(procsCol);
const taskCol = this.roPool.collection(tasksColl);
const procDetail = await procCol.findOne(processQueries.findProcessByUser(userType, userId));
if (!procDetail) {
const errorDetail = `Usuario con id ${userId} no tiene procesos asociados con el tipo ${userType}`;
const errorObj = this.errMgr.get(modErrs.updateProc.notFound, errorDetail);
return [errorObj, null];
}
const userTask = await taskCol.find(processQueries.findTask({ procId: procDetail?._id?.toString(), status, name })).toArray();
return [null, userTask];
}
async function getActiveTasks(name, status = ACTIVE, pageNumber = null, pageSize = null) {
const taskCol = this.roPool.collection(tasksColl);
let userTasks;
let totalTasks;
if (pageNumber && pageSize) {
const numberSkip = (pageNumber - 1) * pageSize;
const userTasksCursor = await taskCol.find(processQueries.findTask({ name, status })).sort({ creation: -1 });
totalTasks = await userTasksCursor.count();
userTasks = await userTasksCursor.skip(numberSkip).limit(pageSize).toArray();
} else {
userTasks = await taskCol.find(processQueries.findTask({ name, status })).toArray();
}
return [null, { userTasks, totalTasks }];
}
async function getUserTasks(userId, taskName = null, status = ACTIVE, pageNumber = null, pageSize = null) {
const taskCol = this.roPool.collection(tasksColl);
let userTasks;
let totalTasks;
if (pageNumber && pageSize) {
const numberSkip = (pageNumber - 1) * pageSize;
const query = { userAssigned: userId, status };
taskName && Object.assign(query, { name: taskName });
const userTasksCursor = await taskCol.find(processQueries.findTask(query));
totalTasks = await userTasksCursor.count();
userTasks = await userTasksCursor.skip(numberSkip).limit(pageSize).toArray();
} else {
const query = { userAssigned: userId, status };
taskName && Object.assign(query, { name: taskName });
userTasks = await taskCol.find(processQueries.findTask(query)).toArray();
}
return [null, { userTasks, totalTasks }];
}
async function getGroupsTasks(groupIdArray) {
const taskCol = this.roPool.collection(tasksColl);
const groupsTasks = await taskCol.find(processQueries.findGroupsTasks(groupIdArray)).toArray();
return [null, groupsTasks];
}
async function getProcessTaskHistory(procId) {
const taskCol = this.roPool.collection(tasksColl);
const userTasks = await taskCol.find(processQueries.findTask({ procId, status: COMPLETE })).toArray();
return [null, userTasks];
}
async function getTasksInProgress(procId) {
const taskCol = this.roPool.collection(tasksColl);
const userTasks = await taskCol.find(processQueries.findTask({ procId, status: ACTIVE })).toArray();
return [null, userTasks];
}
module.exports = {
methods: {
getTaskInstance,
createTaskInstance,
updateTaskNextNode,
updateTaskInstance,
startNewTask,
cancelActiveTasks,
assignTaskToUser,
assignTaskToGroup,
stopTask,
completeTask,
getProcessActiveTasks,
getUserProcess,
getActiveTasks,
getUserTasks,
getGroupsTasks,
getProcessTaskHistory,
getTasksInProgress,
},
errors: modErrs,
};