diff --git a/hw6/Claudio_Maggioni/app.js b/hw6/Claudio_Maggioni/app.js index b9367aa..0ccd0e7 100644 --- a/hw6/Claudio_Maggioni/app.js +++ b/hw6/Claudio_Maggioni/app.js @@ -39,7 +39,8 @@ app.use(express.static('public')); // Initialize routers here const routers = require('./routes/routers'); app.use('/', routers.root); -app.use('/favorites', routers.favourites_db); +// app.use('/favorites', routers.favourites_db); +app.use('/favorites', routers.favourites_db_promises); app.use('/bookmarked', routers.bookmarked); module.exports = app; diff --git a/hw6/Claudio_Maggioni/routes/favourites_db/router.js b/hw6/Claudio_Maggioni/routes/favourites_db/router.js index efc3cd9..cb1d70b 100644 --- a/hw6/Claudio_Maggioni/routes/favourites_db/router.js +++ b/hw6/Claudio_Maggioni/routes/favourites_db/router.js @@ -7,7 +7,17 @@ const express = require('express'); const router = express.Router(); const mongoose = require('mongoose'); const Favorite = mongoose.model('Favorite'); -const { error, renderFav } = require('../utils'); +const { error, renderFav, parseId, notFound } = require('../utils'); + +function findAndRender(filter, req, res) { + Favorite.find(filter, (err, favs) => { + if (err) { + return error(err, res); + } + + renderFav(req, res, favs); + }); +} router.post('/', (req, res) => { if (!req.body.name || !req.body.dataURL) { @@ -25,6 +35,8 @@ router.post('/', (req, res) => { if (req.body._id) { favourite._id = req.body._id; + } else { + favourite._id = mongoose.Types.ObjectId(); } favourite.save((err, fav) => { @@ -39,13 +51,7 @@ router.post('/', (req, res) => { }); router.get('/', (req, res) => { - Favorite.find({}, (err, favs) => { - if (err) { - return error(err, res); - } - - renderFav(req, res, favs); - }); + findAndRender({}, req, res); }); router.get('/search', (req, res) => { @@ -53,25 +59,20 @@ router.get('/search', (req, res) => { delete filter['dataURL']; delete filter['_method']; - Favorite.find(filter, (err, favs) => { - if (err) { - return error(err, res); - } - - renderFav(req, res, favs); - }); + findAndRender(filter, req, res); }); router.get('/:id', (req, res) => { - Favorite.findById(req.params.id, (err, fav) => { + Favorite.findById(parseId(req), (err, fav) => { if (err) { return error(err, res); - } else if (!fav) { - res.writeHead(404, {'Content-Type': 'text/plain'}); - res.end('Not found'); - } else { - renderFav(req, res, fav, false); } + + if (notFound(fav, res)) { + return; + } + + renderFav(req, res, fav, false); }); }); @@ -90,10 +91,10 @@ function handleUpdate(partial = false) { } if (req.body.bookmarked !== undefined) { - edit.bookmarked = !!req.body.bookmarked; + edit.bookmarked = req.body.bookmarked; } - Favorite.findByIdAndUpdate(req.params.id, { $set: edit }, { + Favorite.findByIdAndUpdate(parseId(req), { $set: edit }, { new: true, upsert: true, setDefaultsOnInsert: true, @@ -112,18 +113,16 @@ function handleUpdate(partial = false) { } router.put('/:id', handleUpdate()); + router.patch('/:id', handleUpdate(true)); - router.delete('/:id', (req, res) => { - Favorite.findByIdAndDelete(req.params.id, (err, fav) => { + Favorite.findByIdAndDelete(parseId(req), (err, fav) => { if (err) { return error(err, res); } - if (fav == null) { - res.writeHead(404, { 'Content-Type': 'text/plain' }); - res.end('Favourite not found'); + if (notFound(fav, res)) { return; } @@ -136,20 +135,18 @@ router.delete('/:id', (req, res) => { }); router.put('/:id/bookmarked', (req, res) => { - Favorite.findByIdAndUpdate(req.params.id, { - $set: { bookmarked: !!req.body.bookmarked } + Favorite.findByIdAndUpdate(parseId(req), { + $set: { bookmarked: req.body.bookmarked } }, { new: true }, (err, fav) => { - if (false) { - res.writeHead(404, { 'Content-Type': 'text/plain' }); - res.end('Favourite to bookmark not found'); - } else if (!req.body.bookmarked) { + if (notFound(fav, res)) { + return; + } + + if (!req.body.bookmarked) { res.writeHead(400, { 'Content-Type': 'text/plain' }); res.end('Bad PUT bookmark form parameters'); } else { - res.format({ - html: () => renderFav(req, res, fav, false), - json: () => res.json(fav) - }); + renderFav(req, res, fav, false); } }); }); diff --git a/hw6/Claudio_Maggioni/routes/favourites_db_promises/router.js b/hw6/Claudio_Maggioni/routes/favourites_db_promises/router.js new file mode 100644 index 0000000..3139d2f --- /dev/null +++ b/hw6/Claudio_Maggioni/routes/favourites_db_promises/router.js @@ -0,0 +1,136 @@ +/** @module root/router */ +'use strict'; +// vim: set ts=2 sw=2 et tw=80: + +const fs = require('fs'); +const express = require('express'); +const router = express.Router(); + +const mongoose = require('mongoose'); +const Favorite = mongoose.model('Favorite'); + +const { error, catchErrs, renderFav, parseId, notFound } = require('../utils'); + +function findAndRender(filter, req, res) { + catchErrs(Favorite.find(filter), res).then(favs => { + renderFav(req, res, favs); + }); +} + +router.post('/', (req, res) => { + if (!req.body.name || !req.body.dataURL) { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + res.end('Bad create form parameters'); + return; + } + + const data = { + name: req.body.name, + dataURL: req.body.dataURL, + bookmarked: req.body.bookmarked, + }; + const favourite = new Favorite(data); + + if (req.body._id) { + favourite._id = req.body._id; + } else { + favourite._id = mongoose.Types.ObjectId(); + } + + catchErrs(favourite.save(), res).then(fav => { + res.status = 201; + const _id = fav._id; + renderFav(req, res, Object.assign({ _id }, data), false); + }); +}); + +router.get('/', (req, res) => { + findAndRender({}, req, res); +}); + +router.get('/search', (req, res) => { + const filter = Object.assign({}, req.query); + delete filter['dataURL']; + delete filter['_method']; + + findAndRender(filter, req, res); +}); + +router.get('/:id', (req, res) => { + catchErrs(Favorite.findById(parseId(req)), res).then(fav => { + if (notFound(fav, res)) { + return; + } + + renderFav(req, res, fav, false); + }); +}); + +function handleUpdate(partial = false) { + return (req, res) => { + const edit = {}; + for (const key of ['dataURL', 'name']) { + if (req.body[key]) { + edit[key] = req.body[key]; + } else if (!partial) { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + res.end('Bad PUT form parameters'); + return; + } + } + + if (req.body.bookmarked !== undefined) { + edit.bookmarked = req.body.bookmarked; + } + + catchErrs(Favorite.findByIdAndUpdate(parseId(req), { $set: edit }, { + new: true, + upsert: true, + setDefaultsOnInsert: true, + }), res).then(fav => { + console.log(arguments); + + // FIXME: return 201 on creation + res.status = 200; + renderFav(req, res, fav, false); + }); + }; +} + +router.put('/:id', handleUpdate()); + +router.patch('/:id', handleUpdate(true)); + +router.delete('/:id', (req, res) => { + catchErrs(Favorite.findByIdAndDelete(parseId(req)), res).then(fav => { + if (notFound(fav, res)) { + return; + } + + res.format({ + json: () => res.writeHead(204), + html: () => res.writeHead(302, { 'Location': '/favorites' }) + }); + res.end(); + }); +}); + +router.put('/:id/bookmarked', (req, res) => { + catchErrs(Favorite.findByIdAndUpdate(parseId(req), { + $set: { bookmarked: req.body.bookmarked } + }, { new: true }), res).then(fav => { + if (notFound(fav, res)) { + return; + } + + if (!req.body.bookmarked) { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + res.end('Bad PUT bookmark form parameters'); + } else { + renderFav(req, res, fav, false); + } + }); +}); + +/** router for /root */ +module.exports = router; diff --git a/hw6/Claudio_Maggioni/routes/utils.js b/hw6/Claudio_Maggioni/routes/utils.js index f62be29..215c1d7 100644 --- a/hw6/Claudio_Maggioni/routes/utils.js +++ b/hw6/Claudio_Maggioni/routes/utils.js @@ -1,8 +1,16 @@ +// vim: set ts=2 sw=2 et tw=80: + +const mongoose = require('mongoose'); + +function catchErrs(promise, res) { + return promise.catch(err => error(err, res)); +} + function error(err, res) { console.error(err); res.status = 500; res.format({ - json: () => res.json({error: err}), + json: () => res.json({ error: err }), html: () => res.render('500.dust', { err: JSON.stringify(err, null, 2) }), }); res.end(); @@ -18,15 +26,16 @@ function renderFav(req, res, favs, list = true) { } }; - if (list) { - favs = favs.map(makeTestsPass); - } else { - favs = makeTestsPass(favs); - } - if (req.accepts('html')) { - res.render(favs ? 'favourites.dust' : 'favourite.dust', list ? { favs } : favs); + res.render(list ? 'favourites.dust' : 'favourite.dust', + list ? { favs } : favs); } else if (req.accepts('json')) { + if (list) { + favs = favs.map(makeTestsPass); + } else { + favs = makeTestsPass(favs); + } + res.json(favs); } else { res.writeHead(406); @@ -34,6 +43,22 @@ function renderFav(req, res, favs, list = true) { } } -module.exports = { error, renderFav }; +function parseId(req) { + if (typeof req.params.id === 'string' && req.params.id.length == 24) { + return mongoose.Types.ObjectId(req.params.id); + } else { + return req.params.id; + } +} + +function notFound(e, res) { + if (e == null) { + res.writeHead(404, {'Content-Type': 'text/plain'}); + res.end('Not found'); + return true; + } else return false; +} + +module.exports = { error, renderFav, catchErrs, parseId, notFound }; diff --git a/hw6/Claudio_Maggioni/views/favourite_partial.dust b/hw6/Claudio_Maggioni/views/favourite_partial.dust index a431d83..703a421 100644 --- a/hw6/Claudio_Maggioni/views/favourite_partial.dust +++ b/hw6/Claudio_Maggioni/views/favourite_partial.dust @@ -16,13 +16,13 @@


- {?b} + {?bookmarked} {:else} - {/b} + {/bookmarked} Favourites list {/details} diff --git a/hw6/Claudio_Maggioni/views/favourites.dust b/hw6/Claudio_Maggioni/views/favourites.dust index 271d285..b8dc167 100644 --- a/hw6/Claudio_Maggioni/views/favourites.dust +++ b/hw6/Claudio_Maggioni/views/favourites.dust @@ -17,7 +17,7 @@ {/bookmarked} {#favs}
- {>"favourite_partial" name=name dataURL=dataURL _id=_id b=b details="true" /} + {>"favourite_partial" name=name dataURL=dataURL _id=_id bookmarked=bookmarked details="true" /}
{:else} No favourites. diff --git a/hw6/Claudio_Maggioni/views/index.dust b/hw6/Claudio_Maggioni/views/index.dust index 02c3344..e7febfa 100644 --- a/hw6/Claudio_Maggioni/views/index.dust +++ b/hw6/Claudio_Maggioni/views/index.dust @@ -28,7 +28,7 @@

Favourites

{#favs} - {>"favourite_partial" name=name dataURL=dataURL _id=_id b=b details="true" /} + {>"favourite_partial" name=name dataURL=dataURL _id=_id bookmarked=bookmarked details="true" /} {/favs}