cklanac
2/16/2018 - 2:29 PM

Challenge 15: Mongo(ose) Relationships II

Challenge 15: Mongo(ose) Relationships II

In this challenge, you will add Tags to the Noteful app, populate the notes with tags, create routes and tests for the tags and update the routes and test for the notes.

Requirements

  • Create tag schema, update notes schema and then seed the database
  • Create tags endpoints and update notes endpoints
  • Update server.js to mount tags router
  • Update the client

Create/Update schemas and seed the database

Create Tag Model and tags.json

Since you created a similar schema and model for Notes and Folders we will keep the instructions brief.

Create a /models/tags.js file and create a tagsSchema with the following specs:

  • Create a /models/tags.js file for the Schema and Model.
  • Create a simple tagSchema and define a name field with the following criteria:
    • Type is String
    • Required is true
    • Unique is true
  • Create timestamps fields createdAt and updatedAt using the Mongoose timestamps option
  • Configure the Mongoose toObject option to transform the doc:
    • Include virtuals like id to show in the output
    • Suppress the __v version key
    • Delete the _id from the return object
  • Create Tag model using the tagSchema and export it

Update Seed Data

Next you will create Tags seed data and update Notes seed data with tags.

  • Create /db/seed/tags.json file
  • Add seed data like the following example.
[
  {
    "_id": "222222222222222222222200",
    "name": "breed"
  },
  {
    "_id": "222222222222222222222201",
    "name": "hybrid"
  },
  {
    "_id": "222222222222222222222202",
    "name": "domestic"
  },
  {
    "_id": "222222222222222222222203",
    "name": "feral"
  }
]

Update Note Model and update notes.json with Tags info

Now that you have defined a Tag Schema and Model, you can add a tags field to the Notes Schema.

In /models/note.js, update the noteSchema as follows:

  • Define a tags field:
    • Array of ObjectId
    • References Tag model
tags: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Tag' }],

Let's update the notes.json. Below is a sample note with its tags set to the _id for foo, bar, and baz. Update the sample data in your seed data file with tags info

  {
    "_id": "000000000000000000000003",
    "title": "7 things lady gaga has in common with cats",
    "content": "Posuere sollicitudin aliquam ultrices sagittis orci a. Feugiat sed lectus vestibulum mattis ullamcorper velit. Odio pellentesque diam volutpat commodo sed egestas egestas fringilla. Velit egestas dui id ornare arcu odio. Molestie at elementum eu facilisis sed odio morbi. Tempor nec feugiat nisl pretium. At tempor commodo ullamcorper a lacus. Egestas dui id ornare arcu odio. Id cursus metus aliquam eleifend. Vitae sapien pellentesque habitant morbi tristique. Dis parturient montes nascetur ridiculus. Egestas egestas fringilla phasellus faucibus scelerisque eleifend. Aliquam faucibus purus in massa tempor nec feugiat nisl.",
    "folderId": "111111111111111111111101",
    "tags": ["222222222222222222222200", "222222222222222222222201", "222222222222222222222202"]
  },

Update seed-database.js

Update the /utils/seed-database.js file to insert tags and create an index. The index Tag.createIndexes() call tells mongo to create an index immediately which is used enforce the unique rule on Tag names.

Below is a simple and reliable way to seed the database and trigger indexing. Update your /utils/seed-database.js file to require the Tag model and seed data. And then update it to insert Tags and create an index as shown below.


// ... REMOVED FOR BREVITY

mongoose.connect(MONGODB_URI)
  .then(() => mongoose.connection.db.dropDatabase())
  .then(() => {
    return Promise.all([
      Note.insertMany(seedNotes),
      Folder.insertMany(seedFolders),
      Tag.insertMany(seedTags),
      Folder.createIndexes(),
      Tag.createIndexes()
    ]);
  })

// ... REMOVED FOR BREVITY

Confirm the seed process worked correctly by verifying the data using Mongo shell or your favorite database GUI.

Create Tag endpoints and Integration Tests

Your next challenge is to create Folder endpoints and integration tests.

Create a new /routes/tags.js file to hold the endpoints and mount it on server.js. In the file, create the following CRUD endpoints.

  • GET all /tags
    • Sort the response by name
  • GET /tags by id
    • Add validation that protects against invalid Mongo ObjectIds and prevents unnecessary database queries.
    • Add condition that checks the result and returns a 200 response with the result or a 404 Not Found
  • POST /tags to create a new tag
    • Add validation that protects against missing name field
    • A successful insert returns a location header and a 201 status
    • Add condition that checks for a duplicate key error with code 11000 and responds with a helpful error message
  • PUT /tags by id to update a tag
    • Add validation which protects against missing name field
    • Add validation which protects against an invalid ObjectId
    • Add condition that checks the result and returns a 200 response with the result or a 404 Not Found
      • Ensure you are returning the updated/modified document, not the document prior to the update
    • Add condition that checks for a duplicate key error with code 11000 and responds with a helpful error message
  • DELETE /tags by id deletes the tag AND removes it from the notes collection
    • Remove the tag
    • Using $pull, remove the tag from the tags array in the notes collection.
    • Add condition that checks the result and returns a 200 response with the result or a 204 status

Update /routes/notes.js endpoints

  • GET all /notes
    • Add tags to the response
    • Use .populate() to populate the tags array
    • Capture the incoming tagId and conditionally add it to the database query filter
  • GET /notes by id
    • Add tags to the response
    • Use .populate() to populate the tags array
  • POST /notes
    • Capture tags from request body
    • Verify each tag id is a valid ObjectId and, if necessary, return a user-friendly response with a 400 status
    • Save tags along with the other note fields
  • PUT /notes
    • Capture tags from request body
    • Verify each tag id is a valid ObjectId and, if necessary, return a user-friendly response with a 400 status
    • Save tags along with the other note fields
  • DELETE /notes
    • No change :)

Create /test/tags.js

Create a /test/tags.js file, the basic structure in similar to /test/folders.js. Update the require statements and the Mocha life-cycle hooks to use Tag models and tags seed data.

Create tests to verify the functionality of the /tags endpoints. As your work through the tests, check the code coverage for clues on which aspects of your code you have validated and which still needs tests.

Update /test/notes.js

Don't forget to update /test/notes.js. You added new features to these endpoints earlier and that functionality needs to be tested to ensure it continues to work properly.

  • Update the require statements to include tags.
  • Update the life-cycle hooks to seed the database with notes, folders and tags
  • Update the tests to verify tag related features

Check your code coverage. Are there any lines of code which are not covered by tests? Create tests for any outstanding features.

Update the Client

Activate tags search on client

Finally, uncomment the tags search on /public/index.js.

  Promise.all([
    api.search('/api/notes'),
    api.search('/api/folders'),
    api.search('/api/tags')
  ])