diff --git a/Gruntfile.js b/Gruntfile.js index 122d93ee10..530074150e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -25,6 +25,9 @@ module.exports = function (grunt) { files: ['lib/**/*.js', 'test/**/*.js', 'Gruntfile.js', 'package.json'] }, simplemocha: { + application: { + src: ['test/application.test.js'] + }, mixins: { src: ['test/mixins/**/*.test.js'] }, @@ -39,5 +42,7 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-jsbeautifier'); grunt.loadNpmTasks('grunt-simple-mocha'); - grunt.registerTask('default', ['jsbeautifier', 'jshint', 'simplemocha']); + grunt.registerTask('test', 'simplemocha'); + grunt.registerTask('beautify', 'jsbeautifier'); + grunt.registerTask('default', ['jshint', 'simplemocha']); }; diff --git a/documentation.md b/documentation.md new file mode 100644 index 0000000000..1468708c88 --- /dev/null +++ b/documentation.md @@ -0,0 +1,424 @@ +## Introduction + +Feathers sits right on top of Express, one of the most popular web frameworks for NodeJS. If you are not familiar with Express head over to the [Express Guides](http://expressjs.com/guide.html). Feathers works the exact same way except that `var app = require('express')();` is replaced with `var app = require('feathers')()`. The small differences and additional functionality available is outline in the following documentation. + +## Configuration + +### REST + +Exposing services through a RESTful JSON interface is enabled by default. If you only want to use SocketIO +call `app.disabled('feathers rest')` _before_ registering any services. + +### SocketIO + +To expose services via [SocketIO](http://socket.io/) call `app.configure(feathers.socketio())`. It is also possible pass a `function(io) {}` when initializing the provider where `io` is the main SocketIO object so you can listen to custom events, change the configuration or add [authorization](https://github.com/LearnBoost/socket.io/wiki/Authorizing): + +```js +app.configure(feathers.socketio(function(io) { + io.on('connection', function(socket) { + socket.emit('news', { hello: 'world' }); + socket.on('my other event', function (data) { + console.log(data); + }); + }); + + io.set('authorization', function (handshakeData, callback) { + app.lookup('users').find({ + username: handshakeData.username, + password: handshakeData.password + }, callback); + }); +})); +``` + +Once the server has been started with `app.listen()` the SocketIO object is available as `app.io`. + +## API + +### listen + +`app.listen([port])` starts the application on the given port. Before calling the original [Express app.listen([port])](http://expressjs.com/api.html#app.listen) Feathers will initialize the SocketIO server (if set up) and call all services `setup(app, path)` methods in the order they have been registered. + +```js +var app = feathers(); +app.use('/todos', { + setup: function(app, path) { + // path -> 'todos' + } +}); + +var server = app.listen(8080); + +server.close(); +``` + +### lookup + +`app.lookup(path)` returns the wrapped service object for the given path. Note that the returned object will provide the same methods and functionality as the original service but actually is a new object with additional functionality added (most notably it is possible to listen to service events). `path` can be the service name with or without leading and trailing slashes. + +```js +app.use('/my/todos', { + create: function(data, params, callback) { + callback(null, data); + } +}); + +var todoService = app.lookup('my/todos'); +// todoService is an event emitter +todoService.on('created', function(todo) { + console.log('Created todo', todo); +}); +``` + +### use + +`app.use([path], service)` works just like [Express app.use([path], middleware)](http://expressjs.com/api.html#app.use) but additionally allows to register a service object (an object which at least provides one of the service methods as outlined in the Services section) instead of the middleware function. Note that REST services are registered in the same order as any other middleware so the below example will allow the `/todos` service only to [Passport](http://passportjs.org/) authenticated users. + +```js +// Serve public folder for everybody +app.use(feathers.static(__dirname + '/public'); +// Make sure that everything else only works with authentication +app.use(function(req,res,next){ + if(req.isAuthenticated()){ + next(); + } else { + // 401 Not Authorized + next(new Error(401)); + } +}); +// Add a service. +app.use('/todos', { + get: function(name, params, callback) { + callback(null, { + id: name, + description: "You have to do " + name + "!" + }); + } +}); +``` + +## Services + +A service can be any JavaScript object that offers one or more of the `find`, `get`, `create`, `update`, `remove` and `setup` service methods: + +```js +var myService = { + find: function(params, callback) {}, + get: function(id, params, callback) {}, + create: function(data, params, callback) {}, + update: function(id, data, params, callback) {}, + remove: function(id, params, callback) {}, + setup: function(app) {} +} +``` + +All callbacks follow the `function(error, data)` NodeJS convention. `params` can contain any additional parameters, for example the currently authenticated user. REST service calls set `params.query` with the query parameters (e.g. a query string like `?status=active&type=user` becomes `{ status: "active", type: "user" }`). + +### find + +`find(params, callback)` retrieves a list of all resources from the service. Ideally use `params.query` for things like filtering and paging so that REST calls like `todo?status=completed&user=10` work right out of the box. + +__REST__ + + GET todo?status=completed&user=10 + +__SocketIO__ + +```js +socket.emit('todo::find', { + query: { + status: 'completed' + user: 10 + } +}, function(error, data) { +}); +``` + +### get + +`get(id, params, callback)` retrieves a single resource with the given `id` from the service. + +__REST__ + + GET todo/1 + +__SocketIO__ + +```js +socket.emit('todo::get', 1, {}, function(error, data) { + +}); +``` + +### create + +`create(data, params, callback)` creates a new resource with `data`. The callback should be called with the newly +created resource data. + +__REST__ + + POST todo + { "description": "I really have to iron" } + +By default the body can be eihter JSON or form encoded as long as the content type is set accordingly. + +__SocketIO__ + +```js +socket.emit('todo::create', { + description: 'I really have to iron' +}, function(error, data) { +}); +``` + +### update + +`update(id, data, params, callback)` updates the resource identified by `id` using `data`. The callback should +be called with the updated resource data. + +__REST__ + + PUT todo/2 + { "description": "I really have to do laundry" } + +__SocketIO__ + +```js +socket.emit('todo::update', 2, { + description: 'I really have to do laundry' +}, {}, function(error, data) { + // data -> { id: 2, description: "I really have to do laundry" } +}); +``` + +### remove + +`remove(id, params, callback)` removes the resource with `id`. The callback should be called with the removed resource. + +__REST__ + + DELETE todo/2 + +__SocketIO__ + +```js +socket.emit('todo::remove', 2, {}, function(error, data) { +}); +``` + +### setup + +`setup(app, path)` initializes the service passing an instance of the Feathers application and the path it has been registered on. The SocketIO server is available via `app.io`. `setup` is a great way to connect services: + +```js +var todoService = { + get: function(name, params, callback) { + callback(null, { + id: name, + description: 'You have to ' + name + '!' + }); + } +}; + +var myService = { + setup: function(app) { + this.todo = app.lookup('todo'); + }, + + get: function(name, params, callback) { + this.todo.get('take out trash', {}, function(error, todo) { + callback(null, { + name: name, + todo: todo + }); + }); + } +} + +feathers() + .use('todo', todoService) + .use('my', myService) + .listen(8000); +``` + +You can see the combination when going to `http://localhost:8000/my/test`. + +__Pro tip:__ + +Bind the apps `lookup` method to your service to always look your services up dynamically: + +``` +var myService = { + setup: function(app) { + this.lookup = app.lookup.bind(app); + }, + + get: function(name, params, callback) { + this.lookup('todos').get('take out trash', {}, function(error, todo) { + callback(null, { + name: name, + todo: todo + }); + }); + } +} +``` + +## Events + +Any registered service will be automatically turned into an event emitter that emits events when a resource has changed, that is a `create`, `update` or `remove` service call returned successfully. It is therefore possible to bind to the below events via `app.lookup(servicename).on()` and, if enabled, all events will also broadcast to all connected SocketIO clients in the form of ` `. + +### created + +The `created` event will be published with the callback data when a service `create` calls back successfully. + +```js +app.use('/todos', { + create: function(data, params, callback) { + callback(null, data); + } +}); + +app.lookup('/todos').on('created', function(todo) { + console.log('Created todo', todo); +}); + +app.lookup('/todos').create({ + description: 'We have to do something!' +}, {}, function(error, callback) { + // ... +}); + +app.listen(8000); +``` + +__SocketIO__ + +```html + + +``` + +### updated + +The `updated` event will be published with the callback data when a service `update` calls back successfully. + +```js +app.use('/todos', { + update: function(id, data, params, callback) { + callback(null, data); + } +}); + +app.lookup('/todos').on('updated', function(todo) { + console.log('Updated todo', todo); +}); + +app.listen(8000); +``` + +__SocketIO__ + +```html + + +``` + +### removed + +The `removed` event will be published with the callback data when a service `remove` calls back successfully. + +```js +app.use('/todos', { + remove: function(id, params, callback) { + callback(null, { id: id }); + } +}); + +app.lookup('/todos').remove(1, {}, function(error, callback) { + // ... +}); + +app.listen(8000); +``` + +__SocketIO__ + +```html + + +``` + +## Why? + +We know... Oh God another NodeJS framework! Yes we are also very tired of seeing all these NodeJS frameworks. All the rails clones are getting a bit boring and really aren't taking advantage of the real strengths of NodeJS. We wanted to take a different approach than every other framework we have seen, because we believe that data is core to the web and should be the core focus of web applications. + +We also think that your data resources can and should be encapsulated in such a way that they can be ultra scalable and self contained. The MVC pattern works well but it is becoming antiquated in today's web. Frankly you don't need it and they tend to become bloated. + +With that being said there are some amazing frameworks already out there and we wanted to leverage the ideas that have been put into them, which is why Feathers is built on top of [Express](http://expressjs.com) and is inspired in part by [Sails](http://sailsjs.org), [Flatiron](http://flatironjs.org) and [Derby](http://derbyjs.com). + +## Changelog + +__0.2.0__ + +- Pre-initialize `req.feathers` in REST provider to set service parameters +- Allowing to initialize services with or without slashes to be more express-compatible + +__0.1.0__ + +- First beta release +- Directly extends Express +- Removed built in services and moved to [Legs](https://github.com/feathersjs/legs) +- Created [example repository](https://github.com/feathersjs/examples) + +__0.0.x__ + +- Initial test alpha releases + +## License + +Copyright (C) 2013 David Luecke daff@neyeon.com +Copyright (C) 2013 Eric Kryski e.kryski@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/application.js b/lib/application.js index 7b1b785ed3..60fa92faa5 100644 --- a/lib/application.js +++ b/lib/application.js @@ -4,6 +4,9 @@ var Proto = require('uberproto'); var _ = require('underscore'); var mixins = require('./mixins'); +var stripSlashes = function (name) { + return name.replace(/^\/|\/$/g, ''); +}; module.exports = { init: function () { @@ -25,6 +28,8 @@ module.exports = { var protoService = Proto.extend(service); var self = this; + location = stripSlashes(location); + // Add all the mixins _.each(this.mixins, function (fn) { fn.call(self, protoService); @@ -44,7 +49,7 @@ module.exports = { }, lookup: function (location) { - return this.services[location]; + return this.services[stripSlashes(location)]; }, listen: function () { diff --git a/lib/providers/rest.js b/lib/providers/rest.js index c564575ab4..f776920b0f 100644 --- a/lib/providers/rest.js +++ b/lib/providers/rest.js @@ -27,7 +27,7 @@ module.exports = function (config) { return function () { var app = this; - var responder = app.get('feathers rest handler') || function (req, res) { + var responder = config.responder || function (req, res) { res.format(_.extend({ 'application/json': function () { res.json(res.data); @@ -37,6 +37,11 @@ module.exports = function (config) { 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')) { diff --git a/lib/providers/socketio.js b/lib/providers/socketio.js index 5e6f671f71..d02e226ee7 100644 --- a/lib/providers/socketio.js +++ b/lib/providers/socketio.js @@ -30,7 +30,7 @@ module.exports = function (config) { return httpServer; } - var io = this._io = listen(httpServer); + var io = this.io = listen(httpServer); _.each(services, function (service, path) { // If the service emits events that we want to listen to (Event mixin) @@ -54,6 +54,10 @@ module.exports = function (config) { }); }); + if(typeof config === 'function') { + config.call(this, io); + } + return httpServer; }; diff --git a/package.json b/package.json index 4264c4d3bf..fac14292a8 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "feathers", "description": "An ultra scalable, feather weight, data oriented framework", - "version": "0.1.0", - "homepage": "https://feathersjs.com", + "version": "0.2.0", + "homepage": "http://feathersjs.com", "repository": { "type": "git", "url": "git://github.com/feathersjs/feathers.git" @@ -34,11 +34,11 @@ "npm": "*" }, "dependencies": { - "uberproto": ">= 1.0.0", - "express": "~ 3.1.1", + "uberproto": "~ 1.1.0", + "express": "~ 3.4.0", "rubberduck": "~0.2.0", - "underscore": "~1.4.4", - "socket.io": "~0.9.14" + "underscore": "~1.5.0", + "socket.io": "~0.9.0" }, "devDependencies": { "request": "~2.21.0", diff --git a/readme.md b/readme.md index efc9921a2d..14dfcd80a5 100644 --- a/readme.md +++ b/readme.md @@ -16,7 +16,7 @@ As with any NodeJS module, just install it as a dependency in your application: ## Getting Started Is Easy -Building an app with Feathers is easy. There are only 4 things to worry about. A wrapped express server, providers, services & middleware. Services are just simple modules that expose certain methods to the providers in order to CRUD your data. We can easily initialize a service that say... provides a single TODO: +Building an app with Feathers is easy. There are only 4 things to worry about. A wrapped express server, providers, services & middleware. Services are just simple modules that expose certain methods to the providers in order to CRUD your data. We can easily initialize a service that say... provides a single Todo: ```js var feathers = require('feathers'); @@ -32,7 +32,7 @@ var todoService = { feathers() .configure(feathers.socketio()) - .use('todo', todoService) + .use('/todo', todoService) .listen(8000); ``` @@ -67,216 +67,6 @@ Create an HTML page and insert the following code to see the response data logge ``` -## Services +## What's next? -A service can be any JavaScript object that offers one or more of the `find`, `get`, `create`, `update`, -`destroy` and `setup` service methods: - -```js -var myService = { - find: function(params, callback) {}, - get: function(id, params, callback) {}, - create: function(data, params, callback) {}, - update: function(id, data, params, callback) {}, - destroy: function(id, params, callback) {}, - setup: function(app) {} -} -``` - -All callbacks follow the `function(error, data)` NodeJS convention. `params` can contain any additional parameters, for -example the currently authenticated user. REST service calls set `params.query` with the query parameters (e.g. a query string -like `?status=active&type=user` becomes `{ status: "active", type: "user" }`). - -### `find(params, callback)` - -Retrieves a list of all resources from the service. Ideally use `params.query` for things like filtering and paging so -that REST calls like `todo?status=completed&user=10` work right out of the box. - -__REST__ - -> GET todo?status=completed&user=10 - -__SocketIO__ - -```js -socket.emit('todo::find', { - status: 'completed' - user: 10 -}, function(error, data) { -}); -``` - -### `get(id, params, callback)` - -Retrieves a single resource with the given `id` from the service. - -__REST__ - -> GET todo/1 - -__SocketIO__ - -```js -socket.emit('todo::get', 1, {}, function(error, data) { - -}); -``` - -### create(data, params, callback) - -Creates a new resource with `data`. The callback should be called with that resource (and the id initialized). - -__REST__ - -> POST todo -> { "description": "I really have to iron" } - -By default the body can be eihter JSON or form encoded as long as the content type is set accordingly. - -__SocketIO__ - -```js -socket.emit('todo::create', { - description: 'I really have to iron' -}, function(error, data) { -}); -``` - -### update(id, data, params, callback) - -Updates the resource identified by `id` using `data`. - -__REST__ - -> PUT todo/2 -> { "description": "I really have to do laundry" } - -__SocketIO__ - -```js -socket.emit('todo::update', 2, { - description: 'I really have to do laundry' -}, {}, function(error, data) { - // data -> { id: 2, description: "I really have to do laundry" } -}); -``` - -### remove(id, params, callback) - -Remove the resource with `id`. - -__REST__ - -> DELETE todo/2 - -__SocketIO__ - -```js -socket.emit('todo::delete', 2, {}, function(error, data) { -}); -``` - -### setup(app) - -Initializes the service passing an instance of the Feathers application. -`app` can do everything a normal Express application does and additionally provides `app.lookup(path)` -to retrieve another service by its path. `setup` is a great way to connect services: - -```js -var todoService = { - get: function(name, params, callback) { - callback(null, { - id: name, - description: 'You have to ' + name + '!' - }); - } -}; - -var myService = { - setup: function(app) { - this.todo = app.lookup('todo'); - }, - - get: function(name, params, callback) { - this.todo.get('take out trash', {}, function(error, todo) { - callback(null, { - name: name, - todo: todo - }); - }); - } -} - -feathers() - .use('todo', todoService) - .use('my', myService) - .listen(8000); -``` - -You can see the combination when going to `http://localhost:8000/my/test`. - -## Getting Real, Time - -The secret ingredient to create real time applications using Feathers and SocketIO is the -`created`, `updated` and `removed` events every Feathers service automatically emits. -Here is another simple Todo service, that just passes the data through `create`: - -```js -var feathers = require('feathers'); - -var todoService = { - create: function(data, params, callback) { - callback(null, data); - } -}; - -var app = feathers() - .configure(feathers.socketio()) - .use('todo', todoService) - .listen(8000); -``` - -Lets make an HTML file that creates a new Todo using SocketIO every two seconds: - -```html - - -``` - -In another file we just listen to the `todo created` event and log it: - -```html - - -``` -When visiting both HTMl files in a browser at the same time you should see a new Todo being logged every -two seconds on both pages. - - -## Why Another NodeJS Framework? - -We know... Oh God another bloody NodeJS framework! Yes we are also very tired of seeing all these NodeJS frameworks. All the rails clones are getting a bit boring and really aren't taking advantage of the real strengths of NodeJS. We wanted to take a different approach than every other framework we have seen, because we believe that data is core to the web and should be the core focus of web applications. - -We also think that your data resources can and should be encapsulated in such a way that they can be ultra scalable and self contained. The MVC pattern works well but it is becoming antiquated in today's web. Frankly you don't need it and they tend to become bloated. - -With that being said there are some amazing frameworks already out there and we wanted to leverage the ideas that have been put into them, which is why Feathers is built on top of [Express](http://expressjs.com) and is inspired in part by [Sails](http://sailsjs.org), [Flatiron](http://flatironjs.org) and [Derby](http://derbyjs.com). +Head over to the Feathers website at [feathersjs.com](http://feathersjs.com/) for more examples and the detailed documenation. \ No newline at end of file diff --git a/test/application.test.js b/test/application.test.js new file mode 100644 index 0000000000..f31c961903 --- /dev/null +++ b/test/application.test.js @@ -0,0 +1,86 @@ +'use strict'; + +var assert = require('assert'); +var Proto = require('uberproto'); +var io = require('socket.io-client'); +var request = require('request'); + +var feathers = require('../lib/feathers'); +var express = require('express'); + +describe('Feathers application', function () { + it('registers service and looks it up with and without leading and trailing slashes', function () { + var dummyService = { + find: function (params, callback) { + // No need to implement this + } + }; + + var app = feathers().use('/dummy/service/', dummyService); + + assert.ok(typeof app.lookup('dummy/service').find === 'function', 'Could look up without slashes'); + assert.ok(typeof app.lookup('/dummy/service').find === 'function', 'Could look up with leading slash'); + assert.ok(typeof app.lookup('dummy/service/').find === 'function', 'Could look up with trailing slash'); + }); + + it('registers a service, wraps it and adds the event mixin', function (done) { + var dummyService = { + create: function (data, params, callback) { + callback(null, data); + } + }; + + var app = feathers().use('/dummy', dummyService); + var server = app.listen(7887); + var wrappedService = app.lookup('dummy'); + + assert.ok(Proto.isPrototypeOf(wrappedService), 'Service got wrapped as Uberproto object'); + assert.ok(typeof wrappedService.on === 'function', 'Wrapped service is an event emitter'); + + wrappedService.on('created', function (data) { + assert.equal(data.message, 'Test message', 'Got created event with test message'); + server.close(done); + }); + + wrappedService.create({ + message: 'Test message' + }, {}, function (error, data) { + assert.ok(!error, 'No error'); + assert.equal(data.message, 'Test message', 'Got created event with test message'); + }); + }); + + it('adds REST by default and registers SocketIO provider', function (done) { + var todoService = { + get: function (name, params, callback) { + callback(null, { + id: name, + description: "You have to do " + name + "!" + }); + } + }; + + var oldlog = console.log; + console.log = function () {}; + + var app = feathers().configure(feathers.socketio()).use('/todo', todoService); + var server = app.listen(6999).on('listening', function () { + console.log = oldlog; + + var socket = io.connect('http://localhost:6999'); + + request('http://localhost:6999/todo/dishes', function (error, response, body) { + assert.ok(response.statusCode === 200, 'Got OK status code'); + var data = JSON.parse(body); + assert.equal(data.description, 'You have to do dishes!'); + + socket.emit('todo::get', 'laundry', {}, function (error, data) { + assert.equal(data.description, 'You have to do laundry!'); + + socket.disconnect(); + server.close(done); + }); + }); + }); + }); +}); diff --git a/test/providers/rest.test.js b/test/providers/rest.test.js index c1c0d8f31d..dd92750eee 100644 --- a/test/providers/rest.test.js +++ b/test/providers/rest.test.js @@ -10,12 +10,11 @@ var verify = fixture.verify; describe('REST provider', function () { describe('CRUD', function () { - var server; + var server, app; before(function () { - server = feathers() - .use('todo', todoService) - .listen(3000); + app = feathers().use('todo', todoService); + server = app.listen(4777); }); after(function (done) { @@ -23,7 +22,7 @@ describe('REST provider', function () { }); it('GET .find', function (done) { - request('http://localhost:3000/todo', function (error, response, body) { + request('http://localhost:4777/todo', function (error, response, body) { assert.ok(response.statusCode === 200, 'Got OK status code'); verify.find(JSON.parse(body)); done(error); @@ -31,7 +30,7 @@ describe('REST provider', function () { }); it('GET .get', function (done) { - request('http://localhost:3000/todo/dishes', function (error, response, body) { + request('http://localhost:4777/todo/dishes', function (error, response, body) { assert.ok(response.statusCode === 200, 'Got OK status code'); verify.get('dishes', JSON.parse(body)); done(error); @@ -44,7 +43,7 @@ describe('REST provider', function () { }; request({ - url: 'http://localhost:3000/todo', + url: 'http://localhost:4777/todo', method: 'post', body: JSON.stringify(original), headers: { @@ -64,7 +63,7 @@ describe('REST provider', function () { }; request({ - url: 'http://localhost:3000/todo/544', + url: 'http://localhost:4777/todo/544', method: 'put', body: JSON.stringify(original), headers: { @@ -80,7 +79,7 @@ describe('REST provider', function () { it('DELETE .remove', function (done) { request({ - url: 'http://localhost:3000/todo/233', + url: 'http://localhost:4777/todo/233', method: 'delete' }, function (error, response, body) { assert.ok(response.statusCode === 200, 'Got OK status code'); @@ -90,4 +89,35 @@ describe('REST provider', function () { }); }); }); + + it('sets service parameters', function (done) { + var service = { + get: function (id, params, callback) { + callback(null, params); + } + }; + + var server = feathers() + .use(function (req, res, next) { + assert.ok(req.feathers, 'Feathers object initialized'); + req.feathers.test = 'Happy'; + next(); + }) + .use('service', service) + .listen(4778); + + request('http://localhost:4778/service/bla?some=param&another=thing', function (error, response, body) { + var expected = { + test: 'Happy', + query: { + some: 'param', + another: 'thing' + } + }; + + assert.ok(response.statusCode === 200, 'Got OK status code'); + assert.deepEqual(JSON.parse(body), expected, 'Got params object back'); + server.close(done); + }); + }); }); diff --git a/test/providers/socketio.test.js b/test/providers/socketio.test.js index 912df36904..daa59b77b4 100644 --- a/test/providers/socketio.test.js +++ b/test/providers/socketio.test.js @@ -3,6 +3,7 @@ var assert = require('assert'); var feathers = require('../../lib/feathers'); var io = require('socket.io-client'); +var request = require('request'); var fixture = require('./service-fixture'); var todoService = fixture.Service; @@ -20,11 +21,11 @@ describe('SocketIO provider', function () { server = feathers() .configure(feathers.socketio()) .use('todo', todoService) - .listen(3000); + .listen(7886); console.log = oldlog; - socket = io.connect('http://localhost:3000'); + socket = io.connect('http://localhost:7886'); }); after(function (done) {