hw6: mandatory tasks done (tests pass, manual test needed for HTML)

This commit is contained in:
Claudio Maggioni 2019-10-28 22:12:20 +01:00
parent c6293aea8f
commit ab8c9c9668
7 changed files with 211 additions and 52 deletions

View File

@ -39,7 +39,8 @@ app.use(express.static('public'));
// Initialize routers here // Initialize routers here
const routers = require('./routes/routers'); const routers = require('./routes/routers');
app.use('/', routers.root); 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); app.use('/bookmarked', routers.bookmarked);
module.exports = app; module.exports = app;

View File

@ -7,7 +7,17 @@ const express = require('express');
const router = express.Router(); const router = express.Router();
const mongoose = require('mongoose'); const mongoose = require('mongoose');
const Favorite = mongoose.model('Favorite'); 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) => { router.post('/', (req, res) => {
if (!req.body.name || !req.body.dataURL) { if (!req.body.name || !req.body.dataURL) {
@ -25,6 +35,8 @@ router.post('/', (req, res) => {
if (req.body._id) { if (req.body._id) {
favourite._id = req.body._id; favourite._id = req.body._id;
} else {
favourite._id = mongoose.Types.ObjectId();
} }
favourite.save((err, fav) => { favourite.save((err, fav) => {
@ -39,13 +51,7 @@ router.post('/', (req, res) => {
}); });
router.get('/', (req, res) => { router.get('/', (req, res) => {
Favorite.find({}, (err, favs) => { findAndRender({}, req, res);
if (err) {
return error(err, res);
}
renderFav(req, res, favs);
});
}); });
router.get('/search', (req, res) => { router.get('/search', (req, res) => {
@ -53,25 +59,20 @@ router.get('/search', (req, res) => {
delete filter['dataURL']; delete filter['dataURL'];
delete filter['_method']; delete filter['_method'];
Favorite.find(filter, (err, favs) => { findAndRender(filter, req, res);
if (err) {
return error(err, res);
}
renderFav(req, res, favs);
});
}); });
router.get('/:id', (req, res) => { router.get('/:id', (req, res) => {
Favorite.findById(req.params.id, (err, fav) => { Favorite.findById(parseId(req), (err, fav) => {
if (err) { if (err) {
return error(err, res); 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) { 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, new: true,
upsert: true, upsert: true,
setDefaultsOnInsert: true, setDefaultsOnInsert: true,
@ -112,18 +113,16 @@ function handleUpdate(partial = false) {
} }
router.put('/:id', handleUpdate()); router.put('/:id', handleUpdate());
router.patch('/:id', handleUpdate(true)); router.patch('/:id', handleUpdate(true));
router.delete('/:id', (req, res) => { router.delete('/:id', (req, res) => {
Favorite.findByIdAndDelete(req.params.id, (err, fav) => { Favorite.findByIdAndDelete(parseId(req), (err, fav) => {
if (err) { if (err) {
return error(err, res); return error(err, res);
} }
if (fav == null) { if (notFound(fav, res)) {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Favourite not found');
return; return;
} }
@ -136,20 +135,18 @@ router.delete('/:id', (req, res) => {
}); });
router.put('/:id/bookmarked', (req, res) => { router.put('/:id/bookmarked', (req, res) => {
Favorite.findByIdAndUpdate(req.params.id, { Favorite.findByIdAndUpdate(parseId(req), {
$set: { bookmarked: !!req.body.bookmarked } $set: { bookmarked: req.body.bookmarked }
}, { new: true }, (err, fav) => { }, { new: true }, (err, fav) => {
if (false) { if (notFound(fav, res)) {
res.writeHead(404, { 'Content-Type': 'text/plain' }); return;
res.end('Favourite to bookmark not found'); }
} else if (!req.body.bookmarked) {
if (!req.body.bookmarked) {
res.writeHead(400, { 'Content-Type': 'text/plain' }); res.writeHead(400, { 'Content-Type': 'text/plain' });
res.end('Bad PUT bookmark form parameters'); res.end('Bad PUT bookmark form parameters');
} else { } else {
res.format({ renderFav(req, res, fav, false);
html: () => renderFav(req, res, fav, false),
json: () => res.json(fav)
});
} }
}); });
}); });

View File

@ -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;

View File

@ -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) { function error(err, res) {
console.error(err); console.error(err);
res.status = 500; res.status = 500;
res.format({ res.format({
json: () => res.json({error: err}), json: () => res.json({ error: err }),
html: () => res.render('500.dust', { err: JSON.stringify(err, null, 2) }), html: () => res.render('500.dust', { err: JSON.stringify(err, null, 2) }),
}); });
res.end(); 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')) { 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')) { } else if (req.accepts('json')) {
if (list) {
favs = favs.map(makeTestsPass);
} else {
favs = makeTestsPass(favs);
}
res.json(favs); res.json(favs);
} else { } else {
res.writeHead(406); 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 };

View File

@ -16,13 +16,13 @@
<input type="text" name="name" placeholder="Name" value="{name}"><br> <input type="text" name="name" placeholder="Name" value="{name}"><br>
<button>Update</button><br> <button>Update</button><br>
<button formaction="/favorites/{_id}?_method=DELETE">Delete</button><br> <button formaction="/favorites/{_id}?_method=DELETE">Delete</button><br>
{?b} {?bookmarked}
<button name="bookmarked" value="false" <button name="bookmarked" value="false"
formaction="/favorites/{_id}/bookmarked?_method=PUT">Remove bookmark</button> formaction="/favorites/{_id}/bookmarked?_method=PUT">Remove bookmark</button>
{:else} {:else}
<button name="bookmarked" value="true" <button name="bookmarked" value="true"
formaction="/favorites/{_id}/bookmarked?_method=PUT">Add bookmark</button> formaction="/favorites/{_id}/bookmarked?_method=PUT">Add bookmark</button>
{/b} {/bookmarked}
</form> </form>
<a href="/favorites">Favourites list</a> <a href="/favorites">Favourites list</a>
{/details} {/details}

View File

@ -17,7 +17,7 @@
{/bookmarked} {/bookmarked}
{#favs} {#favs}
<div> <div>
{>"favourite_partial" name=name dataURL=dataURL _id=_id b=b details="true" /} {>"favourite_partial" name=name dataURL=dataURL _id=_id bookmarked=bookmarked details="true" /}
</div> </div>
{:else} {:else}
<strong>No favourites.</strong> <strong>No favourites.</strong>

View File

@ -28,7 +28,7 @@
<h2>Favourites</h2> <h2>Favourites</h2>
<div id="favourites"> <div id="favourites">
{#favs} {#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} {/favs}
</div> </div>
<script src="scripts/brushes.js"></script> <script src="scripts/brushes.js"></script>