Issue Tracker - [Error: expected undefined to be an object]

Hi,
I finished the project and it works as expected.
When I test my project link on the FCC project page though it fails to pass these test:

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 }.

When the PUT request sent to /api/issues/{projectname} does not include update fields, the return value is { error: 'no update field(s) sent', '_id': _id }. On any other error, the return value is { error: 'could not update', '_id': _id }.

With the following errors:

[Error: expected undefined to be an object]
[Error: expected { error: 'could not update', …(1) } to deeply equal { …(2) }]

I’m really stuck cause I don’t understand what is happening.

Here’s the project link to test:
Emi Issue Tracker

Here’s my api.js:

"use strict";
const mongoose = require("mongoose");

// connect to DB
mongoose.connect(process.env.MONGO_URI, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

// create project Schema
const projectSchema = new mongoose.Schema(
  {
    title: {
      type: String,
      required: true,
    },
    issues: Array,
  },
  { versionKey: false },
); // avoid the attribute "_v" when creating doc

// create project Model
const ProjectModel = new mongoose.model("Project", projectSchema);

// create issue Schema
const issueSchema = new mongoose.Schema(
  {
    issue_title: {
      type: String,
      required: true,
    },
    issue_text: {
      type: String,
      required: true,
    },
    created_on: {
      type: Date,
      default: new Date(Date.now()).toISOString(),
    },
    updated_on: {
      type: Date,
      default: new Date(Date.now()).toISOString(),
    },
    created_by: {
      type: String,
      required: true,
    },
    assigned_to: {
      type: String,
      required: false,
      default: "",
    },
    open: {
      type: Boolean,
      default: true,
    },
    status_text: {
      type: String,
      required: false,
      default: "",
    },
  },
  { versionKey: false },
); // avoid the attribute "_v" when creating doc

// create issue Model
const IssueModel = new mongoose.model("Issue", issueSchema);

// function to set 'true'/'false' to boolean
function fixBoolean(obj) {
  for (const [filter, value] of Object.entries(obj)) {
    if (value === "true") {
      obj[filter] = true;
    } else if (value === "false") {
      obj[filter] = false;
    }
  }
  return obj;
}

module.exports = function (app) {
  app
    .route("/api/issues/:project")

    .get(function (req, res) {
      let project = req.params.project;
      // if there are no filters return false else return obj with filters
      let filters =
        Object.keys({ ...req.query }).length === 0
          ? false
          : fixBoolean({ ...req.query });
      // number of criterias to satisfy
      let criterias = Object.keys(filters).length;
      ProjectModel.findOne({ title: project }).then((data) => {
        // if project found
        if (data) {
          console.log("\nProject found!");
          // if filters active
          if (filters) {
            console.log("Filters found! ->", filters);
            // for every issue in project
            let filteredArr = data.issues.filter((item, i) => {
              console.log(
                "\nChecking issue",
                i + 1,
                "of",
                data.issues.length,
                "\n",
              );
              let count = 0;
              // loop through issue keys and compare it to filters key
              for (const [filter, value] of Object.entries(item)) {
                // if filter is matching with issue values
                if (item[filter] === filters[filter]) {
                  count++;
                  console.log(
                    filter,
                    item[filter],
                    "correspond to",
                    filter,
                    filters[filter],
                    "✅",
                    " (",
                    count,
                    "/",
                    criterias,
                    ")",
                  );
                  if (count === criterias) {
                    // if all the filter criterias are met add issues to the arr
                    console.log(
                      "\n✅✅✅ All filters match this issue, adding to array... ✅✅✅ ",
                    );
                    return item;
                  }
                } else {
                  console.log(
                    filter,
                    item[filter],
                    "does not correspond to",
                    filter,
                    filters[filter],
                    "❌",
                  );
                }
              }
            });
            res.json(filteredArr);
          } else {
            // else return all issues
            res.json(data.issues);
          }
        } else {
          // create new project
          console.log("Project not found, creating a new one...");
          let new_project = new ProjectModel({
            title: project,
            issues: [],
          });
          // save project in DB
          new_project.save().then((data) => {
            console.log("Project added!");
            // send response
            res.json(data.issues);
          });
        }
      });
    })

    .post(function (req, res) {
      let project = req.params.project;
      // find project
      ProjectModel.findOne({ title: project }).then((currentProject) => {
        // create new issue
        let issue = new IssueModel({
          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,
        });
        // if project found
        if (currentProject) {
          // save issue
          issue.save().then(
            (data) => {
              // push new issue in project
              currentProject.issues.push(data);
              currentProject.save();
              // return created issue
              res.json(data);
            },
            (error) => {
              res.json({ error: "required field(s) missing" });
            },
          );
        } else {
          // create new project
          console.log("Project not found, creating a new one...");
          let new_project = new ProjectModel({
            title: project,
            issues: [],
          });
          // save project in DB
          new_project.save().then((newProject) => {
            console.log("Project added!");
            // save issue
            issue.save().then(
              (data) => {
                // push new issue in project
                newProject.issues.push(data);
                newProject.save();
                // return created issue
                res.json(data);
              },
              (error) => {
                res.json({ error: "required field(s) missing" });
              },
            );
          });
        }
      });
    })

    .put(function (req, res) {
      let project = req.params.project;
      let issueId = req.body._id || false;
      // find project
      ProjectModel.findOne({ title: project }).then(
        (currentProject) => {
          // if project exists
          if (currentProject) {
            console.log("Project to update found! ✅");
            // if issue is not an empty string
            if (issueId) {
              // find issue
              IssueModel.findOne({ _id: issueId }).then(
                (issue) => {
                  // if found
                  if (issue) {
                    console.log("Issue found! ✅");
                    // strip body of _id field
                    let { _id, ...bodyWithoutId } = req.body;
                    // check if at least one update is sent (EXCEPT _ID)
                    let atLeastOneUpdate =
                      Object.values(bodyWithoutId).filter(
                        (item) => item.length > 0,
                      ).length > 0;
                    // if at least one update is sent
                    if (atLeastOneUpdate) {
                      console.log("At least one update sent found ✅");
                      // UPDATING FIELDS WITH TRUTHY VALUES ------
                      issue.issue_title =
                        req.body.issue_title || issue.issue_title;
                      issue.issue_text =
                        req.body.issue_text || issue.issue_text;
                      issue.created_by =
                        req.body.created_by || issue.created_by;
                      issue.assigned_to =
                        req.body.assigned_to || issue.assigned_to;
                      issue.status_text =
                        req.body.status_text || issue.status_text;
                      issue.open = req.body.open || issue.open;
                      issue.updated_on = new Date(Date.now()).toISOString();
                      // ------- UPDATING FIELDS WITH TRUTHY VALUES
                      // save edits
                      issue.save().then((data) => {
                        console.log("Updating issue...", issueId);
                        // find index of issue in project issues array
                        let indexOfIssue = currentProject.issues.findIndex(
                          (item) => item._id.toString() === issueId,
                        );
                        console.log(
                          "Issue",
                          indexOfIssue + 1,
                          "of",
                          currentProject.issues.length,
                        );
                        // replace issue in project issues array
                        currentProject.issues[indexOfIssue] = data;
                        // save edits
                        currentProject.save().then((data) => {
                          console.log("Issue saved in project array! ✅");
                          // return json
                          res.json({
                            result: "successfully updated",
                            _id: issueId,
                          });
                        });
                      });
                    } else {
                      console.log("No update fields sent! ❌");
                      res.json({ error: "no update field(s) sent", _id: issueId });
                    }
                  } else {
                    console.log("Issue not found! ❌");
                    res.json({ error: "could not update", _id: issueId });
                  }
                },
                (error) => {
                  res.json({ error: "could not update", _id: issueId });
                },
              );
            } else {
              console.log("Missing issue id! ❌");
              res.json({ error: "missing _id" });
            }
          } else {
            console.log("Project to update not found! ❌");
            res.json({ error: "could not update", _id: issueId });
          }
        },
        (error) => {
          res.json({ error: "could not update", _id: issueId });
        },
      );
    })

    .delete(function (req, res) {
      let project = req.params.project;
      let issueId = req.body._id || false;
      // if issue id is provided
      if (issueId) {
        // find project
        ProjectModel.findOne({ title: project }).then(
          (currentProject) => {
            // if project found
            if (currentProject) {
              console.log("Project to delete issue within is found! ✅");
              // find issue
              IssueModel.findOneAndDelete({ _id: issueId }).then(
                (issue) => {
                  // if issue found
                  if (issue) {
                    console.log("Deleting issue...", issueId);
                    // return currentProject without that issue
                    currentProject.issues = currentProject.issues.filter(
                      (item) => item._id.toString() != issueId,
                    );
                    // save edits
                    currentProject.save().then(
                      (data) => {
                        console.log(
                          "Issue",
                          issueId,
                          "successfully deleted from project",
                          project,
                          "✅",
                        );
                        res.json({
                          result: "successfully deleted",
                          _id: issueId,
                        });
                      },
                      (error) => {
                        res.json({ error: "could not delete", _id: issueId });
                      },
                    );
                  } else {
                    console.log("Issue to delete not found! ❌");
                    res.json({ error: "could not delete", _id: issueId });
                  }
                },
                (error) => {
                  res.json({ error: "could not delete", _id: issueId });
                },
                (error) => {
                  res.json({ error: "could not delete", _id: issueId });
                },
              );
            } else {
              console.log("Project to delete issue within is not found! ❌");
              res.json({ error: "project not found", project: project });
            }
          },
          (error) => {
            res.json({ error: "could not delete", _id: issueId });
          },
        );
      } else {
        console.log("Missing issue id! ❌");
        res.json({ error: "missing _id" });
      }
    });
};

Any hint is appreciated.
Thanks

There is a GET after the first PUT you are not handling. Look in the browser network tab when you submit (the GET between the PUTs). It isn’t really explicitly stated as part of the requirements other than the filter query requirement.

I believe one of your PUT responses should be ‘no update field(s) sent’ but that never happens. I think it is because the test is using a bad id (non-existing id) to test for both 'no update field(s) sent' and 'could not update' but your logic for sending 'no update field(s) sent' depends on a valid issue id.

The tests:

https://github.com/freeCodeCamp/freeCodeCamp/blob/main/curriculum/challenges/english/06-quality-assurance/quality-assurance-projects/issue-tracker.md