Issue Tracker – PUT request

Hello FCC community! I am working on the QA Issue Tracker project, and I am stuck on one of the PUT request FCC tests. Thank you in advance to anyone who takes a look! :slightly_smiling_face:

Here’s the test that I am failing:
You can send a PUT request to /api/issues/{projectname} with an _id and one or more fields to update. On success, the updated_on field should be updated, and returned should be { result: 'successfully updated', '_id': _id }

By adding some console logs and checking my database, it looks to me like this test should be passing, so I am not sure what I am missing here.

Here’s a link to my Repl, and for ease of reference, this is my code in routes/api.js:

'use strict';

require('dotenv').config();
const mongoose = require('mongoose');

mongoose.connect(process.env.DB, { useNewUrlParser: true, useUnifiedTopology: true });
const db = mongoose.connection;
db.on("error", (error) => console.log("Database connection error: ", error));
db.on("connected", () => console.log("Connected to database"));

const issueSchema = new mongoose.Schema({
  issue_title: {type: String, required: true },
  issue_text: {type: String, required: true },
  created_on: { type: String, default: Date() },
  updated_on: { type: String, default: Date() },
  created_by: {type: String, required: true },
  assigned_to: { type: String, default: "" },
  open: { type: Boolean, default: true },
  status_text: { type: String, default: "" }
});

const projectSchema = new mongoose.Schema({
  project_name: String,
  project_issue_tracker: [issueSchema]
});

const Project = mongoose.model("Project", projectSchema);
const Issue = mongoose.model("Issue", issueSchema);

module.exports = function (app) {

  app.route('/api/issues/:project')
  
    .get(function (req, res){
      let project = req.params.project;
      
      // create issue filter from query string
      let filter = {};
      for (const field in req.query) {
        filter[field] = req.query[field];
      }

      // retrieve project and return filtered issue tracker
      Project.findOne({ project_name: project }, (error, doc) => {
        if (error) res.send(`Error: ${error}`);
        if (!doc) res.send(`${project} does not exist.`);
        let filteredTracker = doc.project_issue_tracker.filter(issue => {
          for (const field in filter) {
            if (issue[field] !== filter[field]) return false;
          }
          return true;
        });
        res.json(filteredTracker);       
      });
    })
    
    .post(function (req, res){
      let project = req.params.project;

      // create new issue object
      const issue = new Issue({
        issue_title: req.body.issue_title,
        issue_text: req.body.issue_text,
        created_by: req.body.created_by,
        assigned_to: req.body.assigned_to,
        status_text: req.body.status_text
      });

      // display error message if required fields are missing
      if (!req.body.issue_title || !req.body.issue_text || !req.body.created_by) {
        res.json({ error: 'required field(s) missing' });
      } else {
        // if project exists, push issue to tracker
        // Otherwise, create new project and add issue to tracker
        Project.findOne({ project_name: project }, (error, doc) => {
          if (error) {
            res.send(`Error: ${error}`);
          } else if (doc) {
            doc.project_issue_tracker.push(issue);
            doc.save((error) => {
              error ? res.send(`Error: ${error}`) : res.json(issue);
            });
          } else {
            Project.create({
              project_name: project,
              project_issue_tracker: [issue]
            });
            res.json(issue);
          }
        });
      }
    })
    
    .put(function (req, res){
      let project = req.params.project;

      // create update object from query string
      let update = { updated_on: new Date() };
      
      for (const field in req.body) {
        if (req.body[field]) update[field] = req.body[field];
      }

      // if no _id is entered, send an error
      if (!req.body._id) {
        res.json({ error: 'missing _id' });
      } else if (Object.keys(update).length < 3) { // no fields provided: error
        res.json({ error: 'no update field(s) sent', '_id': req.body._id });
      } else {
        Project.findOne({ "project_name": project }, (error, doc) => {
          // find element in project_issue_tracker array with _id from req.body._id
          const index = doc.project_issue_tracker.findIndex(issue => issue._id.toString() === req.body._id);

          if (error || !doc || index === -1) {
            res.json({ error: 'could not update', '_id': req.body._id });
          } else {
            // update found element with updated fields using for-in loop
            for (const field in update) {
              doc.project_issue_tracker[index][field] = update[field];
            }
            // save updated document
            doc.save((error, doc) => {
              if (error || !doc) {
                res.json({ error: 'could not update', '_id': req.body._id });
              } else {
                res.json({ result: 'successfully updated', '_id': req.body._id });
              }
            });
          }
        });
      }
    })
    
    .delete(function (req, res){
      let project = req.params.project;

      // if no _id is entered, send an error message
      if (!req.body._id) {
        res.json({ error: 'missing _id' });
      } else {
        Project.findOne({ "project_name": project }, (error, doc) => {
          // find element in project_issue_tracker array with _id from req.body._id
          const index = doc.project_issue_tracker.findIndex(issue => issue._id.toString() === req.body._id);
          if (index === -1 || error) {
            res.json({ error: 'could not delete', '_id': req.body._id });
          } else {
            doc.project_issue_tracker.splice(index, 1);
            // save updated document
            doc.save(error => `${error} saving document.`);
            res.json({ result: 'successfully deleted', '_id': req.body._id });
          }
        });
      } 

    });
    
};

Hello there,

This is what I did to debug:

Project.findOne({ project_name: project }, (error, doc) => {
        if (error) res.send(`Error: ${error}`);
        if (!doc) res.send(`${project} does not exist.`);
        let filteredTracker = doc.project_issue_tracker.filter(issue => {
          for (const field in filter) {
            if (issue[field] !== filter[field]) return false;
          }
          return true;
        });
        console.log('filteredTracker: ', filteredTracker);
        res.json(filteredTracker);       
      });

Here are the results from the failing test:

Error: expected undefined to be an object at eval

Here is the test code:

  try {
    let initialData = {
      issue_title: 'Issue to be Updated',
      issue_text: 'Functional Test - Put target',
      created_by: 'fCC'
    };
    const url = getUserInput('url') + '/api/issues/fcc-project';
    const itemToUpdate = await $.post(url, initialData);
    const updateSucccess = await $.ajax({
      url: url,
      type: 'PUT',
      data: { _id: itemToUpdate._id, issue_text: 'New Issue Text' }
    });
    assert.isObject(updateSucccess);
    assert.deepEqual(updateSucccess, {
      result: 'successfully updated',
      _id: itemToUpdate._id
    });
    const getUpdatedId = await $.get(url + '?_id=' + itemToUpdate._id);
    assert.isArray(getUpdatedId);
    assert.isObject(getUpdatedId[0]);
    assert.isAbove(
      Date.parse(getUpdatedId[0].updated_on),
      Date.parse(getUpdatedId[0].created_on)
    );
  } catch (err) {
    throw new Error(err.responseText || err.message);
  }

Specifically, look at the GET request, and how you are finding/saving projects by _id.

Hope this helps

1 Like

Thank you for the thorough response! I now see that I’m not handling the _id field properly, so calling GET with an _id in the query does not return the issue. I think this should get me the rest of the way there – I’ll circle back when I fix the bug. I really appreciate the help!

1 Like