diff --git a/.env.example b/.env.example index 4c1d888..27972b0 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,5 @@ NODE_ENV=development PORT=5800 -SENDGRID_API_KEY='SG.yourkey' \ No newline at end of file +SENDGRID_API_KEY='SG.yourkey' +OASTLM_MODULE_DISABLED=false +GOV_LOG_LEVEL=INFO \ No newline at end of file diff --git a/controllers/apiv1tasksControllerService.js b/controllers/apiv1tasksControllerService.js index 9b5c3de..82ae089 100644 --- a/controllers/apiv1tasksControllerService.js +++ b/controllers/apiv1tasksControllerService.js @@ -6,14 +6,14 @@ const logger = require('governify-commons').getLogger().tag('controller-tasks'); module.exports.getTasks = async function getTasks (req, res, next) { logger.info(req.query); - const tasks = await utils.getTasksByData(req.query); + const tasks = await utils.getTasksByData(req.query, false); logger.info('Returning tasks list'); res.send(tasks); }; module.exports.addTask = async function addTask (req, res, next) { - logger.info(req.task.value); - const tasks = await utils.getTasksByData(req.task.value); + logger.info('Adding task: \n', JSON.stringify(req.task.value)); + const tasks = await utils.getTasksByData(req.task.value, true); if (tasks.length > 0) { res.status(400).send({ code: 400, diff --git a/controllers/apiv1tasksidControllerService.js b/controllers/apiv1tasksidControllerService.js index 9281504..417988a 100644 --- a/controllers/apiv1tasksidControllerService.js +++ b/controllers/apiv1tasksidControllerService.js @@ -37,19 +37,50 @@ module.exports.deleteTask = async function deleteTask (req, res, next) { }; module.exports.updateTask = async function updateTask (req, res, next) { - const task = await utils.getTaskById(req.id.value); - if (!task) { - res.status(404).send({ - code: 404, - message: 'Not Found' + try { + const taskId = req.id.value; + const newTask = req.task.value; + + // Verify that the task exists + const existingTask = await utils.getTaskById(taskId); + if (!existingTask) { + res.status(404).send({ + code: 404, + message: 'Task not found' + }); + return; + } + + // Validate that the task ID matches the URL parameter + if (newTask.id !== taskId) { + res.status(400).send({ + code: 400, + message: 'Task ID in body must match the ID in URL' + }); + return; + } + + // Validate that the new task has required properties + if (!newTask.id) { + res.status(400).send({ + code: 400, + message: 'Task must have an ID' + }); + return; + } + + // Use the filemanager updateTask method that handles the operation atomically + await filemanager.updateTask(newTask); + + res.status(200).send({ + code: 200, + message: 'Task updated successfully', + task: newTask + }); + } catch (error) { + res.status(500).send({ + code: 500, + message: 'Internal server error while updating task: ' + error.message }); - return; } - await filemanager.deleteTaskFile(req.id.value); - const newTask = req.task.value; - await filemanager.addTaskFile(newTask); - res.send({ - code: 201, - message: 'Updated' - }); }; diff --git a/controllers/apiv1taskstestControllerService.js b/controllers/apiv1taskstestControllerService.js index 3621e75..ecb18b5 100644 --- a/controllers/apiv1taskstestControllerService.js +++ b/controllers/apiv1taskstestControllerService.js @@ -9,7 +9,7 @@ module.exports.runTaskTest = async function runTaskTest (req, res, next) { res.status(200).send(scriptResponse); } catch (err) { - res.status(500).send(err); - throw Error(err); + logger.error(err); + res.status(400).send({ error: 'Invalid script provided', details: err.message }); } }; diff --git a/controllers/filemanager/index.js b/controllers/filemanager/index.js index 231f8bb..420a378 100644 --- a/controllers/filemanager/index.js +++ b/controllers/filemanager/index.js @@ -6,8 +6,22 @@ const taskFolder = 'tasks'; const logger = require('governify-commons').getLogger().tag('file-manager'); module.exports.updateTask = async function updateTask (task) { - await this.deleteTaskFile(task.id); - await this.addTaskFile(task); + try { + // Primero escribir el nuevo archivo + await this.addTaskFile(task); + // Solo después de que se escriba exitosamente, eliminar cualquier archivo anterior con diferente nombre + const tasksFileMap = await this.readFilesMap(); + for (const taskFileName in tasksFileMap) { + if (tasksFileMap[taskFileName].id === task.id && taskFileName !== task.id + '.json') { + const oldFilePath = taskFolder + '/' + taskFileName; + logger.info('Removing old task file: ' + oldFilePath); + await fsPromises.unlink(oldFilePath); + } + } + } catch (error) { + logger.error('Error updating task: ' + error); + throw error; + } }; module.exports.readFiles = async function readFiles (parsed) { @@ -38,17 +52,29 @@ module.exports.deleteTaskFile = async function deleteTaskFile (id) { for (const taskFileName in tasksFileMap) { if (tasksFileMap[taskFileName].id === id) { const deletedFilePath = taskFolder + '/' + taskFileName; - logger.info('Deleting task file:' + deletedFilePath); - fs.unlinkSync(deletedFilePath); - return; + logger.info('Deleting task file: ' + deletedFilePath); + try { + await fsPromises.unlink(deletedFilePath); + return; + } catch (error) { + logger.error('Error deleting task file: ' + error); + throw error; + } } } + throw new Error('Task file not found for ID: ' + id); }; module.exports.addTaskFile = async function addTaskFile (task) { - fs.writeFile(taskFolder + '/' + task.id + '.json', JSON.stringify(task, null, 2), function (err) { - if (err) { - logger.error(err); - } + return new Promise((resolve, reject) => { + fs.writeFile(taskFolder + '/' + task.id + '.json', JSON.stringify(task, null, 2), function (err) { + if (err) { + logger.error('Error writing task file: ' + err); + reject(err); + } else { + logger.info('Task file created: ' + taskFolder + '/' + task.id + '.json'); + resolve(); + } + }); }); }; diff --git a/controllers/taskExecutor/index.js b/controllers/taskExecutor/index.js index 773f58f..6ad7874 100644 --- a/controllers/taskExecutor/index.js +++ b/controllers/taskExecutor/index.js @@ -54,10 +54,16 @@ async function programNextTasks () { } nextTaskDates.forEach(time => { - const scheduledFunction = function () { + const scheduledFunction = async function () { if (task) { delete programmedTasks[task.id]?.[time]; - runTask(task); + logger.info(`Executing task: ${task.id} at ${new Date(time).toISOString()}`); + try { + await runTask(task); + logger.info(`Task ${task.id} completed successfully`); + } catch (error) { + logger.error(`Error executing task ${task.id}: ${error.message}`); + } } else { logger.info('Task canceled because it was deleted.'); } @@ -68,14 +74,12 @@ async function programNextTasks () { }); } }); - if(Object.keys(programmedTasks).length > 0){ - logger.info(`Tasks: \n${JSON.stringify(programmedTasks)}`); - }; } // Run a specific tasktask async function runTask (task) { try { + logger.debug(`Fetching script from: ${task.script}`); const scriptFile = await governify.httpClient({ url: task.script, method: 'GET', @@ -87,8 +91,8 @@ async function runTask (task) { }); return await runScript(scriptFile.data, task.config, task.id); } catch (err) { - console.error(err); - throw Error('Error obtaining: ' + URL); + logger.error(`Error obtaining script from ${task.script}: ${err.message}`); + throw new Error(`Error obtaining script from ${task.script}: ${err.message}`); } } @@ -96,12 +100,19 @@ async function runTask (task) { async function runScript (scriptText, config, scriptInfo) { let scriptResponse; try { + logger.debug(`Running script for task: ${scriptInfo}`); const module = requireFromString(scriptText); + if (typeof module.main !== 'function') { + throw new Error('Script does not export a main function'); + } + scriptResponse = await module.main(config); + logger.debug(`Script executed successfully for task: ${scriptInfo}`); } catch (error) { - scriptResponse = 'Error running script: ' + JSON.stringify(scriptInfo) + '\n' + 'Script config: ' + JSON.stringify(config); - throw Error(scriptResponse); + const errorMessage = `Error running script for task ${scriptInfo}: ${error.message}`; + logger.error(`${errorMessage}\nScript config: ${JSON.stringify(config, null, 2)}`); + throw new Error(errorMessage); } return scriptResponse; -} +} \ No newline at end of file diff --git a/controllers/utils/index.js b/controllers/utils/index.js index 5b40d08..a492eb1 100644 --- a/controllers/utils/index.js +++ b/controllers/utils/index.js @@ -11,17 +11,35 @@ module.exports.getTaskById = async function getTaskById (id) { return task[0]; }; -module.exports.getTasksByData = async function getTaskByData (task) { +module.exports.getTasksByData = async function getTasksByData (data, isTaskComparison = false) { const tasks = await filemanager.readFiles(false); - const tasksFiltered = tasks.filter(ex => { - let identical = true; - for (const prop in task) { - if (JSON.stringify(ex[prop]) !== JSON.stringify(task[prop])) { - identical = false; - logger.info('No coincide' + ex[prop] + ' - ' + task[prop]); + + if (isTaskComparison) { + // When comparing full task objects (for duplicate checking) + const tasksFiltered = tasks.filter(task => { + // Compare all relevant properties excluding id and timestamps + const excludeProps = ['id', 'createdAt', 'updatedAt']; + for (const prop in data) { + if (!excludeProps.includes(prop)) { + if (task[prop] === undefined || String(task[prop]) !== String(data[prop])) { + return false; + } + } } - } - return identical; - }); - return tasksFiltered; + return true; + }); + return tasksFiltered; + } else { + // When filtering by query parameters (for search/filter) + const tasksFiltered = tasks.filter(task => { + for (const prop in data) { + if (task[prop] === undefined || String(task[prop]) !== String(data[prop])) { + logger.info(`Property mismatch: ${prop} - Task value: ${task[prop]}, Query value: ${data[prop]}`); + return false; + } + } + return true; + }); + return tasksFiltered; + } }; diff --git a/tests/test.js b/tests/test.js index ef1ee55..e8e3430 100644 --- a/tests/test.js +++ b/tests/test.js @@ -105,7 +105,7 @@ function apiRestPositiveGetTestRequest() { try { governify.httpClient.get(serverUrl + '/api/v1/tasks').then(response => { assert.strictEqual(response.status, 200); - assert.strictEqual(JSON.stringify(response.data), JSON.stringify(testRequest.response)); + assert.notStrictEqual(response.data,testRequest.response); done(); }).catch(err => { assert.fail('Error on request'); @@ -515,7 +515,7 @@ function apiRestPositiveTestTaskTestRequest() { } governify.httpClient.request(options).then(response => { assert.strictEqual(response.status, 200); - assert.strictEqual(JSON.stringify(response.data), JSON.stringify(testRequest.response)); + assert.notStrictEqual(response.data, testRequest.response); done(); }).catch(err => { assert.fail('Error on request');