Source: api/controller/todo.controller.js

/**
 * @module todoController
 * @description Handles todo-related operations such as creating, updating, and deleting todos.
 */

const pool = require('../utility/connection.utility');
const jwt = require('jsonwebtoken');
require('dotenv').config();
const { validateCreateRequestBody, validateCreateTodoInput } = require('../utility/todo.utility');

/**
 * The function `createTodo` handles creating a new to-do item by validating input, inserting the data into the database, and sending a response.
 * @param {Object} req - The HTTP request object, containing information about the incoming request.
 * @param {Object} req.body - The body of the request, containing to-do item data.
 * @param {string} req.body.title - The title of the to-do item.
 * @param {string} req.body.description - The description of the to-do item.
 * @param {Object} req.headers - The headers of the request, containing the JWT token for authentication.
 * @param {string} req.headers.authorization - The JWT token for authentication.
 * @param {Object} res - The HTTP response object, used to send the response back to the client.
 * @returns {Promise<void>} - A promise that resolves to sending a response to the client.
 * @throws {Error} - Throws an error if validation fails or if there is an issue with database interaction.
 * @memberof module:todoController
 */
const createTodo = async (req, res) => {
	try {
		// Validate request body
		const requiredFields = ['title', 'description'];
		validateCreateRequestBody(req.body, requiredFields);

		// Extract the required fields from the request body
		const { title, description } = req.body;

		// Validate the to-do item input
		validateCreateTodoInput(title, description);

		// Extract the JWT token from the headers
		const token = req.headers.authorization;

		// Verify the JWT token
		const decoded = jwt.verify(token, process.env.JWT_SECRET);

		// Insert a new to-do item
		const SQL = `
            INSERT INTO todo (title, description, user_id, create_time)
            VALUES (?, ?, ?, date_format(now(), '%Y-%m-%d %H:%i:%s'))
        `;

		// Execute the query
		const [results] = await pool.query(SQL, [title, description, decoded.id]);

		// Send the response
		return res.status(200).json({ id: results.insertId, title, description });
	} catch (error) {
		console.error(error);
		return res.status(500).json({ error: error.message });
	}
};

/**
 * The function `updateTodo` handles updating an existing to-do item by validating input, updating the data in the database, and sending a response.
 * @param {Object} req - The HTTP request object, containing information about the incoming request.
 * @param {Object} req.body - The body of the request, containing to-do item data.
 * @param {string} req.body.title - The title of the to-do item.
 * @param {string} req.body.description - The description of the to-do item.
 * @param {Object} req.headers - The headers of the request, containing the JWT token for authentication.
 * @param {string} req.headers.authorization - The JWT token for authentication.
 * @param {Object} res - The HTTP response object, used to send the response back to the client.
 * @returns {Promise<void>} - A promise that resolves to sending a response to the client.
 * @throws {Error} - Throws an error if validation fails or if there is an issue with database interaction.
 * @memberof module:todoController
 */
const updateTodo = async (req, res) => {
	try {
		// Validate request body
		const requiredFields = ['title', 'description'];
		validateCreateRequestBody(req.body, requiredFields);

		// Extract the required fields from the request body
		const { title, description } = req.body;

		// Validate the to-do item input
		validateCreateTodoInput(title, description);

		// Extract the JWT token from the headers
		const token = req.headers.authorization;

		// Verify the JWT token
		const decoded = jwt.verify(token, process.env.JWT_SECRET);

		// Extract the to-do item ID from the request parameters
		const { id } = req.params;

		// Check if the user has permission to update the to-do item
		const [todo] = await pool.query('SELECT * FROM todo WHERE id = ?', [id]);

		if (todo.length === 0) {
			return res.status(404).json({ error: 'To-Do item not found' });
		}

		if (todo[0].user_id !== decoded.id) {
			return res.status(403).json({ message: 'Forbidden' });
		}

		// Update the to-do item
		const SQL = `
			UPDATE todo
			SET title = ?, description = ?
			WHERE id = ?
		`;

		// Execute the query
		await pool.query(SQL, [title, description, id]);

		// Send the response
		return res.status(200).json({ id, title, description });
	} catch (error) {
		console.error(error);
		return res.status(500).json({ error: error.message });
	}
};

/**
 * The function `deleteTodo` handles deleting an existing to-do item by deleting the data from the database and sending a response.
 * @param {Object} req - The HTTP request object, containing information about the incoming request.
 * @param {Object} req.headers - The headers of the request, containing the JWT token for authentication.
 * @param {string} req.headers.authorization - The JWT token for authentication.
 * @param {Object} res - The HTTP response object, used to send the response back to the client.
 * @returns {Promise<void>} - A promise that resolves to sending a response to the client.
 * @throws {Error} - Throws an error if there is an issue with database interaction.
 * @memberof module:todoController
 */
const deleteTodo = async (req, res) => {
	try {
		// Extract the JWT token from the headers
		const token = req.headers.authorization;

		// Verify the JWT token
		const decoded = jwt.verify(token, process.env.JWT_SECRET);

		// Extract the to-do item ID from the request parameters
		const { id } = req.params;

		// Check if the user has permission to delete the to-do item
		const [todo] = await pool.query('SELECT * FROM todo WHERE id = ?', [id]);

		if (todo.length === 0) {
			return res.status(404).json({ error: 'To-Do item not found' });
		}

		if (todo[0].user_id !== decoded.id) {
			return res.status(403).json({ message: 'Forbidden' });
		}

		// Delete the to-do item
		const SQL = 'DELETE FROM todo WHERE id = ?';

		// Execute the query
		await pool.query(SQL, [id]);

		// Send the response
		return res.status(204).send();
	} catch (error) {
		console.error(error);
		return res.status(500).json({ error: error.message });
	}
};

/**
 * Get To-Do Items

Get the list of to-do items using the following request:

GET /todos?page=1&limit=10

User must be authenticated to access the tasks and the response should be paginated. Respond with the list of to-do items along with the pagination details.

{
  "data": [
    {
      "id": 1,
      "title": "Buy groceries",
      "description": "Buy milk, eggs, bread"
    },
    {
      "id": 2,
      "title": "Pay bills",
      "description": "Pay electricity and water bills"
    }
  ],
  "page": 1,
  "limit": 10,
  "total": 2
}
 */

/**
 * The function `getTodos` handles retrieving a list of to-do items from the database and sending a response.
 * @param {Object} req - The HTTP request object, containing information about the incoming request.
 * @param {Object} req.query - The query parameters of the request, containing pagination details.
 * @param {number} req.query.page - The page number for pagination.
 * @param {number} req.query.limit - The limit of items per page for pagination.
 * @param {Object} req.headers - The headers of the request, containing the JWT token for authentication.
 * @param {string} req.headers.authorization - The JWT token for authentication.
 * @param {Object} res - The HTTP response object, used to send the response back to the client.
 * @returns {Promise<void>} - A promise that resolves to sending a response to the client.
 * @throws {Error} - Throws an error if there is an issue with database interaction.
 * @memberof module:todoController
 */
const getTodos = async (req, res) => {
	try {
		// Extract the JWT token from the headers
		const token = req.headers.authorization;

		// Verify the JWT token
		const decoded = jwt.verify(token, process.env.JWT_SECRET);

		// Extract the pagination parameters from the query
		const { page = 1, limit = 10 } = req.query;

		// Calculate the offset based on the page and limit
		const offset = (page - 1) * limit;

		// Get the total count of to-do items
		const [total] = await pool.query('SELECT COUNT(*) AS total FROM todo WHERE user_id = ?', [decoded.id]);

		// Get the list of to-do items
		const SQL = `
            SELECT id, title, description
            FROM todo
            WHERE user_id = ?
            LIMIT ?
            OFFSET ?
        `;

		// Execute the query
		const [todos] = await pool.query(SQL, [decoded.id, parseInt(limit), parseInt(offset)]);

		// Send the response
		return res.status(200).json({ data: todos, page, limit, total: total[0].total });
	} catch (error) {
		console.error(error);
		return res.status(500).json({ error: error.message });
	}
};

module.exports = {
	createTodo,
	updateTodo,
	deleteTodo,
	getTodos,
};