Skip to content

Commit

Permalink
update docRouter to support restdoc initial draft
Browse files Browse the repository at this point in the history
  • Loading branch information
saary committed May 3, 2012
1 parent 158b47e commit f5a4c9b
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 348 deletions.
5 changes: 2 additions & 3 deletions README.md
@@ -1,18 +1,17 @@
# 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.

## Retrieving the documentation ##
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
Expand Down
117 changes: 70 additions & 47 deletions doc.jade
Expand Up @@ -85,66 +85,89 @@ 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
.header
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

117 changes: 91 additions & 26 deletions 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;

Expand All @@ -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<resources.length; i++) {
if (resources[i].id === id) {
return resources[i];
}
}

return null;
}

this.connectRouter.get("/!!", function (req, res) {
getWadl(req, res);
getDescription(req, res);
});

this.connectRouter.options(/\/?[*]/g, function (req, res) {
getDescription(req, res);
});

this.connectRouter.options("/:resource", function (req, res) {
var resource = getResource(req.params.resource) || {};

this.connectRouter.options("/", function (req, res) {
getWadl(req, res);
res.writeHead(200, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify(resource));
});

this.connectRouter.setResourceInfo = function(resource) {
if (!resource) {
throw new Error("resource must be provided");
}
if (!resource.id) {
throw new Error("resource must include an id");
}

var existingResource = getResource(resource.id);

if (!existingResource) {
existingResource = { id: resource.id, methods: {} };
self.restdoc.resources.push(existingResource);
}

return extendObject(existingResource, resource);
}

this.connectRouter.setHeadersInfo = function(headers) {
headers = headers || {};
return extendObject(self.restdoc.headers, headers);
}

var method,
i,
l;
Expand All @@ -45,15 +107,30 @@ var DocRouter = function (connectRouter, baseUrl) {
var args = arguments;
var handlersCount = args.length;
// Multiple arguments may exist for multiple middlewares. The Json describing the method is the last argument.
methodJson = {};
var methodJson = {};
if (typeof(args[args.length - 1]) === 'object'){
methodJson = args[args.length - 1];
handlersCount -= 1;
}
methodJson.method = method.toUpperCase();
method = method.toUpperCase();
methodJson.path = route;

self.methodJsons.push(methodJson);
methodJson.description = methodJson.description || methodJson.doc;

// add backward support for style field (query/template)
if (method.params) {
var param;
for (param in method.params) {
if (!param.style) {
param.style = ~route.indexOf('{?' + param + '}') ? "query" : "template";
}
}
}

var id = methodJson.id || methodJson.path;
var resource = getResource(id) || { id: id, methods: {} };

resource.methods[method] = methodJson;

// call the original router with the original arguments

Expand All @@ -63,15 +140,8 @@ var DocRouter = function (connectRouter, baseUrl) {
}(method, self.connectRouter[method]));
}

function getWadl(req, res) {
function getDescription(req, res) {
var htmlTemplate;
if (req.headers.accept &&
(~req.headers.accept.indexOf('application/json') || ~req.headers.accept.indexOf('text/json')))
{
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(self.methodJsons));
return;
}

if (req.headers.accept &&
(~req.headers.accept.indexOf('text/html') || ~req.headers.accept.indexOf('text/plain')))
Expand All @@ -80,21 +150,16 @@ var DocRouter = function (connectRouter, baseUrl) {
if (!self.html) {
try {
htmlTemplate = jade.compile(jadeTemplate);
self.html = htmlTemplate({methodJsons: self.methodJsons, baseUrl: self.baseUrl});
self.html = htmlTemplate({restdoc: self.restdoc, baseUrl: self.baseUrl});
} catch(e) {
console.error(e);
}
}
res.end(self.html);
return;
}

if (!self.wadl) {
self.wadl = j2w.toWadl(self.methodJsons, self.baseUrl, { pretty: true });
return res.end(self.html);
}

res.writeHead(200, { 'Content-Type': 'application/xml' });
res.end(self.wadl);
res.writeHead(200, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify(self.restdoc));
}
};

Expand Down

0 comments on commit f5a4c9b

Please sign in to comment.