diff --git a/api/model/book.js b/api/model/book.js new file mode 100644 index 0000000..657e4d4 --- /dev/null +++ b/api/model/book.js @@ -0,0 +1,15 @@ +var mongoose = require("mongoose"); +var Schema = mongoose.Schema; + +var schema = new Schema({ + title: { type: String }, + published: { type: Number }, + authors: [ + { + name: String, + dob: String + } + ] +}); + +module.exports = mongoose.model("Books", schema); diff --git a/api/routes/books.authors.js b/api/routes/books.authors.js new file mode 100644 index 0000000..f4da61d --- /dev/null +++ b/api/routes/books.authors.js @@ -0,0 +1,53 @@ +// Include :seriesId +const router = require("express").Router({ mergeParams: true }); +const Books = require("../model/book"); + +router.get("/", async (req, res, next) => { + const status = 200; + + const { authors } = await Books.findById(req.params.bookId).select("authors"); + res.json({ status, authors }); +}); + +router.get("/:id", async (req, res, next) => { + const status = 200; + + const books = await Books.findById(req.params.bookId); + + const author = books.authors.id(req.params.id); + + res.status(status).json({ status, author }); +}); + +router.post("/", async (req, res, next) => { + const status = 201; + const books = await Books.findById(req.params.bookId); + + books.authors.push(req.body); + await books.save(); + + const author = books.authors[books.authors.length - 1]; + res.status(status).json({ status, author }); +}); + +router.put("/:id", async (req, res, next) => { + const status = 200; + const books = await Books.findById(req.params.bookId); + + const author = books.authors.id(req.params.id); + Object.assign(author, req.body); + await books.save(); + + res.status(status).json({ status, author }); +}); + +router.delete("/:id", async (req, res, next) => { + const status = 200; + const books = await Books.findById(req.params.bookId); + const author = books.authors.id(req.params.id).remove(); + await books.save(); + + res.status(status).json({ status, author }); +}); + +module.exports = router; diff --git a/api/routes/books.js b/api/routes/books.js index 10a56a9..742638b 100644 --- a/api/routes/books.js +++ b/api/routes/books.js @@ -1,73 +1,80 @@ -const router = require('express').Router() -const { generate: generateId } = require('shortid') +const router = require("express").Router(); +const { generate: generateId } = require("shortid"); +const Books = require("../model/book"); const books = [ { - id: 'j9U3iNIQi', - title: 'The Colour of Magic', + id: "j9U3iNIQi", + title: "The Colour of Magic", published: 1983, authors: [ { - name: 'Sir Terry Pratchett', - dob: '04-28-1948' + name: "Sir Terry Pratchett", + dob: "04-28-1948" } ] }, { - id: 'ubQnXOfJV', - title: 'Stardust', + id: "ubQnXOfJV", + title: "Stardust", published: 1997, authors: [ { - name: 'Neil Gaiman', - dob: '11-10-1960' + name: "Neil Gaiman", + dob: "11-10-1960" } ] } ]; -router.get('/', (req, res, next) => { - const status = 200 - const response = books - - res.json({ status, response }) -}) +router.get("/", async (req, res, next) => { + const status = 200; -router.post('/', (req, res, next) => { - const status = 201 - - books.push({ id: generateId(), ...req.body }) - const response = books - - res.json({ status, response }) -}) + Books.find().then(response => { + res.json({ status, response }); + }); +}); +/************************************************* */ +router.post("/", async (req, res, next) => { + const status = 201; -router.get('/:id', (req, res, next) => { - const status = 200 - const response = books.find(({ id }) => id === req.params.id) + try { + Books.create(req.body).then(response => { + res.json({ status, response }); + }); + } catch (error) { + console.error(error); + const e = new Error("Sonthing went wrong"); - res.json({ status, response }) -}) - -router.put('/:id', (req, res, next) => { - const status = 200 - const response = { id: req.params.id, ...req.body } - const single = books.find(({ id }) => id === req.params.id) - const index = books.indexOf(single) - - books.splice(index, 1, response) - - res.json({ status, response }) -}) + e.status = 400; + next(e); + } +}); +/************************************************* */ +router.get("/:id", async (req, res, next) => { + const status = 200; -router.delete('/:id', (req, res, next) => { - const status = 200 - const response = books.find(({ id }) => id === req.params.id) - const index = books.indexOf(response) + Books.findById(req.params.id).then(response => { + res.json({ status, response }); + }); +}); +/************************************************* */ +router.put("/:id", async (req, res, next) => { + const status = 200; + const response = await Books.findOneAndUpdate( + { _id: req.params.id }, + { title: req.body.title, published: req.body.published }, + { new: true } + ); - books.splice(index, 1) + res.json({ status, response }); +}); +/************************************************* */ +router.delete("/:id", async (req, res, next) => { + const status = 200; + const response = await Books.findOneAndDelete({ _id: req.params.id }); - res.json({ status, response }) -}) + res.json({ status, response }); +}); -module.exports = router \ No newline at end of file +module.exports = router; diff --git a/app.js b/app.js index 075ce75..c74a97d 100644 --- a/app.js +++ b/app.js @@ -1,37 +1,41 @@ -const { MONGO_DB_CONNECTION, NODE_ENV, PORT } = process.env -const express = require('express') -const mongoose = require('mongoose') -const app = express() +const { MONGO_DB_CONNECTION, NODE_ENV, PORT } = process.env; +const express = require("express"); +const mongoose = require("mongoose"); +const app = express(); // Database Connection if (MONGO_DB_CONNECTION) { - mongoose.connect(MONGO_DB_CONNECTION, { useNewUrlParser: true }) - console.log('Connected to database...') + mongoose.connect(MONGO_DB_CONNECTION, { + useNewUrlParser: true, + useFindAndModify: false + }); + console.log("Connected to database..."); } else { - console.log('Could not connect to database!') + console.log("Could not connect to database!"); } // Application-level Middleware -if (NODE_ENV === 'development') app.use(require('morgan')('dev')) -app.use(require('body-parser').json()) +if (NODE_ENV === "development") app.use(require("morgan")("dev")); +app.use(require("body-parser").json()); // Routes -app.use('/api/books', require('./api/routes/books')) +app.use("/api/books", require("./api/routes/books")); +app.use("/api/books/:bookId/authors", require("./api/routes/books.authors")); // Not Found Handler app.use((req, res, next) => { - const error = new Error(`Could not ${req.method} ${req.path}`) - error.status = 404 - next(error) -}) + const error = new Error(`Could not ${req.method} ${req.path}`); + error.status = 404; + next(error); +}); // Error Handler app.use((err, req, res, next) => { - if (NODE_ENV === 'development') console.error(err) - const { message, status } = err - res.status(status).json({ status, message }) -}) + if (NODE_ENV === "development") console.error(err); + const { message, status } = err; + res.status(status).json({ status, message }); +}); // Open Connection -const listener = () => console.log(`Listening on Port ${PORT}!`) -app.listen(PORT, listener) +const listener = () => console.log(`Listening on Port ${PORT}!`); +app.listen(PORT, listener); diff --git a/package-lock.json b/package-lock.json index 5dcf387..38c21d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -855,8 +855,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -877,14 +876,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -899,20 +896,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -1029,8 +1023,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -1042,7 +1035,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -1057,7 +1049,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1065,14 +1056,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -1091,7 +1080,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -1172,8 +1160,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -1185,7 +1172,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -1271,8 +1257,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -1308,7 +1293,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -1328,7 +1312,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -1372,14 +1355,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -1775,9 +1756,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", + "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==" }, "lowercase-keys": { "version": "1.0.1", @@ -1895,9 +1876,9 @@ "dev": true }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -2491,9 +2472,9 @@ } }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -2872,38 +2853,15 @@ } }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "unique-string": { diff --git a/readme.md b/readme.md index 4a3bdf4..7b903f4 100644 --- a/readme.md +++ b/readme.md @@ -13,10 +13,11 @@ This exercise will assess your ability to build a fully working API with MongoDB As it stands, this repository contains an API for books. Update each route so that it uses a MongoDB database instead of recording the information in-memory. Additionally, add a new set of routes for the authors. For example: + ``` -GET /api/books/:bookId/authors -GET /api/books/:bookId/authors/:authorId -POST /api/books/:bookId/authors -PUT /api/books/:bookId/authors/:authorId +GET /api/books/:bookId/authors +GET /api/books/:bookId/authors/:authorId +POST /api/books/:bookId/authors +PUT /api/books/:bookId/authors/:authorId DELETE /api/books/:bookId/authors/:authorId ```