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}