From 95f334f095acc4167c6ece044307a29b93ca5ccc Mon Sep 17 00:00:00 2001 From: Typicode Date: Wed, 22 Jul 2015 07:20:56 +0200 Subject: [PATCH] Add singular resources --- src/server/router/index.js | 84 ++++++++++++++++++++ src/server/router/nested.js | 18 +++++ src/server/{router.js => router/plural.js} | 90 ++++------------------ src/server/router/singular.js | 40 ++++++++++ test/server/{index.js => plural.js} | 0 test/server/singular.js | 67 ++++++++++++++++ 6 files changed, 222 insertions(+), 77 deletions(-) create mode 100644 src/server/router/index.js create mode 100644 src/server/router/nested.js rename src/server/{router.js => router/plural.js} (66%) create mode 100644 src/server/router/singular.js rename test/server/{index.js => plural.js} (100%) create mode 100644 test/server/singular.js diff --git a/src/server/router/index.js b/src/server/router/index.js new file mode 100644 index 0000000..ccdd274 --- /dev/null +++ b/src/server/router/index.js @@ -0,0 +1,84 @@ +var express = require('express') +var methodOverride = require('method-override') +var bodyParser = require('body-parser') +var _ = require('lodash') +var _db = require('underscore-db') +var low = require('lowdb') +var plural = require('./plural') +var nested = require('./nested') +var singular = require('./singular') +var mixins = require('../mixins') + +module.exports = function (source) { + + // Create router + var router = express.Router() + + // Add middlewares + router.use(bodyParser.json({limit: '10mb'})) + router.use(bodyParser.urlencoded({extended: false})) + router.use(methodOverride()) + + // Create database + var db + if (_.isObject(source)) { + db = low() + db.object = source + } else { + db = low(source) + } + + // Add underscore-db methods to db + db._.mixin(_db) + + // Add specific mixins + db._.mixin(mixins) + + // Expose database + router.db = db + + // Expose database + router.db = db + + // Expose render + router.render = function (req, res) { + res.jsonp(res.locals.data) + } + + // GET /db + function showDatabase (req, res, next) { + res.locals.data = db.object + next() + } + + router.get('/db', showDatabase) + + router.use(nested()) + + // Create routes + for (var prop in db.object) { + if (_.isPlainObject(db.object[prop])) { + router.use('/' + prop, singular(db, prop)) + continue + } + + if (_.isArray(db.object[prop])) { + router.use('/' + prop, plural(db, prop)) + continue + } + + throw new Error('Unsupported type') + + } + + router.use(function (req, res) { + if (!res.locals.data) { + res.status(404) + res.locals.data = {} + } + + router.render(req, res) + }) + + return router +} diff --git a/src/server/router/nested.js b/src/server/router/nested.js new file mode 100644 index 0000000..b776058 --- /dev/null +++ b/src/server/router/nested.js @@ -0,0 +1,18 @@ +var express = require('express') +var pluralize = require('pluralize') +var utils = require('../utils') + +module.exports = function () { + + var router = express.Router() + + // Rewrite url to /:nested?:resourceId=:id + router.get('/:resource/:id/:nested', function (req, res, next) { + var prop = pluralize.singular(req.params.resource) + req.query[prop + 'Id'] = utils.toNative(req.params.id) + req.url = '/' + req.params.nested + next() + }) + + return router +} diff --git a/src/server/router.js b/src/server/router/plural.js similarity index 66% rename from src/server/router.js rename to src/server/router/plural.js index b501666..d268bae 100644 --- a/src/server/router.js +++ b/src/server/router/plural.js @@ -1,69 +1,25 @@ var express = require('express') -var methodOverride = require('method-override') -var bodyParser = require('body-parser') var _ = require('lodash') -var _db = require('underscore-db') -var low = require('lowdb') var pluralize = require('pluralize') -var utils = require('./utils') -var mixins = require('./mixins') +var utils = require('../utils') + +module.exports = function (db, name) { -module.exports = function (source) { // Create router var router = express.Router() - // Add middlewares - router.use(bodyParser.json({limit: '10mb'})) - router.use(bodyParser.urlencoded({extended: false})) - router.use(methodOverride()) - - // Create database - var db - if (_.isObject(source)) { - db = low() - db.object = source - } else { - db = low(source) - } - - // Add underscore-db methods to db - db._.mixin(_db) - - // Add specific mixins - db._.mixin(mixins) - - // Expose database - router.db = db - - // Expose render - router.render = function (req, res) { - res.jsonp(res.locals.data) - } - - // GET /db - function showDatabase (req, res, next) { - res.locals.data = db.object - next() - } - // GET /:resource // GET /:resource?q= // GET /:resource?attr=&attr= - // GET /:parent/:parentId/:resource?attr=&attr= - // GET /*?*&_end= - // GET /*?*&_start=&_end= + // GET /:resource?_end=&* + // GET /:resource?_start=&_end=&* function list (req, res, next) { - // Test if resource exists - if (!db.object.hasOwnProperty(req.params.resource)) { - res.status(404) - return next() - } // Filters list var filters = {} // Resource chain - var chain = db(req.params.resource).chain() + var chain = db(name).chain() // Remove q, _start, _end, ... from req.query to avoid filtering using those // parameters @@ -96,12 +52,6 @@ module.exports = function (source) { } - // Add :parentId filter in case URL is like /:parent/:parentId/:resource - if (req.params.parent) { - var parent = pluralize.singular(req.params.parent) - filters[parent + 'Id'] = +req.params.parentId - } - // Add query parameters filters // Convert query parameters to their native counterparts for (var key in req.query) { @@ -157,7 +107,7 @@ module.exports = function (source) { function show (req, res, next) { var _embed = req.query._embed var id = utils.toNative(req.params.id) - var resource = db(req.params.resource) + var resource = db(name) .getById(id) if (resource) { @@ -173,7 +123,7 @@ module.exports = function (source) { && otherResource.trim().length > 0 && db.object[otherResource]) { var query = {} - var prop = pluralize.singular(req.params.resource) + 'Id' + var prop = pluralize.singular(name) + 'Id' query[prop] = id resource[otherResource] = db(otherResource).where(query) @@ -181,9 +131,6 @@ module.exports = function (source) { }) res.locals.data = resource - } else { - res.status(404) - res.locals.data = {} } next() @@ -195,7 +142,7 @@ module.exports = function (source) { req.body[key] = utils.toNative(req.body[key]) } - var resource = db(req.params.resource) + var resource = db(name) .insert(req.body) res.status(201) @@ -210,14 +157,11 @@ module.exports = function (source) { req.body[key] = utils.toNative(req.body[key]) } - var resource = db(req.params.resource) + var resource = db(name) .updateById(utils.toNative(req.params.id), req.body) if (resource) { res.locals.data = resource - } else { - res.status(404) - res.locals.data = {} } next() @@ -225,7 +169,7 @@ module.exports = function (source) { // DELETE /:resource/:id function destroy (req, res, next) { - db(req.params.resource).removeById(utils.toNative(req.params.id)) + db(name).removeById(utils.toNative(req.params.id)) // Remove dependents documents var removable = db._.getRemovable(db.object) @@ -238,23 +182,15 @@ module.exports = function (source) { next() } - router.get('/db', showDatabase, router.render) - - router.route('/:resource') + router.route('/') .get(list) .post(create) - router.route('/:resource/:id') + router.route('/:id') .get(show) .put(update) .patch(update) .delete(destroy) - router.get('/:parent/:parentId/:resource', list) - - router.all('*', function (req, res) { - router.render(req, res) - }) - return router } diff --git a/src/server/router/singular.js b/src/server/router/singular.js new file mode 100644 index 0000000..8c38ce9 --- /dev/null +++ b/src/server/router/singular.js @@ -0,0 +1,40 @@ +var express = require('express') +var utils = require('../utils') + +module.exports = function (db, name) { + + var router = express.Router() + + function show (req, res, next) { + res.locals.data = db.object[name] + next() + } + + function create (req, res, next) { + for (var prop in req.body) { + req.body[prop] = utils.toNative(req.body[prop]) + } + + res.locals.data = db.object[name] = req.body + res.status(201) + next() + } + + function update (req, res, next) { + for (var prop in req.body) { + db.object[name][prop] = utils.toNative(req.body[prop]) + } + + res.locals.data = db.object[name] + next() + } + + router.route('/') + .get(show) + .post(create) + .put(update) + .patch(update) + + return router + +} diff --git a/test/server/index.js b/test/server/plural.js similarity index 100% rename from test/server/index.js rename to test/server/plural.js diff --git a/test/server/singular.js b/test/server/singular.js new file mode 100644 index 0000000..ebec933 --- /dev/null +++ b/test/server/singular.js @@ -0,0 +1,67 @@ +var request = require('supertest') +var assert = require('assert') +var jsonServer = require('../../src/server') + +/* global beforeEach, describe, it */ + +describe('Server', function () { + + var server + var router + var db + + beforeEach(function () { + db = {} + + db.user = { + name: 'foo', + email: 'foo@example.com' + } + + server = jsonServer.create() + router = jsonServer.router(db) + server.use(router) + }) + + describe('GET /:resource', function () { + it('should respond with corresponding resource', function (done) { + request(server) + .get('/user') + .expect(db.user) + .expect(200, done) + }) + }) + + describe('POST /:resource', function () { + it('should create resource', function (done) { + var user = { name: 'bar' } + request(server) + .post('/user') + .send(user) + .expect(user) + .expect(201, done) + }) + }) + + describe('PUT /:resource', function () { + it('should uptade resource', function (done) { + var user = { name: 'bar' } + request(server) + .put('/user') + .send(user) + .expect(db.user) + .expect(200, done) + }) + }) + + describe('PATCH /:resource', function () { + it('should uptade resource', function (done) { + request(server) + .patch('/user') + .send({ name: 'bar' }) + .expect({ name: 'bar', email: 'foo@example.com' }) + .expect(200, done) + }) + }) + +})