From 5677f941fd18cfe75e8720ae89be3c9a6dd74f55 Mon Sep 17 00:00:00 2001 From: Loaye Date: Wed, 2 Aug 2017 20:49:59 -0700 Subject: [PATCH 1/3] SCAFFOLD: Basic scaffold --- .eslintrc | 21 ++++++++++++++++++ .gitignore | 1 + README.md | 60 ---------------------------------------------------- package.json | 38 +++++++++++++++++++++++++++++++++ server.js | 27 +++++++++++++++++++++++ 5 files changed, 87 insertions(+), 60 deletions(-) create mode 100644 .eslintrc create mode 100644 .gitignore create mode 100644 package.json create mode 100644 server.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..b663d77 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,21 @@ +{ + "rules": { + "no-console": "off", + "indent": [ "error", 2 ], + "quotes": [ "error", "single" ], + "semi": ["error", "always"], + "linebreak-style": [ "error", "unix" ] + }, + "env": { + "es6": true, + "node": true, + "mocha": true, + "jasmine": true + }, + "ecmaFeatures": { + "modules": true, + "experimentalObjectRestSpread": true, + "impliedStrict": true + }, + "extends": "eslint:recommended" +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/README.md b/README.md index 37f3822..e69de29 100644 --- a/README.md +++ b/README.md @@ -1,60 +0,0 @@ -![CF](https://camo.githubusercontent.com/70edab54bba80edb7493cad3135e9606781cbb6b/687474703a2f2f692e696d6775722e636f6d2f377635415363382e706e67) 13: Single Resource Mongo and Express API -=== - -## Submission Instructions - * fork this repository & create a new branch for your work - * write all of your code in a directory named `lab-` + `` **e.g.** `lab-susan` - * push to your repository - * submit a pull request to this repository - * submit a link to your PR in canvas - * write a question and observation on canvas - -## Learning Objectives -* students will be able to work with the MongoDB database management system -* students will understand the primary concepts of working with a NoSQL database management system -* students will be able to create custom data models *(schemas)* through the use of mongoose.js -* students will be able to use mongoose.js helper methods for interacting with their database persistence layer - -## Requirements -#### Configuration -* `package.json` -* `.eslintrc` -* `.gitignore` -* `README.md` - * your `README.md` should include detailed instructions on how to use your API - -#### Feature Tasks -* create an HTTP Server using `express` -* create a resource **model** of your choice that uses `mongoose.Schema` and `mongoose.model` -* use the `body-parser` express middleware to parse the `req` body on `POST` and `PUT` requests -* use the npm `debug` module to log the functions and methods that are being used in your application -* use the express `Router` to create a route for doing **RESTFUL CRUD** operations against your _model_ - -## Server Endpoints -### `/api/resource-name` -* `POST` request - * should pass data as stringifed JSON in the body of a post request to create a new resource - -### `/api/resource-name/:id` -* `GET` request - * should pass the id of a resource through the url endpoint to get a resource - * **this should use `req.params`, not querystring parameters** -* `PUT` request - * should pass data as stringifed JSON in the body of a put request to update a pre-existing resource -* `DELETE` request - * should pass the id of a resource though the url endpoint to delete a resource - * **this should use `req.params`** - -### Tests -* create a test that will ensure that your API returns a status code of 404 for routes that have not been registered -* create a series of tests to ensure that your `/api/resource-name` endpoint responds as described for each condition below: - * `GET` - test 200, returns a resource with a valid body - * `GET` - test 404, respond with 'not found' for valid requests made with an id that was not found - * `PUT` - test 200, returns a resource with an updated body - * `PUT` - test 400, responds with 'bad request' if no request body was provided - * `PUT` - test 404, responds with 'not found' for valid requests made with an id that was not found - * `POST` - test 400, responds with 'bad request' if no request body was provided - * `POST` - test 200, returns a resource for requests made with a valid body - -### Bonus -* **2pts:** a `GET` request to `/api/resource-name` should return an array of stored resources diff --git a/package.json b/package.json new file mode 100644 index 0000000..6a47e90 --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "13-mongodb", + "version": "1.0.0", + "description": "![CF](https://camo.githubusercontent.com/70edab54bba80edb7493cad3135e9606781cbb6b/687474703a2f2f692e696d6775722e636f6d2f377635415363382e706e67) 13: Single Resource Mongo and Express API ===", + "main": "server.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "DEBUG='pokemon*' mocha", + "start": "DEBUG='pokemon*' node server.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Loaye/13-mongodb.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/Loaye/13-mongodb/issues" + }, + "homepage": "https://github.com/Loaye/13-mongodb#readme", + "dependencies": { + "bluebird": "^3.5.0", + "body-parser": "^1.17.2", + "cors": "^2.8.4", + "debug": "^2.6.8", + "express": "^4.15.3", + "mongoose": "^4.11.5", + "morgan": "^1.8.2" + }, + "devDependencies": { + "chai": "^4.1.0", + "mocha": "^3.5.0", + "superagent": "^3.5.2" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..0ea79f8 --- /dev/null +++ b/server.js @@ -0,0 +1,27 @@ +'use strict'; + +const express = require('express'); +const morgan = require('morgan'); +const cors = require('cors'); +const Promise = require('bluebird'); +const mongoose = require('mongoose'); +const debug = require('debug')('pokemon:server'); + +const bandRouter = require('./route/pokemon-route'); +const errors = require('./lib/error-middleware.js'); + +const app = express(); +const PORT = process.env.PORT || 3000; +const MONGODB_URI = 'mongodb://localhost/pokemonlist'; + +mongoose.Promise = Promise; +mongoose.connect(MONGODB_URI); + +app.use(cors()); +app.use(morgan('dev')); +app.use(bandRouter); +app.use(errors); + +app.listen(PORT, () => { + debug(`listening on ${PORT}`); +}); \ No newline at end of file From d601325e122629ebe4d3e6a984befdf94623a6e7 Mon Sep 17 00:00:00 2001 From: Loaye Date: Wed, 2 Aug 2017 21:04:13 -0700 Subject: [PATCH 2/3] implements tests --- lib/error-middleware.js | 21 ++++++ model/pokemon.js | 11 +++ npm-debug.log | 45 ++++++++++++ route/pokemon-route-js | 42 +++++++++++ server.js | 4 +- test/pokemon-route-test.js | 146 +++++++++++++++++++++++++++++++++++++ 6 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 lib/error-middleware.js create mode 100644 model/pokemon.js create mode 100644 npm-debug.log create mode 100644 route/pokemon-route-js create mode 100644 test/pokemon-route-test.js diff --git a/lib/error-middleware.js b/lib/error-middleware.js new file mode 100644 index 0000000..ce088ae --- /dev/null +++ b/lib/error-middleware.js @@ -0,0 +1,21 @@ +'use strict'; + +const createError = require('http-errors'); +const debug = require('debug')('song:error-middleware'); + +module.exports = function(err, req, res, next) { + console.error(err.message); + + if (err.status) { + debug('user error'); + + res.status(err.status).send(err.name); + next(); + return; + } + + debug('server error'); + err = createError(500, err.message); + res.status(err.status).send(err.name); + next(); +}; \ No newline at end of file diff --git a/model/pokemon.js b/model/pokemon.js new file mode 100644 index 0000000..bb2f88f --- /dev/null +++ b/model/pokemon.js @@ -0,0 +1,11 @@ +'use strict'; + +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; + +const pokemonSchema = Schema({ + name: { type: String, required: true }, + origin: { type: String, required: true } +}); + +module.exports = mongoose.model('pokemon', pokemonSchema); \ No newline at end of file diff --git a/npm-debug.log b/npm-debug.log new file mode 100644 index 0000000..c7a4002 --- /dev/null +++ b/npm-debug.log @@ -0,0 +1,45 @@ +0 info it worked if it ends with ok +1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'run', 'test' ] +2 info using npm@3.10.10 +3 info using node@v6.11.0 +4 verbose run-script [ 'pretest', 'test', 'posttest' ] +5 info lifecycle 13-mongodb@1.0.0~pretest: 13-mongodb@1.0.0 +6 silly lifecycle 13-mongodb@1.0.0~pretest: no script for pretest, continuing +7 info lifecycle 13-mongodb@1.0.0~test: 13-mongodb@1.0.0 +8 verbose lifecycle 13-mongodb@1.0.0~test: unsafe-perm in lifecycle true +9 verbose lifecycle 13-mongodb@1.0.0~test: PATH: /usr/local/lib/node_modules/npm/bin/node-gyp-bin:/Users/Jake/CodeFellows/401/13-mongodb/node_modules/.bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin +10 verbose lifecycle 13-mongodb@1.0.0~test: CWD: /Users/Jake/CodeFellows/401/13-mongodb +11 silly lifecycle 13-mongodb@1.0.0~test: Args: [ '-c', 'DEBUG=\'pokemon*\' mocha' ] +12 silly lifecycle 13-mongodb@1.0.0~test: Returned: code: 1 signal: null +13 info lifecycle 13-mongodb@1.0.0~test: Failed to exec test script +14 verbose stack Error: 13-mongodb@1.0.0 test: `DEBUG='pokemon*' mocha` +14 verbose stack Exit status 1 +14 verbose stack at EventEmitter. (/usr/local/lib/node_modules/npm/lib/utils/lifecycle.js:255:16) +14 verbose stack at emitTwo (events.js:106:13) +14 verbose stack at EventEmitter.emit (events.js:191:7) +14 verbose stack at ChildProcess. (/usr/local/lib/node_modules/npm/lib/utils/spawn.js:40:14) +14 verbose stack at emitTwo (events.js:106:13) +14 verbose stack at ChildProcess.emit (events.js:191:7) +14 verbose stack at maybeClose (internal/child_process.js:891:16) +14 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:226:5) +15 verbose pkgid 13-mongodb@1.0.0 +16 verbose cwd /Users/Jake/CodeFellows/401/13-mongodb +17 error Darwin 16.6.0 +18 error argv "/usr/local/bin/node" "/usr/local/bin/npm" "run" "test" +19 error node v6.11.0 +20 error npm v3.10.10 +21 error code ELIFECYCLE +22 error 13-mongodb@1.0.0 test: `DEBUG='pokemon*' mocha` +22 error Exit status 1 +23 error Failed at the 13-mongodb@1.0.0 test script 'DEBUG='pokemon*' mocha'. +23 error Make sure you have the latest version of node.js and npm installed. +23 error If you do, this is most likely a problem with the 13-mongodb package, +23 error not with npm itself. +23 error Tell the author that this fails on your system: +23 error DEBUG='pokemon*' mocha +23 error You can get information on how to open an issue for this project with: +23 error npm bugs 13-mongodb +23 error Or if that isn't available, you can get their info via: +23 error npm owner ls 13-mongodb +23 error There is likely additional logging output above. +24 verbose exit [ 1, true ] diff --git a/route/pokemon-route-js b/route/pokemon-route-js new file mode 100644 index 0000000..74efbe7 --- /dev/null +++ b/route/pokemon-route-js @@ -0,0 +1,42 @@ +'use strict'; + +const Router = require('express').Router; +const jsonParser = require('body-parser').json(); +const debug = require('debug')('song:pokemon-route'); +const createError = require('http-errors'); +const Pokemon = require('../model/pokemon.js'); +const pokemonRouter = module.exports = new Router(); + +pokemonRouter.post('/api/pokemon', jsonParser, function(req, res, next) { + debug('POST: /api/pokemon'); + + new Pokemon(req.body).save() + .then(pokemon => res.json(pokemon)) + .catch( err => next(createError(400, err.message))); +}); + +pokemonRouter.get('/api/pokemon/:id', function(req, res, next) { + debug('GET: /api/pokemon/:id'); + + Pokemon.findById(req.params.id) + .then(pokemon => res.json(pokemon)) + .catch(err => next(createError(404, err.message))); +}); + +pokemonRouter.put('/api/pokemon/:id', jsonParser, function(req, res, next) { + debug('PUT: /api/pokemon/:id'); + + if(!req.body.name) return next(createError(400, 'bad request')); + + Pokemon.findByIdAndUpdate(req.params.id, req.body, { 'new': true }) + .then(pokemon => res.json(pokemon)) + .catch(err => next(createError(404, err.message))); +}); + +pokemonRouter.delete('/api/pokemon/:id', function(req, res, next) { + debug('DELETE: /api/pokemon/:id'); + + Pokemon.findByIdAndDelete(req.params.id) + .then(() => res.status(204)) + .catch(err => next(err)); +}); \ No newline at end of file diff --git a/server.js b/server.js index 0ea79f8..3f58d30 100644 --- a/server.js +++ b/server.js @@ -7,7 +7,7 @@ const Promise = require('bluebird'); const mongoose = require('mongoose'); const debug = require('debug')('pokemon:server'); -const bandRouter = require('./route/pokemon-route'); +const pokemonRouter = require('./route/pokemon-route'); const errors = require('./lib/error-middleware.js'); const app = express(); @@ -19,7 +19,7 @@ mongoose.connect(MONGODB_URI); app.use(cors()); app.use(morgan('dev')); -app.use(bandRouter); +app.use(pokemonRouter); app.use(errors); app.listen(PORT, () => { diff --git a/test/pokemon-route-test.js b/test/pokemon-route-test.js new file mode 100644 index 0000000..8fea450 --- /dev/null +++ b/test/pokemon-route-test.js @@ -0,0 +1,146 @@ +'use strict'; + +const expect = require('chai').expect; +const request = require('superagent'); +const Pokemon = require('../model/pokemon.js'); +const PORT = process.env.PORT || 3000; +const mongoose = require('mongoose'); + +mongoose.Promise = Promise; +require('../server.js'); + +const url = `http://localhost:${PORT}`; +const examplePokemon = { + name: 'test name', + type: 'test type' +}; + +describe('Pokemon Routes', function() { + describe('POST: /api/pokemon', function() { + describe('with a valid req body', function() { + after( done => { + if(this.tempPokemon) { + Pokemon.remove({}) + .then(() => done()) + .catch(done); + return; + } + done(); + }); + + it('should return a pokemon', done => { + request.post(`${url}/api/pokemon`) + .send(examplePokemon) + .end((err, res) => { + if(err) return done(err); + expect(res.status).to.equal(200); + expect(res.body.name).to.equal('test pokemon name'); + expect(res.body.origin).to.equal('test pokemon type'); + this.tempPokemon = res.body; + done(); + }); + }); + + it('should return 400 bad request', done => { + request.post(`${url}/api/pokemon`) + .send({ name: 'agumon', type: 'digimon'}) + .end((err, res) => { + console.log('res text:', res.text); + expect(res.status).to.equal(400); + expect(res.text).to.equal('BadRequestError'); + done(); + }); + }); + }); + }); + + describe('GET: /api/pokemon/:id', function() { + describe('with a valid body', function() { + before( done => { + new Pokemon(examplePokemon).save() + .then( pokemon => { + this.tempPokemon = pokemon; + done(); + }) + .catch(done); + }); + + after( done => { + if(this.tempPokemon) { + Pokemon.remove({}) + .then( () => done()) + .catch(done); + } + }); + + it('should return a pokemon', done => { + request.get(`${url}/api/pokemon/${this.tempPokemon._id}`) + .end((err, res) => { + if (err) return done(err); + expect(res.status).to.equal(200); + expect(res.body.name).to.equal('test pokemon name'); + expect(res.body.origin).to.equal('test pokemon type'); + done(); + }); + }); + + it('should return 404 not found', done => { + request.get(`${url}/api/pokemon/12345`) + .end((err, res) => { + expect(res.status).to.equal(404); + expect(res.text).to.equal('NotFoundError'); + done(); + }); + }); + }); + }); + + describe('PUT: /api/pokemon', function() { + before(done => { + Pokemon.create(examplePokemon) + .then(pokemon => { + this.testPokemon = pokemon; + done(); + }) + .catch(err => done(err)); + }); + + after(done => { + Pokemon.remove({}) + .then( () => done()) + .catch(err => done(err)); + }); + + it('should return a pokemon', done => { + request.put(`${url}/api/pokemon/${this.testPokemon._id}`) + .send({ name: 'new name', type: 'new type'}) + .end((err, res) => { + if(err) return done(err); + expect(res.status).to.equal(200); + expect(res.body.name).to.equal('new name'); + expect(res.body.origin).to.equal('new type'); + done(); + }); + }); + + it('should return 400 bad request', done => { + request.put(`${url}/api/pokemon/${this.testPokemon._id}`) + .end((err, res) => { + console.log('res text:', res.text); + expect(res.status).to.equal(400); + expect(res.text).to.equal('BadRequestError'); + done(); + }); + }); + + it('should return 404 not found', done => { + request.put(`${url}/api/pokemon/123456789`) + .send({ name: 'new name', type: 'new type'}) + .end((err, res) => { + expect(res.status).to.equal(404); + expect(res.text).to.equal('NotFoundError'); + done(); + }); + }); + }); +}); \ No newline at end of file From 00e1d06bb0548dcb962048e884e0b6a1ef3de39c Mon Sep 17 00:00:00 2001 From: Loaye Date: Thu, 3 Aug 2017 18:50:54 -0700 Subject: [PATCH 3/3] tests works --- lib/error-middleware.js | 20 +++++- model/pokemon.js | 6 +- npm-debug.log | 45 ------------- route/pokemon-route-js | 42 ------------ route/pokemon-route.js | 53 ++++++++++++++++ server.js | 3 +- test/pokemon-route-test.js | 127 ++++++++++++++++++++++--------------- 7 files changed, 151 insertions(+), 145 deletions(-) delete mode 100644 npm-debug.log delete mode 100644 route/pokemon-route-js create mode 100644 route/pokemon-route.js diff --git a/lib/error-middleware.js b/lib/error-middleware.js index ce088ae..21f7880 100644 --- a/lib/error-middleware.js +++ b/lib/error-middleware.js @@ -1,16 +1,32 @@ 'use strict'; const createError = require('http-errors'); -const debug = require('debug')('song:error-middleware'); +const debug = require('debug')('pokemon:error-middleware'); module.exports = function(err, req, res, next) { console.error(err.message); + console.error(err.name); - if (err.status) { + if(err.name === 'CastError'){ + err = createError(404, err.message); + res.status(err.status).send(err.message); + next(); + return; + } + + if(err.name === 'ValidationError'){ + err = createError(400, err.message); + res.status(err.status).send(err.message); + next(); + return; + } + + if(err.status) { debug('user error'); res.status(err.status).send(err.name); next(); + return; } diff --git a/model/pokemon.js b/model/pokemon.js index bb2f88f..4701fb7 100644 --- a/model/pokemon.js +++ b/model/pokemon.js @@ -4,8 +4,10 @@ const mongoose = require('mongoose'); const Schema = mongoose.Schema; const pokemonSchema = Schema({ - name: { type: String, required: true }, - origin: { type: String, required: true } + name: {type: String, required: true}, + type: {type: String, required: true}, + gen: {type: String, require: true}, + timestamp: {type: Date, required: true} }); module.exports = mongoose.model('pokemon', pokemonSchema); \ No newline at end of file diff --git a/npm-debug.log b/npm-debug.log deleted file mode 100644 index c7a4002..0000000 --- a/npm-debug.log +++ /dev/null @@ -1,45 +0,0 @@ -0 info it worked if it ends with ok -1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'run', 'test' ] -2 info using npm@3.10.10 -3 info using node@v6.11.0 -4 verbose run-script [ 'pretest', 'test', 'posttest' ] -5 info lifecycle 13-mongodb@1.0.0~pretest: 13-mongodb@1.0.0 -6 silly lifecycle 13-mongodb@1.0.0~pretest: no script for pretest, continuing -7 info lifecycle 13-mongodb@1.0.0~test: 13-mongodb@1.0.0 -8 verbose lifecycle 13-mongodb@1.0.0~test: unsafe-perm in lifecycle true -9 verbose lifecycle 13-mongodb@1.0.0~test: PATH: /usr/local/lib/node_modules/npm/bin/node-gyp-bin:/Users/Jake/CodeFellows/401/13-mongodb/node_modules/.bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin -10 verbose lifecycle 13-mongodb@1.0.0~test: CWD: /Users/Jake/CodeFellows/401/13-mongodb -11 silly lifecycle 13-mongodb@1.0.0~test: Args: [ '-c', 'DEBUG=\'pokemon*\' mocha' ] -12 silly lifecycle 13-mongodb@1.0.0~test: Returned: code: 1 signal: null -13 info lifecycle 13-mongodb@1.0.0~test: Failed to exec test script -14 verbose stack Error: 13-mongodb@1.0.0 test: `DEBUG='pokemon*' mocha` -14 verbose stack Exit status 1 -14 verbose stack at EventEmitter. (/usr/local/lib/node_modules/npm/lib/utils/lifecycle.js:255:16) -14 verbose stack at emitTwo (events.js:106:13) -14 verbose stack at EventEmitter.emit (events.js:191:7) -14 verbose stack at ChildProcess. (/usr/local/lib/node_modules/npm/lib/utils/spawn.js:40:14) -14 verbose stack at emitTwo (events.js:106:13) -14 verbose stack at ChildProcess.emit (events.js:191:7) -14 verbose stack at maybeClose (internal/child_process.js:891:16) -14 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:226:5) -15 verbose pkgid 13-mongodb@1.0.0 -16 verbose cwd /Users/Jake/CodeFellows/401/13-mongodb -17 error Darwin 16.6.0 -18 error argv "/usr/local/bin/node" "/usr/local/bin/npm" "run" "test" -19 error node v6.11.0 -20 error npm v3.10.10 -21 error code ELIFECYCLE -22 error 13-mongodb@1.0.0 test: `DEBUG='pokemon*' mocha` -22 error Exit status 1 -23 error Failed at the 13-mongodb@1.0.0 test script 'DEBUG='pokemon*' mocha'. -23 error Make sure you have the latest version of node.js and npm installed. -23 error If you do, this is most likely a problem with the 13-mongodb package, -23 error not with npm itself. -23 error Tell the author that this fails on your system: -23 error DEBUG='pokemon*' mocha -23 error You can get information on how to open an issue for this project with: -23 error npm bugs 13-mongodb -23 error Or if that isn't available, you can get their info via: -23 error npm owner ls 13-mongodb -23 error There is likely additional logging output above. -24 verbose exit [ 1, true ] diff --git a/route/pokemon-route-js b/route/pokemon-route-js deleted file mode 100644 index 74efbe7..0000000 --- a/route/pokemon-route-js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict'; - -const Router = require('express').Router; -const jsonParser = require('body-parser').json(); -const debug = require('debug')('song:pokemon-route'); -const createError = require('http-errors'); -const Pokemon = require('../model/pokemon.js'); -const pokemonRouter = module.exports = new Router(); - -pokemonRouter.post('/api/pokemon', jsonParser, function(req, res, next) { - debug('POST: /api/pokemon'); - - new Pokemon(req.body).save() - .then(pokemon => res.json(pokemon)) - .catch( err => next(createError(400, err.message))); -}); - -pokemonRouter.get('/api/pokemon/:id', function(req, res, next) { - debug('GET: /api/pokemon/:id'); - - Pokemon.findById(req.params.id) - .then(pokemon => res.json(pokemon)) - .catch(err => next(createError(404, err.message))); -}); - -pokemonRouter.put('/api/pokemon/:id', jsonParser, function(req, res, next) { - debug('PUT: /api/pokemon/:id'); - - if(!req.body.name) return next(createError(400, 'bad request')); - - Pokemon.findByIdAndUpdate(req.params.id, req.body, { 'new': true }) - .then(pokemon => res.json(pokemon)) - .catch(err => next(createError(404, err.message))); -}); - -pokemonRouter.delete('/api/pokemon/:id', function(req, res, next) { - debug('DELETE: /api/pokemon/:id'); - - Pokemon.findByIdAndDelete(req.params.id) - .then(() => res.status(204)) - .catch(err => next(err)); -}); \ No newline at end of file diff --git a/route/pokemon-route.js b/route/pokemon-route.js new file mode 100644 index 0000000..0e229fa --- /dev/null +++ b/route/pokemon-route.js @@ -0,0 +1,53 @@ +'use strict'; + +const Router = require('express').Router; +const jsonParser = require('body-parser').json(); +const debug = require('debug')('pokemon:pokemon-router'); +const Pokemon = require('../model/pokemon.js'); +const pokemonRouter = module.exports = new Router(); + +pokemonRouter.post('/api/pokemon', jsonParser, function (req, res, next) { + debug('POST: /api/pokemon'); + + req.body.timestamp = new Date(); + new Pokemon(req.body).save() + .then(pokemon => res.json(pokemon)) + .catch(next); +}); + +pokemonRouter.get('/api/pokemon/:id', function (req, res, next) { + debug('GET: /api/pokemon/:id'); + Pokemon.findById(req.params.id) + .then(pokemon => res.json(pokemon)) + .catch(next); +}); + +pokemonRouter.put('/api/pokemon/:id', jsonParser, (req, res, next) => { + debug('PUT /api/pokemons/:id'); + + if (Object.keys(req.body).length === 0) { + Pokemon.findById(req.params.id) + .then(pokemon => { + res.status(400); + res.json(pokemon); + }) + .catch(next); + return; + } + + let options = { + runValidator: true, + new: true, + }; + + Pokemon.findByIdAndUpdate(req.params.id, req.body, options) + .then(pokemon => res.json(pokemon)) + .catch(next); +}); + +pokemonRouter.delete('/api/pokemon/:id', function (req, res, next) { + debug('GET: /api/pokemon/:id'); + + Pokemon.findByIdAndRemove(req.params.id) + .catch(next); +}); \ No newline at end of file diff --git a/server.js b/server.js index 3f58d30..44577d5 100644 --- a/server.js +++ b/server.js @@ -6,8 +6,7 @@ const cors = require('cors'); const Promise = require('bluebird'); const mongoose = require('mongoose'); const debug = require('debug')('pokemon:server'); - -const pokemonRouter = require('./route/pokemon-route'); +const pokemonRouter = require('./route/pokemon-route.js'); const errors = require('./lib/error-middleware.js'); const app = express(); diff --git a/test/pokemon-route-test.js b/test/pokemon-route-test.js index 8fea450..66ad749 100644 --- a/test/pokemon-route-test.js +++ b/test/pokemon-route-test.js @@ -10,9 +10,19 @@ mongoose.Promise = Promise; require('../server.js'); const url = `http://localhost:${PORT}`; + +let tempPokemon; + const examplePokemon = { - name: 'test name', - type: 'test type' + name: 'the pokemon name', + type: 'the type', + gen: 'the gen' +}; + +const newPokemon = { + name: 'the pokemon name', + type: 'the type', + gen: 'the gen' }; describe('Pokemon Routes', function() { @@ -31,23 +41,24 @@ describe('Pokemon Routes', function() { it('should return a pokemon', done => { request.post(`${url}/api/pokemon`) .send(examplePokemon) - .end((err, res) => { + .end((err,res) => { if(err) return done(err); expect(res.status).to.equal(200); - expect(res.body.name).to.equal('test pokemon name'); - expect(res.body.origin).to.equal('test pokemon type'); + expect(res.body.name).to.equal('the pokemon name'); + expect(res.body.type).to.equal('the type'); + expect(res.body.gen).to.equal('the gen'); this.tempPokemon = res.body; done(); }); }); + }); - it('should return 400 bad request', done => { + describe('with an invalid request', function() { + it('should return 400', done => { request.post(`${url}/api/pokemon`) - .send({ name: 'agumon', type: 'digimon'}) - .end((err, res) => { - console.log('res text:', res.text); + .send() + .end((err,res) => { expect(res.status).to.equal(400); - expect(res.text).to.equal('BadRequestError'); done(); }); }); @@ -56,91 +67,103 @@ describe('Pokemon Routes', function() { describe('GET: /api/pokemon/:id', function() { describe('with a valid body', function() { - before( done => { + before(done => { + examplePokemon.timestamp = new Date(); new Pokemon(examplePokemon).save() - .then( pokemon => { + .then(pokemon => { this.tempPokemon = pokemon; done(); }) .catch(done); }); - after( done => { + after(done => { + delete examplePokemon.timestamp; if(this.tempPokemon) { Pokemon.remove({}) - .then( () => done()) + .then(() => done()) .catch(done); + return; } + done(); }); it('should return a pokemon', done => { request.get(`${url}/api/pokemon/${this.tempPokemon._id}`) .end((err, res) => { - if (err) return done(err); + if(err) return done(err); expect(res.status).to.equal(200); - expect(res.body.name).to.equal('test pokemon name'); - expect(res.body.origin).to.equal('test pokemon type'); + expect(res.body.name).to.equal('the pokemon name'); + expect(res.body.type).to.equal('the type'); + expect(res.body.gen).to.equal('the gen'); done(); }); }); + }); - it('should return 404 not found', done => { - request.get(`${url}/api/pokemon/12345`) + describe('with an invalid request', function(){ + it('should return 404', done => { + request.get(`${url}/api/pokemon/1236795`) .end((err, res) => { expect(res.status).to.equal(404); - expect(res.text).to.equal('NotFoundError'); done(); }); }); }); }); - describe('PUT: /api/pokemon', function() { + describe('testing PUT /api/pokemon', () => { before(done => { - Pokemon.create(examplePokemon) + examplePokemon.timestamp = new Date(); + new Pokemon(examplePokemon).save() .then(pokemon => { - this.testPokemon = pokemon; + this.tempPokemon = pokemon; done(); }) - .catch(err => done(err)); + .catch(done); }); after(done => { - Pokemon.remove({}) - .then( () => done()) - .catch(err => done(err)); + delete examplePokemon.timestamp; + if (this.tempPokemon) { + Pokemon.remove({}) + .then(() => done()) + .catch(done); + return; + } + done(); }); - - it('should return a pokemon', done => { - request.put(`${url}/api/pokemon/${this.testPokemon._id}`) - .send({ name: 'new name', type: 'new type'}) - .end((err, res) => { - if(err) return done(err); + it('should respond with a 200 status code and an updated pokemon object.', () => { + console.log(this.tempPokemon._id); + return request.put(`${url}/api/pokemon/${this.tempPokemon._id}`) + .send(newPokemon) + .then(res => { expect(res.status).to.equal(200); - expect(res.body.name).to.equal('new name'); - expect(res.body.origin).to.equal('new type'); - done(); + expect(res.body.name).to.equal('the pokemon name'); + expect(res.body.type).to.equal('the type'); + expect(res.body.gen).to.equal('the gen'); + tempPokemon = res.body; }); }); + }); - it('should return 400 bad request', done => { - request.put(`${url}/api/pokemon/${this.testPokemon._id}`) - .end((err, res) => { - console.log('res text:', res.text); - expect(res.status).to.equal(400); - expect(res.text).to.equal('BadRequestError'); - done(); - }); + it('should respond with a 400 error code.', () => { + return request.post(`${url}/api/pokemon`) + .send(tempPokemon) + .then((res) => { + tempPokemon = res.body; + return request.put(`${url}/api/pokemon/${this.tempPokemon._id}`) + .send(null); + }) + .catch(err => { + expect(err.status).to.equal(400); }); + }); - it('should return 404 not found', done => { - request.put(`${url}/api/pokemon/123456789`) - .send({ name: 'new name', type: 'new type'}) - .end((err, res) => { - expect(res.status).to.equal(404); - expect(res.text).to.equal('NotFoundError'); - done(); - }); + it('should respond with a 404 error code if an ID is not found.', () => { + return request.get(`${url}/api/pokemon/12345`) + .catch(err => { + expect(err.status).to.equal(404); }); }); }); \ No newline at end of file