diff --git a/ReadMe.md b/ReadMe.md index 13158d3..002917c 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,8 +1,8 @@ # warning - library is under active dev. docs and blog are currently out of sync, will be updating soon. # webservice.js - turn node.js modules into web-services -#### v0.4.8 -webservice.js is a somewhat opinionated node.js library that allows developers to easily create RESTFul web-services based on the exports of node.js modules. Having to deal with both implementing and consuming 100s of web-services over the past ten years, I've grown bitter of web-services with arbitrary restrictions, poor documentation, and piles of boiler-plate code. webservice.js tries to solve these problems by implementing RESTFul principals in a much more relaxed fashion. +#### v0.4.9 +webservice.js is a somewhat opinionated node.js library that allows developers to easily create RESTFul web-services based on the exports of node.js modules. Having to deal with both implementing and consuming 100s of web-services over the past ten years, I've grown bitter of web-services with arbitrary restrictions, poor documentation, and piles of boiler-plate code. webservice.js tries to solve these problems by implementing RESTFul principals in a much more relaxed fashion. webservice.js also plays very nice with node's httpServer and other middleware frameworks ( such as Connect ). @@ -68,12 +68,36 @@ Regular JavaScript methods are automatically transformed into API methods for yo -### The demo module +### demoModule.js + + this.title = "Welcome to your webservice!"; + this.name = "demo api module"; + this.version = "0.1.0"; + this.endpoint = "http://localhost:8080"; + + exports.echo = function(options, callback){ + callback(null, options.msg); + }; + exports.echo.description = "this is the echo method, it echos back your msg"; + exports.echo.schema = { + msg: { + type: 'string', + optional: false + } + }; + + exports.ping = function(options, callback){ + setTimeout(function(){ + callback(null, 'pong'); + }, 2000); + } + exports.ping.description = "this is the ping method, it pongs back after a 2 second delay"; -#### demoModule.js ## Usage +Once you have started up your web-service, visit http://localhost:8080/docs + ## tests tests are good. npm install vows, then run: diff --git a/examples/demoModule.js b/examples/demoModule.js index 09fb807..cfd8c6b 100644 --- a/examples/demoModule.js +++ b/examples/demoModule.js @@ -16,53 +16,9 @@ exports.echo.schema = { }; exports.ping = function(options, callback){ - setTimeout(function(){ callback(null, 'pong'); }, 2000); - } exports.ping.description = "this is the ping method, it pongs back after a 2 second delay"; - -exports.user = function(options, callback){ - - - switch(this.request.method){ - - case 'GET': - if(options.id){ - return callback(null, 'got the user with id ' + options.id); - } - else{ - return callback(null, 'got all users'); - } - break; - - case 'POST': - return callback(null, 'created a user with id ' + options.id + ' and arguments: ' + JSON.stringify(options)); - break; - - case 'UPDATE': - return callback(null, 'updated the user with id' + options.id); - break; - - case 'DELETE': - return callback(null, 'deleted the user with id' + options.id); - break; - - - } - -}; -exports.user.restful = true; -exports.user.schema = { - "name":{"type":"string"}, - "nickname":{"type":"string","optional":true}, - "url":{"type":"string","format":"url","optional":true}, - "email":{ - "type":"string", - "optional":false - } - }; -exports.user.description = "user is a restful resource. its actions will depend on the type of http verb you specify."; diff --git a/lib/createRouter.js b/lib/createRouter.js index da5619d..c3d386b 100644 --- a/lib/createRouter.js +++ b/lib/createRouter.js @@ -13,7 +13,6 @@ var ws = require('./webservice'), var createRouter = exports.createRouter = function createRouter( module, options ){ - var template = fs.readFileSync(__dirname + '/views/home.html'), routes = _createMetaRoutes( module ); @@ -31,21 +30,25 @@ var createRouter = exports.createRouter = function createRouter( module, options res.send(200, {'Content-Type': 'text/html'}, ws.view.renderRoutes('html', '', routes, template.toString())); }); + // returns the docs of the API + map.get('/docs.json').bind(function (res) { + res.send(200, {'Content-Type': 'text/html'}, ws.view.renderRoutes('html', '', routes, JSON.stringify(routes))); + }); + // returns the version of the API map.get('/version').bind(function (res) { res.send(200, {'Content-Type': 'text/html'}, { version: journey.version.join('.') }); }); // extend the Journey router with our generated routes based on the module - _extendRouter(map, module); + _extendRouter(map, module, options); }, { strict: false }); - return router; } -function _extendRouter( map, module ){ +function _extendRouter( map, module, options ){ // iterate through each top-level method in the module and create a route for it in journey for(var method in module){ @@ -56,28 +59,42 @@ function _extendRouter( map, module ){ var regex = new RegExp('\/' + method + '\/(.*?)'), regexfix = new RegExp('\/' + method), - journeyHandler = _createJourneyHandler(module, method); + journeyHandler = _createJourneyHandler(module, method, options); + + + // hard-coded for one additional layer of restful methods + for(var p in module[method]){ + if(typeof module[method][p] == "function"){ + var a = new RegExp('\/' + method + '\/([\w|\-]+)'); + map.route(a).bind(journeyHandler); + } + } + // we should only have one handler being bound, this is a regex bug map.route(regex).bind(journeyHandler); map.route(regexfix).bind(journeyHandler); - + } } -function _createJourneyHandler(module, method){ +function _createJourneyHandler(module, method, options){ var handler = function (res, resource, id, params) { - var args = [], options = {}, self = this; + var args = [], method_options = {}, self = this; + + console.log(resource); + console.log(id); + console.log(params); if(typeof resource != 'object'){ - options.id = resource; + method_options.id = resource; } for(var p in resource){ if(resource[p].length){ - options[p] = resource[p]; + method_options[p] = resource[p]; } } @@ -85,15 +102,21 @@ function _createJourneyHandler(module, method){ var posted = JSON.parse(this.request.body); for(var p in posted){ if(posted[p].length){ - options[p] = posted[p]; + method_options[p] = posted[p]; } } } catch(err){ } + + // bind all createHandler options to each argument + for(var p in options){ + method_options[p] = options[p]; + } + module[method].request = this.request; module[method].res = res; - args.push(options); + args.push(method_options); var callback = _createModuleCallback(self, res); diff --git a/package.json b/package.json index f8ef9af..7687b51 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "webservice", "description": "turns modules into RESTFul web-services", - "version": "0.4.8", + "version": "0.4.9", "author": "Marak Squires ", "repository": { "type": "git", diff --git a/server.js b/server.js index 1c79ce6..084715a 100644 --- a/server.js +++ b/server.js @@ -1,10 +1,5 @@ var webservice = require('./lib/webservice'), - demoModule = require('./examples/demoModule'), - fs = require('fs'), - sys = require('sys'), - assert = require('assert'); - + demoModule = require('./examples/demoModule'); webservice.createServer(demoModule).listen(8080); - -console.log(' > json webservice started on port 8080'); +console.log(' > json webservice started on port 8080'); \ No newline at end of file diff --git a/tests/test-webservice.js b/tests/test-webservice.js index d1b69d2..affd0b4 100644 --- a/tests/test-webservice.js +++ b/tests/test-webservice.js @@ -59,22 +59,6 @@ vows.describe('webservice/').addBatch({ assert.equal(body, 'ohai'); } }, - "a GET request against /echo?msg=ohai": { - topic: function() { - var options = { - uri: host + ':' + port + '/echo?msg=ohai', - method: 'GET' - }; - - request(options, this.callback) - }, - "should respond with 200": function (error, response, body) { - assert.equal(response.statusCode, 200); - }, - "should respond with ohai": function (error, response, body) { - assert.equal(body, 'ohai'); - } - }, "a GET request against /echo?msg=1": { topic: function() { var options = { @@ -91,21 +75,6 @@ vows.describe('webservice/').addBatch({ assert.equal(body, '1'); } }, - "a GET request against /aJSONPService?callback=jsonp1295581437634": { - topic: function() { - var options = { - uri: host + ':' + port + '/aJSONPService', - method: 'POST', - body: JSON.stringify({msg:"ohai"}) - } - }, - "should respond with 200": function (error, response, body) { - assert.equal(response.statusCode, 200); - }, - "should respond with ohai": function (error, response, body) { - assert.equal(body, '"ohai"'); - } - }, "a POST request to /echo with JSON": { topic: function() { var options = { @@ -171,8 +140,40 @@ vows.describe('webservice/').addBatch({ "should respond with pong": function (error, response, body) { assert.equal(body, 'pong'); } + }, + "a GET request against /docs": { + topic: function() { + var options = { + uri: host + ':' + port + '/docs', + method: 'GET' + }; + + request(options, this.callback) + }, + "should respond with 200": function (error, response, body) { + assert.equal(response.statusCode, 200); + }, + "should return html view representing web-service": function (error, response, body) { + assert.equal(body[0], '<'); + } + }, + "a GET request against /docs.json": { + topic: function() { + var options = { + uri: host + ':' + port + '/docs.json', + method: 'GET' + }; + + request(options, this.callback) + }, + "should respond with 200": function (error, response, body) { + assert.equal(response.statusCode, 200); + }, + "should return JSON representing web-service": function (error, response, body) { + var json = JSON.parse(body); + assert.isObject(json); + } } - } }).addBatch({ "when the tests are over": {