From f5a4c9b427f09d1311a1d8fe3a0835867c1c6ae1 Mon Sep 17 00:00:00 2001 From: Saar Yahalom Date: Thu, 3 May 2012 18:17:43 +0300 Subject: [PATCH] update docRouter to support restdoc initial draft --- README.md | 5 +- doc.jade | 117 ++++++++++++++++----------- index.js | 117 +++++++++++++++++++++------ json2wadl.js | 179 ----------------------------------------- tests/sample.js | 43 ++++++++++ tests/testDocRouter.js | 99 +++++++++-------------- tests/testJson2Wadl.js | 34 -------- 7 files changed, 246 insertions(+), 348 deletions(-) delete mode 100644 json2wadl.js create mode 100644 tests/sample.js delete mode 100644 tests/testJson2Wadl.js diff --git a/README.md b/README.md index 55dedb0..2b32ada 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # DocRouter # A Connect/Express router wrapper which exposes a formatted description of the available services of a server. -The documentation is available is multiple formats (Html, Wadl, Json). +The documentation is available is multiple formats (RestDoc Json, Html). The usage mimics the regular router's behavior. @@ -8,11 +8,10 @@ The usage mimics the regular router's behavior. Two options: - !! (GET http://myservice.mydomain.com/!!) -- OPTIONS (OPTIONS http://myservice.mydomain.com/) +- OPTIONS (OPTIONS http://myservice.mydomain.com/*) ## Supported outputs ## - JSON (Accept: application/json) -- WADL (Accept: text/xml) - HTML (Accept: text/html) docRouter follows the ___RestDoc___ spec (https://github.com/RestDoc), RestDoc is a live spec so please diff --git a/doc.jade b/doc.jade index aac0b0f..b1fe071 100644 --- a/doc.jade +++ b/doc.jade @@ -85,15 +85,13 @@ html(lang="en") body .wrapper h1= baseUrl - each method in methodJsons + each resource in restdoc.resources .methodDescription - label.methodType= method.method - label.methodPath= method.path - .doc= method.doc - - if method.params + h1= resource.id + .doc= resource.description + if resource.params h2 Parameters - each param, paramName in method.params + each param, paramName in resource.params .param ul li @@ -101,50 +99,75 @@ html(lang="en") label em #{paramName} li - if param.doc - .doc= param.doc + if param.description + .doc= param.description ul each val, key in param - if key != "doc" + if key != "description" && key != "doc" li strong #{key}:#{" "} em #{val} - if method.request - h2 Request - if method.request.doc - .doc= method.request.doc - if method.request.representations - ul - each val, key in method.request.representations - li #{val} - each param, paramName in method.request.params - .param + + each method, methodType in resource.methods + .methodDescription + label.methodType= methodType + label.methodPath= method.path + .doc= method.description + + if method.params + h2 Parameters + each param, paramName in method.params + .param + ul + li + .header + label + em #{paramName} + li + if param.description + .doc= param.description + ul + each val, key in param + if key != "description" && key != "doc" + li + strong #{key}:#{" "} + em #{val} + if method.request + h2 Request + if method.request.description + .doc= method.request.description + if method.request.representations ul - li - .header - label - em #{paramName} - li - if param.doc - .doc= param.doc - ul - each val, key in param - if key != "doc" - li - strong #{key}:#{" "} - em #{val} - if method.request.example - h3 Example - pre.source-code= method.request.example - if method.response - h2 Response - if method.response.doc - .doc= method.response.doc - if method.response.representations - ul - each val, key in method.response.representations - li #{val} - if method.response.example - h3 Example - pre.source-code= method.response.example + each val, key in method.request.representations + li #{val} + each param, paramName in method.request.params + .param + ul + li + .header + label + em #{paramName} + li + if param.description + .doc= param.description + ul + each val, key in param + if key != "description" && key != "doc" + li + strong #{key}:#{" "} + em #{val} + if method.request.example + h3 Example + pre.source-code= method.request.example + if method.response + h2 Response + if method.response.description + .doc= method.response.description + if method.response.representations + ul + each val, key in method.response.representations + li #{val} + if method.response.example + h3 Example + pre.source-code= method.response.example diff --git a/index.js b/index.js index bdaedeb..b838266 100644 --- a/index.js +++ b/index.js @@ -1,17 +1,37 @@ -var j2w = require('./json2wadl'), - jade = require('jade'), +var jade = require('jade'), fs = require('fs'), methods = require('./methods'), path = require('path'); var jadeTemplate = fs.readFileSync(path.join(__dirname, 'doc.jade'), 'utf8'); +var extendObject = function(target) { // copied from https://github.com/documentcloud/underscore + var i = 1, length = arguments.length, source; + for ( ; i < length; i++ ) { + // Only deal with defined values + if ( (source = arguments[i]) != undefined ) { + Object.getOwnPropertyNames(source).forEach(function(k){ + var d = Object.getOwnPropertyDescriptor(source, k) || {value:source[k]}; + if (d.get) { + target.__defineGetter__(k, d.get); + if (d.set) target.__defineSetter__(k, d.set); + } + else if (target !== d.value) { + target[k] = d.value; + } + }); + } + } + return target; + }; + + var DocRouter = function (connectRouter, baseUrl) { if (!connectRouter) throw new Error("Connect router function is missing."); if (!baseUrl) throw new Error("A base url is missing."); this.connectRouter = null; - this.methodJsons = []; + this.restdoc = { headers: {}, resources : [] }; this.baseUrl = baseUrl; this.wadl = null; @@ -24,15 +44,57 @@ var DocRouter = function (connectRouter, baseUrl) { self.connectRouter = connectRouter; } + function getResource(id) { + var i; + var resources = self.restdoc.resources; + + for (i=0; i - -// - -// - -// -// -// -// -// - -//var desc = { -// path: ':format/user/search/:username', -// method: 'GET', -// id: 'GetUsers', -// doc: 'Returns the users using their username.', -// params: { -// format: { -// style: 'template', -// type: 'string', -// required: true, -// defaultValue: 'json', -// options: { -// 'json': '', -// 'xml': '' -// } -// }, -// username: { -// style: 'query', -// type: 'string', -// required: true, -// defaultValue: 'chacon' -// } -// } -//} - -var builder = require('xmlbuilder'); -var sinatraPathRegex = /:(.*?)(\/|$)/g; - -var WadlHelper = { - sinatraPathToWADL: function (path) { - return path.replace(sinatraPathRegex, "{$1}$2"); - }, - toXsdType: function (type) { - return "xsd:" + type.toLowerCase(); - }, - createWADLDoc: function (baseUrl) { - var doc = builder.create(); - return doc.begin('application', { 'version': '1.0', 'encoding': 'UTF-8', 'standalone': true }) - .att("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance") - .att("xmlns:xsd", "http://www.w3.org/2001/XMLSchema") - .att("xmlns:apigee", "http://api.apigee.com/wadl/2010/07/") - .att("xmlns", "http://wadl.dev.java.net/2009/02") - .att("xsi:schemaLocation", "http://wadl.dev.java.net/2009/02 http://apigee.com/schemas/wadl-schema.xsd http://api.apigee.com/wadl/2010/07/ http://apigee.com/schemas/apigee-wadl-extensions.xsd") - .ele('resources') - .att('base', baseUrl); - }, - addParamsToElement: function (params, element) { - var param, - paramDesc, - option, - paramEle; - - for (param in params) { - paramDesc = params[param]; - paramEle = element.ele('param', { 'name': param }); - if (paramDesc.type) paramEle.att('type', this.toXsdType(paramDesc.type)); - if (paramDesc.style) paramEle.att('style', paramDesc.style); - if (paramDesc.required) paramEle.att('required', paramDesc.required); - if (paramDesc.defaultValue) paramEle.att('default', paramDesc.defaultValue); - if (paramDesc.doc) paramEle.ele('doc', paramDesc.doc); - if (paramDesc.options) { - for (option in paramDesc.options) { - paramEle.ele('option', { 'value': option }); - } - } - } - }, - addMethodToWADLDoc: function (methodJson, resoucesElement) { - var path = this.sinatraPathToWADL(methodJson.path), - resource = resoucesElement.ele('resource'), - representation, - representationEle, - exampleEle, - requestEle, - responseEle, - methodEle, - i, - l; - - resource.att('path', path); - - if (methodJson.params) { - this.addParamsToElement(methodJson.params, resource); - } - - methodEle = resource.ele('method'); - if (methodJson.id) methodEle.att('id', methodJson.id); - methodEle.att('name', methodJson.method); // required - if (methodJson.doc) methodEle.ele('doc', methodJson.doc); - - if (methodJson.request) { - requestEle = methodEle.ele('request'); - if (methodJson.request.doc) requestEle.ele('doc', methodJson.request.doc); - if (methodJson.request.params) { - this.addParamsToElement(methodJson.request.params, requestEle); - } - if (methodJson.request.representations) { - for(i = 0, l = methodJson.request.representations.length; i < l; i++) { - representation = methodJson.request.representations[i]; - representationEle = requestEle.ele('representation ').att('mediaType',representation ); - } - } - if (methodJson.request.example) { - exampleEle = requestEle.ele('doc', methodJson.request.example); - } - } - - if (methodJson.response) { - responseEle = methodEle.ele('response'); - if (methodJson.response.doc) responseEle.ele('doc', methodJson.response.doc); - if (methodJson.response.representations) { - for(i = 0, l = methodJson.response.representations.length; i < l; i++) { - representation = methodJson.response.representations[i]; - representationEle = responseEle.ele('representation ').att('mediaType',representation ); - } - } - if (methodJson.response.example) { - exampleEle = responseEle.ele('doc', methodJson.response.example); - } - } - - return resource.up(); - } -}; - -exports.toWadl = function (methodJsons, baseUrl, format) { - var resoucesElement = WadlHelper.createWADLDoc(baseUrl), - doc = resoucesElement.doc(), - methodJson, - i, - l; - - for (i = 0, l = methodJsons.length; i < l; i++) { - methodJson = methodJsons[i]; - WadlHelper.addMethodToWADLDoc(methodJson, resoucesElement); - } - - if (!format) { - format = { 'pretty': true, 'indent': ' ', 'newline': '\n' }; - } - - return doc.toString(format); -}; \ No newline at end of file diff --git a/tests/sample.js b/tests/sample.js new file mode 100644 index 0000000..1c2c337 --- /dev/null +++ b/tests/sample.js @@ -0,0 +1,43 @@ +var docRouter = require('../index').DocRouter; +var express = require('express'); + +var app = express.createServer(); + +docRouter(app, "http://mydomain.com"); + +var r = app.setResourceInfo({ + id: "App", + params: { + app: { + description: "the app entry id", + required: true + } + } +}); + +app.get('/:app', nope, + { + id: "App", + description: "Gets the app", + }); + +app.post('/:app', nope, + { + id: "App", + description: "Updates the app", + params: { + app: { + description: "the app entry to update", + required: true + } + } + }); + +app.listen(5000); + +console.log('started server on http://localhost:5000'); + +function nope(req, res) { + res.end(req.body); +} + diff --git a/tests/testDocRouter.js b/tests/testDocRouter.js index 88adbf8..f047550 100644 --- a/tests/testDocRouter.js +++ b/tests/testDocRouter.js @@ -4,55 +4,34 @@ var express = require('express'); var request = require('request'); function nope(req, res) { - res.end(); + res.end(req.body); } function mapRouter(app) { + app.setResourceInfo({ + id: "App", + params: { + app: { + style: "template", + type: "string", + required: true + } + } + }); + app.get('/:app', nope, { - id: "GetApp", - doc: "Gets the app", - params: { - app: { - style: "template", - type: "string", - required: true - } - } + id: "App", + description: "Gets the app" }); + app.post('/:app', nope, { - id: "UpdateApp", - doc: "Updates the app", - params: { - app: { - style: "template", - type: "string", - required: true - } - } + id: "App", + doc: "Updates the app" }); } -exports['test get wadl'] = function (test) { - var server = connect.createServer( - docRouter(connect.router, "boo", mapRouter) - ); - server.listen(5000); - - request('http://localhost:5000/!!', function (error, res) { - if (error) { - test.fail("Could not get waml"); - return; - } - - test.ok(~res.body.indexOf('')); - test.ok(~res.body.indexOf('')); - server.close(); - test.done(); - }); -}; - exports['test get json'] = function (test) { var server = connect.createServer( docRouter(connect.router, "boo", mapRouter) @@ -60,7 +39,7 @@ exports['test get json'] = function (test) { server.listen(5000); request({ - uri: 'http://localhost:5000/', + uri: 'http://localhost:5000/*', method: 'OPTIONS', headers: { accept: 'application/json'} }, @@ -70,8 +49,8 @@ exports['test get json'] = function (test) { return; } - var methodsJson = JSON.parse(res.body); - test.ok(methodsJson.length == 2); + var restdoc = JSON.parse(res.body); + test.ok(restdoc.resources.length == 1); server.close(); test.done(); }); @@ -84,7 +63,7 @@ exports['test get html'] = function (test) { server.listen(5000); request({ - uri: 'http://localhost:5000/', + uri: 'http://localhost:5000/*', method: 'OPTIONS', headers: { accept: 'text/html'} }, @@ -103,32 +82,32 @@ exports['test get html'] = function (test) { if (express) { - exports['test get wadl with express'] = function (test) { + exports['test get restdoc with express'] = function (test) { var app = express.createServer(); docRouter(app, "boo"); + app.setResourceInfo({ + id: "App", + params: { + app: { + required: true + } + } + }) + app.get('/:app', nope, { - id: "GetApp", - doc: "Gets the app", - params: { - app: { - style: "template", - type: "string", - required: true - } - } + id: "App", + description: "Gets the app", }); app.post('/:app', nope, { - id: "UpdateApp", - doc: "Updates the app", + id: "App", + description: "Updates the app", params: { app: { - style: "template", - type: "string", required: true } } @@ -138,12 +117,14 @@ if (express) { request('http://localhost:5000/!!', function (error, res) { if (error) { - test.fail("Could not get waml"); + test.fail("Could not get restdoc"); return; } - test.ok(~res.body.indexOf('')); - test.ok(~res.body.indexOf('')); + var restdoc = JSON.parse(res.body); + test.ok(restdoc.resources.length == 1); + test.ok(restdoc.resources[0].methods.GET); + test.ok(restdoc.resources[0].methods.POST); app.close(); test.done(); }); diff --git a/tests/testJson2Wadl.js b/tests/testJson2Wadl.js deleted file mode 100644 index a5e190f..0000000 --- a/tests/testJson2Wadl.js +++ /dev/null @@ -1,34 +0,0 @@ -var j2w = require('../json2wadl'); - -exports["test json 2 wadl"] = function (test) { - var desc = { - path: ':format/user/search/:username', - method: 'GET', - id: 'GetUsers', - doc: 'Returns the users using their username.', - params: { - format: { - style: 'template', - type: 'string', - required: true, - defaultValue: 'json', - options: { - 'xml': '', - 'json': '', - 'yaml': '' - } - }, - username: { - style: 'query', - type: 'string', - required: true, - defaultValue: 'chacon' - } - } - }; - - var wadl = j2w.toWadl([desc], "http://github.com/", { pretty: false }); - var originalWadl = ''; - test.equals(wadl, originalWadl); - test.done(); -}; \ No newline at end of file