Expresso project - Timesheet PUT request

Hello all,

I hope you can help.

I have been working on the Expresso project and have run into an issue I cannot seem to solve.

For the PUT route in the Timesheet module I am asked to:

/api/employees/:employeeId/timesheets/:timesheetId*

  • PUT
    • Updates the timesheet with the specified timesheet ID using the information from the timesheet property of the request body and saves it to the database. Returns a 200 response with the updated timesheet on the timesheet property of the response body
    • If any required fields are missing, returns a 400 response
    • If an employee with the supplied employee ID doesn’t exist, returns a 404 response
    • If an timesheet with the supplied timesheet ID doesn’t exist, returns a 404 response

So far my tests have been passing with each route I have completed, however my tests are now failing the following tests:

PUT /api/employees/:employeeId/timesheets/:timesheetId
1) should update the timesheet with the given ID
2) should return a 200 status code after timesheet update
3) should return the updated timesheet after timesheet update
PUT /api/employees/1/timesheets/999 404 1.765 ms - 9
✓ should return a 404 status code for invalid timesheet IDs
PUT /api/employees/1/timesheets/2 400 1.144 ms - 11
✓ should return a 400 status code for invalid timesheet updates
PUT /api/employees/999/timesheets/1 404 1.084 ms - 9
✓ should return a 404 status code if an employee with the updated employee ID doesn’t exist

and returning the following error:

  1. PUT /api/employees/:employeeId/timesheets/:timesheetId
    should update the timesheet with the given ID:
    Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure “done()” is called; if returning a Promise, ensure it resolves. (/Users/chrismeade/Desktop/Projects/Expresso_project_Meagle_version/test/test.js)
    at listOnTimeout (node:internal/timers:556:17)
    at processTimers (node:internal/timers:499:7)

  2. PUT /api/employees/:employeeId/timesheets/:timesheetId
    should return a 200 status code after timesheet update:
    Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure “done()” is called; if returning a Promise, ensure it resolves. (/Users/chrismeade/Desktop/Projects/Expresso_project_Meagle_version/test/test.js)
    at listOnTimeout (node:internal/timers:556:17)
    at processTimers (node:internal/timers:499:7)

  3. PUT /api/employees/:employeeId/timesheets/:timesheetId
    should return the updated timesheet after timesheet update:
    Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure “done()” is called; if returning a Promise, ensure it resolves. (/Users/chrismeade/Desktop/Projects/Expresso_project_Meagle_version/test/test.js)
    at listOnTimeout (node:internal/timers:556:17)
    at processTimers (node:internal/timers:499:7)

My Timesheet.js module looks like this:

const express = require('express');
const timesheetsRouter = express.Router({ mergeParams: true });

const sqlite3 = require('sqlite3');
const db = new sqlite3.Database(process.env.TEST_DATABASE || './database.sqlite');

timesheetsRouter.param('timesheetId', (req, res, next, timesheetId) => {
    const sql = `SELECT * FROM Timesheet WHERE Timesheet.id = $timesheetId`;
    const value = { $timesheetId: timesheetId };

    db.get(sql, value, (error, timesheet) => {
        if(error) { next(error); }
        else if(timesheet) { req.timesheet = timesheet; next(); }
        else { res.sendStatus(404); }
    });
});

timesheetsRouter.get('/', (req, res, next) => {
    const sql = `SELECT * FROM Timesheet WHERE Timesheet.employee_id = $employeeId`;
    const value = { $employeeId: req.params.employeeId };
    db.all(sql, value, (error, timesheet) => {
        if(error) { next(error); }
        else { res.status(200).json({ timesheets: timesheet }); }
    });
});

timesheetsRouter.post('/', (req, res, next) => {
    const hours = req.body.timesheet.hours;
    const rate = req.body.timesheet.rate;
    const date = req.body.timesheet.date;
    const employeeId = req.params.employeeId;
 
        if(!hours || !rate || !date) { 
            return res.sendStatus(400);
        };
   
    db.run(`INSERT INTO Timesheet (hours, rate, date, employee_id) VALUES ($hours, $rate, $date, $employeeId)`,
    { $hours: hours, $rate: rate, $date: date, $employeeId: employeeId },
    function(error){
        if(error) { next(error); }
        else { db.get(`SELECT * FROM Timesheet WHERE Timesheet.id = ${this.lastID}`,
        (error, timesheet) => {
            res.status(201).json({ timesheet: timesheet });
        });
    }
});
});

timesheetsRouter.put('/:timesheetId', (req, res, next) => {
    const hours = req.body.timesheet.hours;
    const rate = req.body.timesheet.rate;
    const date = req.body.timesheet.date;
    const employeeId = req.params.employeeId;

    if(!hours || !rate || !date) { return res.sendStatus(400); };
    const sql = `UPDATE Timesheet SET hours = $hours, rate = $rate, date = $date, employee_id = $employeeId WHERE Timesheet.id = $timesheetId`;
    const values = {
        $hours: hours,
        $rate: rate,
        $date: date,
        $employeeId: employeeId,
        $timesheetId: req.params.timesheetId
           };
    db.run(sql, values, function(error){
        if(error) {
            next(error)
        } else {
            db.get(`SELECT * FROM Timesheet WHERE Timesheet.id = ${req.params.timesheetId}`),
            (error, timesheet) => {
                res.status(200).json({ timesheet: timesheet });
            };
        };
    }); 
});



module.exports = timesheetsRouter;

and my employees.js module:

const express = require('express');
const employeesRouter = express.Router();

const sqlite3 = require('sqlite3');
const db = new sqlite3.Database(process.env.TEST_DATABASE || './database.sqlite');

const timesheetsRouter = require('./timesheets');

employeesRouter.param('employeeId', (req, res, next, employeeId) => {
    const sql = `SELECT * FROM Employee WHERE Employee.id = $employeeId`;
    const values = { $employeeId: employeeId };

    db.get(sql, values, function(error, employee){
        if(error) { next(error); }
        else if(employee) { req.employee = employee; next(); }
        else { res.sendStatus(404); };

    });
});

employeesRouter.use('/:employeeId/:timesheets', timesheetsRouter);

employeesRouter.get('/', (req, res, next) => {
    const sql = `SELECT * FROM Employee WHERE is_current_employee = 1`;
    db.all(sql, (error, employees) => {
        if(error) return next(error);
        res.status(200).json({ employees: employees });
    });
});

employeesRouter.post('/', (req, res, next) => {
    const name = req.body.employee.name;
    const position = req.body.employee.position;
    const wage = req.body.employee.wage;
    const isCurrentEmployee = req.body.employee.isCurrentEmployee === 0 ? 0 : 1;

    if(!name || !position || !wage) return res.sendStatus(400);

    const sql = `INSERT INTO Employee (name, position, wage, is_current_employee) VALUES ($name, $position, $wage, $isCurrentEmployee)`;
    const values = { $name: name, $position: position, $wage: wage, $isCurrentEmployee: isCurrentEmployee };

    db.run(sql, values, function(error){
        if(error) {
            next(error);
        } else {
        db.get(`SELECT * FROM Employee WHERE Employee.id = ${this.lastID}`, (error, employee) => {
            res.status(201).json({ employee: employee });
        });
    }
    });
});

employeesRouter.get('/:employeeId', (req, res, next) => {
        res.status(200).json({ employee: req.employee })
    });

employeesRouter.put('/:employeeId', (req, res, next) => {
    const name = req.body.employee.name;
    const position = req.body.employee.position;
    const wage = req.body.employee.wage;
    const isCurrentEmployee = req.body.employee.isCurrentEmployee === 0 ? 0 : 1;

    if(!name || !position || !wage) return res.sendStatus(400);

    const sql = `UPDATE Employee SET name = $name, position = $position, wage = $wage, is_current_employee = $isCurrentEmployee WHERE Employee.id = $employeeId`;
    const values = { $name: name, $position: position, $wage: wage, $isCurrentEmployee: isCurrentEmployee, $employeeId: req.params.employeeId };

    db.run(sql, values, function(error){
        if(error) return next(error);
        db.get(`SELECT * FROM Employee WHERE Employee.id = ${req.params.employeeId}`,
        (error, employee) => {
            res.status(200).json({ employee: employee });
        });
    });
});

employeesRouter.delete('/:employeeId', (req, res, next) => {
    const sql = `UPDATE Employee SET is_current_employee = 0 WHERE Employee.id = $employeeId`;
    const value = { $employeeId: req.params.employeeId };

    db.run(sql, value, (error) => {
        if(error) return next(error);
        db.get(`SELECT * FROM Employee WHERE Employee.id = ${req.params.employeeId}`,
        (error, employee) => {
            res.status(200).json({ employee: employee });
        });
    });
});



module.exports = employeesRouter;

My thinking is that the error is occuring because something in the route.param()'s is taking too long to resolve. I have checked that I am calling next() in the correct places, however, that is as far as my knowledge will take me at the moment.

If anyone could help I’d be very grateful. Any questions/comments/criticism is welcome.

Thanks
Chris

Hello, this is one of those bugs that can be difficult to troubleshoot.

Here is the block of code that is causing the issue in timesheetsRouter.put()

You have a misplaced parenthesis that is sending your call to db.get() before you pass in your callback. Since your arrow function doesn’t get passed to db.get(), it never runs and the response is never sent back to the client.

I’m actually surprised a different error about syntax wasn’t raised, so I copied this block of code into my project and found that it didn’t cause a syntax error either. Makes it difficult to pinpoint.

Click if you need help spotting the misplaced parenthesis
db.get(`SELECT * FROM Timesheet WHERE Timesheet.id = ${req.params.timesheetId}`),
(error, timesheet) => {                              // this shouldn't be here ^
    res.status(200).json({ timesheet: timesheet });
}; // it should be before the ;

Thank you so much. The syntax is the one thing I didn’t pay much mind to , lesson learned!