diff --git a/documentation.md b/documentation.md index c3b728ca43..33a858e91b 100644 --- a/documentation.md +++ b/documentation.md @@ -107,7 +107,7 @@ In the Browser you can connect like this: console.log('Someone created a Todo', todo); }); - primus.send('todos::create', { description: 'Do something', {}, function() { + primus.send('todos::create', { description: 'Do something' }, {}, function() { primus.send('todos::find', {}, function(error, todos) { console.log(todos); }); diff --git a/lib/providers/rest.js b/lib/providers/rest.js deleted file mode 100644 index f776920b0f..0000000000 --- a/lib/providers/rest.js +++ /dev/null @@ -1,82 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -var _wrapper = function (req, res, next) { - return function (error, data) { - if (error) { - return next(error); - } - res.data = data; - return next(); - }; -}; -var _getParams = function (req) { - var query = req.query || {}; - return _.extend({ - query: query - }, req.feathers); -}; -var toUri = function (name) { - // TODO - return '/' + name; -}; - -module.exports = function (config) { - config = config || {}; - - return function () { - var app = this; - var responder = config.responder || function (req, res) { - res.format(_.extend({ - 'application/json': function () { - res.json(res.data); - } - }, config.formatters)); - }; - - app.enable('feathers rest'); - - app.use(function (req, res, next) { - req.feathers = {}; - next(); - }); - - // Register the REST provider - app.providers.push(function (path, service) { - if (app.disabled('feathers rest')) { - return; - } - - var uri = toUri(path); - // TODO throw 405 Method Not Allowed with allowed methods - - // GET / -> resource.index(cb, params) - app.get(uri, function (req, res, next) { - service.find(_getParams(req), _wrapper(req, res, next)); - }); - - // GET /:id -> resource.get(cb, id, params) - app.get(uri + '/:id', function (req, res, next) { - service.get(req.params.id, _getParams(req), _wrapper(req, res, next)); - }); - - // POST -> resource.create(cb, data, params) - app.post(uri, function (req, res, next) { - service.create(req.body, _getParams(req), _wrapper(req, res, next)); - }); - - // PUT /:id -> resource.update(cb, id, data, params) - app.put(uri + '/:id', function (req, res, next) { - service.update(req.params.id, req.body, _getParams(req), _wrapper(req, res, next)); - }); - - // DELETE /:id -> resource.destroy(cb, id, params) - app.del(uri + '/:id', function (req, res, next) { - service.remove(req.params.id, _getParams(req), _wrapper(req, res, next)); - }); - - app.use(uri, responder); - }); - }; -}; diff --git a/lib/providers/rest/index.js b/lib/providers/rest/index.js new file mode 100644 index 0000000000..a0042ddbc1 --- /dev/null +++ b/lib/providers/rest/index.js @@ -0,0 +1,51 @@ +'use strict'; + +var _ = require('underscore'); +var wrappers = require('./wrappers'); + +module.exports = function (config) { + config = config || {}; + + var responder = config.responder || function (req, res) { + res.format(_.extend({ + 'application/json': function () { + res.json(res.data); + } + }, config.formatters)); + }; + + return function () { + var app = this; + + app.enable('feathers rest'); + + app.use(function (req, res, next) { + req.feathers = {}; + next(); + }); + + app.rest = wrappers; + + // Register the REST provider + app.providers.push(function (path, service) { + if (app.disabled('feathers rest')) { + return; + } + + var uri = path.indexOf('/') === 0 ? path : '/' + path; + + // GET / -> service.find(cb, params) + app.get(uri, app.rest.find(service)) + // GET /:id -> service.get(cb, id, params) + .get(uri + '/:id', app.rest.get(service)) + // POST -> service.create(cb, data, params) + .post(uri, app.rest.create(service)) + // PUT /:id -> service.update(cb, id, data, params) + .put(uri + '/:id', app.rest.update(service)) + // DELETE /:id -> service.remove(cb, id, params) + .del(uri + '/:id', app.rest.remove(service)); + + app.use(uri, responder); + }); + }; +}; diff --git a/lib/providers/rest/wrappers.js b/lib/providers/rest/wrappers.js new file mode 100644 index 0000000000..d5b7442f3a --- /dev/null +++ b/lib/providers/rest/wrappers.js @@ -0,0 +1,115 @@ +var _ = require('underscore'); + +/** + * Return a service callback that sets the data in the given + * response and calls next on errors. + * + * @param req + * @param res + * @param next + * @returns {Function} + */ +function wrap(req, res, next) { + return function(error, data) { + if (error) { + return next(error); + } + res.data = data; + return next(); + }; +} + +/** + * Returns the service params, setting params.query + * to an empty object (if not set) and grabbing anything set + * in req.feathers. + * + * @param req The request + * @returns {Object} The service parameters + */ +function getParams(req) { + var query = req.query || {}; + return _.extend({ + query: query + }, req.feathers); +} + +/** + * Checks if the service method is available. If not, sets the response HTTP status to + * 405 (Method not allowed) and returns an error. + * + * @param res The HTTP response + * @param service The wrapped service object + * @param name The method name to check for + * @returns {Error|false} `false` or an error object with the description + */ +function checkMethod(res, service, name) { + if (typeof service[name] !== 'function') { + res.status(405); + return new Error('Can not call service method .' + name); + } + + return false; +} + +/** + * Returns wrapped middleware for a service method. + * + * @type {{find: find, get: get, create: create, update: update, remove: remove}} + */ +module.exports = { + find: function(service) { + return function(req, res, next) { + var error = checkMethod(res, service, 'find'); + if (error) { + return next(error); + } + + service.find(getParams(req), wrap(req, res, next)); + }; + }, + + get: function(service) { + return function(req, res, next) { + var error = checkMethod(res, service, 'get'); + if (error) { + return next(error); + } + + service.get(req.params.id, getParams(req), wrap(req, res, next)); + }; + }, + + create: function(service) { + return function(req, res, next) { + var error = checkMethod(res, service, 'create'); + if (error) { + return next(error); + } + + service.create(req.body, getParams(req), wrap(req, res, next)); + }; + }, + + update: function(service) { + return function(req, res, next) { + var error = checkMethod(res, service, 'update'); + if (error) { + return next(error); + } + + service.update(req.params.id, req.body, getParams(req), wrap(req, res, next)); + }; + }, + + remove: function(service) { + return function(req, res, next) { + var error = checkMethod(res, service, 'remove'); + if (error) { + return next(error); + } + + service.remove(req.params.id, getParams(req), wrap(req, res, next)); + }; + } +}; diff --git a/package.json b/package.json index fa0a0c5132..d54277ed95 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "grunt": "~0.4.1", "grunt-release": "~0.5.1", "mocha": "~1.11.0", - "grunt-contrib-jshint": "~0.6.3", + "grunt-contrib-jshint": "~0.x", "grunt-simple-mocha": "~0.4.0", "grunt-jsbeautifier": "~0.2.2" } diff --git a/test/providers/rest.test.js b/test/providers/rest.test.js index dd92750eee..bcec86d708 100644 --- a/test/providers/rest.test.js +++ b/test/providers/rest.test.js @@ -120,4 +120,32 @@ describe('REST provider', function () { server.close(done); }); }); + + it('throws a 405 for undefined service methods', function(done) { + var app = feathers().use('todo', { + get: function(id, params, callback) { + callback(null, { description: 'You have to do ' + id }); + } + }); + + /* jshint ignore:start */ + // Error handler + app.use(function(error, req, res, next) { + assert.equal(error.message, 'Can not call service method .find'); + res.json({ message: error.message }); + }); + /* jshint ignore:end */ + + var server = app.listen(4777); + + request('http://localhost:4777/todo/dishes', function (error, response, body) { + assert.ok(response.statusCode === 200, 'Got OK status code for .get'); + assert.deepEqual(JSON.parse(body), { description: 'You have to do dishes' }, 'Got expected object'); + request('http://localhost:4777/todo', function (error, response, body) { + assert.ok(response.statusCode === 405, 'Got 405 for .find'); + assert.deepEqual(JSON.parse(body), { message: 'Can not call service method .find' }, 'Error serialized as expected'); + server.close(done); + }); + }); + }); });