UNPKG

tuain-bpm-lib

Version:

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

195 lines (185 loc) 7.35 kB
const moment = require('moment'); const InstanceEventData = require('./instance-event'); const { collections: { execution: { timers: timersColl, timerLocks: timerLocksColl } }, dbQueries: { execution: { process: processQueries } }, process: { elements: { event: EVENT }, timerStatus: { scheduled: SCHEDULED, executed: EXECUTED }, timersRefreshTime: REFRESH_TIME, scheduledTimeWindow: TIME_WINDOW, collisionWindow: COLLISION_WINDOW, emmiterTypes: { eventClose: EVENTCLOSE, eventComplete: EVENTCOMPLETE }, }, } = require('../../../config'); const modErrs = { timerEvent: { notDefined: ['01', 'Evento no se encuentra en la definición del proceso'], missingRequiredVariable: ['02', 'Variable requerida para el evento no se recibió'], }, newTimerInstance: { saveFailure: ['01', 'Error registrando evento en base de datos'], }, }; function addScheduledTimer(procId, name, timerId, wakeupDate, eventVars) { const currentlyScheduled = this.scheduledTimers.find(timer => timer.timerId === timerId); if (currentlyScheduled) { return currentlyScheduled; } const wakeUpMoment = moment(wakeupDate); const duration = moment.duration(wakeUpMoment.diff(moment())); const leftHours = duration.asHours(); const leftMinutes = duration.asMinutes(); const leftSeconds = duration.asSeconds() + (Math.random() * COLLISION_WINDOW); if (leftHours < TIME_WINDOW) { this.logger.log({ level: 'info', label: 'instanceEvent', action: 'triggerEvent', message: `Se programa el evento de timer ${timerId} para ser disparado en ${leftMinutes} minutos`, }); this.scheduledTimers.push({ procId, name, timerId }); setTimeout(() => this.triggerTimerEvent(procId, name, eventVars, timerId), leftSeconds * 1000); } // Ojo: Revisar qué se retorna return null; } async function refreshProcessTimers() { // Se obtienen los siguientes timers de la base de datos const timerCol = this.roPool.collection(timersColl); const beforeDate = moment().add(1, 'days').toDate(); const timerCursor = await timerCol.find(processQueries.findTimers(SCHEDULED, beforeDate)); while (await timerCursor.hasNext()) { const scheduledTimer = await timerCursor.next(); const { procId, name, eventVars, timerId, wakeupDate } = scheduledTimer; this.addScheduledTimer(procId, name, timerId, wakeupDate, eventVars); } } function updateTimers() { this.refreshProcessTimers(); clearInterval(this.refreshTimer); this.refreshTimer = setInterval(() => this.refreshProcessTimers, REFRESH_TIME); } async function saveTimerEvent(procId, name, wakeupDate, eventVars) { const eventVarNames = Object.keys(this.processDefinition?.events?.[name]?.variables); const newTimerInstance = { ecosystem: this._ecosystem, procId, processName: this.name, name, creation: new Date(), wakeupDate, variables: {}, status: SCHEDULED, }; for (let index = 0; index < eventVarNames.length; index++) { const eventVarName = eventVarNames[index]; newTimerInstance.variables[eventVarName] = eventVars[eventVarName] ?? null; } this.logger.log({ level: 'debug', label: 'timerInstance', action: 'createTimerInstanceDB', message: `Se crea nueva instancia de temporizador ${name} para la instacia ${procId} del proceso ${this.name} con ${JSON.stringify(newTimerInstance, null, 2)}`, }); const timerCol = this.rwPool.collection(timersColl); const actionResult = await timerCol.insertOne(newTimerInstance); const timerId = actionResult?.insertedId.toString(); if (!timerId) { this.logger.log({ level: 'error', label: 'timerInstance', action: 'createTimerInstanceDB', message: `No fue posible la creación de una instancia del temporizador ${name} para el proceso ${this.name}`, }); const errorObj = this.errorObj.get(modErrs.newTimerInstance.saveFailure); return [errorObj, null]; } return [null, timerId]; } async function registerTimerEvent(procId, name, eventVars) { const eventDef = this.processDefinition?.events?.[name]; if (!eventDef) { const message = `El evento ${name} no se encuentra en la definición del proceso`; this.logger.log({ level: 'error', label: 'timerInstance', action: 'registerTimerEvent', message, }); const errorObj = this.errMgr.get(modErrs.timerEvent.notDefined, message); return [errorObj, null]; } const { variables, time, timeUnit } = eventDef; const variableNames = Object.keys(variables); for (let index = 0; index < variableNames.length; index++) { const variableName = variableNames[index]; const { required } = variables[variableName]; if (required && !eventVars?.[variableName]) { const message = `Variable ${variableName} requerida para programar el evento timer ${name}`; this.logger.log({ level: 'error', label: 'timerInstance', action: 'timerEvent', message, }); const errorObj = this.errMgr.get(modErrs.timerEvent.missingRequiredVariable, message); return [errorObj, null]; } } const inputVarNames = Object.keys(eventVars); for (let index = 0; index < inputVarNames.length; index++) { const inputVarName = inputVarNames[index]; if (!variables[inputVarName]) { delete eventVars[inputVarName]; } } const wakeupDate = moment().add(time, timeUnit).toDate(); const [eventErr, timerId] = await this.saveTimerEvent(procId, name, wakeupDate, eventVars); if (eventErr) { return [eventErr, null]; } this.logger.log({ level: 'info', label: 'instanceEvent', action: 'timerEvent', message: `Se registra nueva instanca ${timerId} del temporizador ${name} en la instancia ${procId} del proceso ${this.name}`, }); this.addScheduledTimer(procId, name, timerId, wakeupDate, eventVars); return [null, true]; } async function triggerTimerEvent(procId, name, eventVars, timerId) { if (!this.lockInit) { this.rwPool.collection(timerLocksColl).createIndex({ timerId: -1 }, { expireAfterSeconds: 120 }); this.lockInit = true; } const timerLock = await this.rwPool.collection(timerLocksColl).insertOne({ procId, name, timerId }); const lockId = timerLock?.insertedId?.toString() ?? null; if (!lockId) { return [null, null]; } const eventDef = this.processDefinition?.events?.[name]; const processEvent = new InstanceEventData(this.name, procId, timerId, name, eventVars); await this.execCustomCallback(EVENTCOMPLETE, name, processEvent); await this.execCustomCallback(EVENTCLOSE, name, processEvent); const timerCol = this.rwPool.collection(timersColl); const actionResult = await timerCol.updateOne(...processQueries.changeTimerStatus(timerId, EXECUTED)); const result = actionResult?.result?.nModified > 0; if (!result) { this.logger.log({ level: 'warn', label: 'timerInstance', action: 'triggerTimerEvent', message: `No se pudo efectuar modificación del temporizador ${timerId}`, }); } this.goToNode(procId, eventDef.next.name, eventDef.next.type, EVENT, name, timerId); return [null, { result }]; } module.exports = { methods: { refreshProcessTimers, updateTimers, saveTimerEvent, registerTimerEvent, triggerTimerEvent, addScheduledTimer, }, errors: modErrs, };