Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
NODE_ENV=development
PORT=5800
SENDGRID_API_KEY='SG.yourkey'
SENDGRID_API_KEY='SG.yourkey'
OASTLM_MODULE_DISABLED=false
GOV_LOG_LEVEL=INFO
6 changes: 3 additions & 3 deletions controllers/apiv1tasksControllerService.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
57 changes: 44 additions & 13 deletions controllers/apiv1tasksidControllerService.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
});
};
4 changes: 2 additions & 2 deletions controllers/apiv1taskstestControllerService.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
}
};
44 changes: 35 additions & 9 deletions controllers/filemanager/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
}
});
});
};
31 changes: 21 additions & 10 deletions controllers/taskExecutor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.');
}
Expand All @@ -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',
Expand All @@ -87,21 +91,28 @@ 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}`);
}
}

// Run raw JS file with a specific configuration.
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;
}
}
40 changes: 29 additions & 11 deletions controllers/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
};
4 changes: 2 additions & 2 deletions tests/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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');
Expand Down