From 1e41216c15e33eeb8107e578f2df004410dffc33 Mon Sep 17 00:00:00 2001 From: Eric Martindale Date: Wed, 5 Aug 2015 19:50:07 -0700 Subject: [PATCH 1/5] Add new API documentation endpoint. --- lib/Service/http.js | 65 ++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/lib/Service/http.js b/lib/Service/http.js index 3a31217c..eae917a0 100644 --- a/lib/Service/http.js +++ b/lib/Service/http.js @@ -311,36 +311,53 @@ HTTP.prototype.attach = function( maki ) { }) } - maki.app.all('/', function(req, res, next) { - if ('OPTIONS' === req.method) { - var resourceList = Object.keys( maki.resources ).filter(function(r) { - var resource = maki.resources[ r ]; - return !resource.internal || !resource.static; - }).map(function(r) { - var resource = maki.resources[ r ]; - - var realPaths = {}; - Object.keys( resource.paths ).forEach(function( path ) { - realPaths[ path ] = resource.paths[ path ].toString(); - }); - resource.paths = realPaths; - - return resource; + function serveDocumentation(req, res, next) { + var resourceList = Object.keys( maki.resources ).filter(function(r) { + var resource = maki.resources[ r ]; + return !resource.internal || !resource.static; + }).map(function(r) { + var resource = maki.resources[ r ]; + + var realPaths = {}; + Object.keys( resource.paths ).forEach(function( path ) { + realPaths[ path ] = resource.paths[ path ].toString(); }); + resource.paths = realPaths; - return res.send({ - config: { - views: { - client: maki.config.views.client - } - }, - // TODO: fix circular JSON issue here - resources: resourceList - }); + return resource; + }); + + return res.format({ + html: function() { + res.render('endpoints', { + resources: resourceList + }) + }, + json: function() { + res.send({ + config: { + views: { + client: maki.config.views.client + } + }, + // TODO: fix circular JSON issue here + resources: resourceList + }); + } + }); + } + + maki.app.all('/', function(req, res, next) { + if ('OPTIONS' === req.method) { + return serveDocumentation(req, res, next); } next(); }); + maki.app.get('/api', function(req, res, next) { + return serveDocumentation(req, res, next); + }); + function filePopulatorFor(field) { return function(req, res, next) { var db = maki.datastore.mongoose.connections[0].db; From a8dd8ab363247a6f150822e552607a239d43844d Mon Sep 17 00:00:00 2001 From: Eric Martindale Date: Wed, 5 Aug 2015 19:52:28 -0700 Subject: [PATCH 2/5] Add missing endpoints.jade file. --- views/endpoints.jade | 74 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 views/endpoints.jade diff --git a/views/endpoints.jade b/views/endpoints.jade new file mode 100644 index 00000000..499b738e --- /dev/null +++ b/views/endpoints.jade @@ -0,0 +1,74 @@ +extends layouts/default + +block content + h1 #{config.service.name} API + + h3 Generally + p All endpoints serve both JSON and HTML, controlled by the `Accept` header of your request. Furthermore, any endpoint can be connected to with a WebSocket, and updates to that resource will be served to you as messages. + p All endpoints can also be interacted with via the following HTTP verbs: + pre + code. + query, get: GET + create: POST, PUT + update: PATCH + delete: DELETE + + - var methodMap = { query: 'GET', get: 'GET', create: 'POST', update: 'PATCH', destroy: 'DELETE' } + + .ui.stackable.grid + .four.wide.column + .ui.vertical.fluid.menu + for resource in resources + a.item(href="/api##{resource.plural.toLowerCase()}") + if (resource.options.icon) + i.icon(class="#{resource.options.icon}") + | #{resource.plural} + .twelve.wide.column + .ui.cards + for resource in resources + .ui.fluid.card + .content + .header + if (resource.options.icon) + i.icon(class="#{resource.options.icon}") + a(href="##{resource.plural.toLowerCase()}", name="#{resource.plural.toLowerCase()}") #{resource.plural} + .description #{resource.description} + .description + h4 Paths + table.ui.table + thead + tr + th Operation + th HTTP Method + th Route + tbody + for method in Object.keys(resource.routes) + tr + td #{method} + td #{methodMap[method]} + td #{resource.routes[method]} + + h3 Attributes + table.ui.table + thead + tr + th Name + th Type + th Required? + th Allowed Values + th Default + th Min + th Max + th Validator + tbody + for name in Object.keys(resource.attributes) + - var attribute = resource.attributes[name] + tr + td #{name} + td #{(attribute.type && attribute.type.name) ? attribute.type.name : attribute.type} + td #{attribute.required} + td #{attribute.enum} + td #{attribute.default} + td #{attribute.min} + td #{attribute.max} + td #{attribute.validator} From 33c266894aa9e7aef3a5c8b015e2d376da6a0b8d Mon Sep 17 00:00:00 2001 From: Eric Martindale Date: Wed, 5 Aug 2015 20:27:26 -0700 Subject: [PATCH 3/5] Allow format to be forced. --- lib/Service/http.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Service/http.js b/lib/Service/http.js index eae917a0..d9c94598 100644 --- a/lib/Service/http.js +++ b/lib/Service/http.js @@ -212,7 +212,12 @@ HTTP.prototype.provide = function() { } }); - res.format( formatters ); + if (req.param('format') === 'json') { + formatters.json(); + } else { + res.format( formatters ); + } + }; From b2c6bdacf274f0f1c7a409733bf31208c4981b67 Mon Sep 17 00:00:00 2001 From: Eric Martindale Date: Wed, 5 Aug 2015 20:31:32 -0700 Subject: [PATCH 4/5] Update API docs. --- views/endpoints.jade | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/views/endpoints.jade b/views/endpoints.jade index 499b738e..7e826966 100644 --- a/views/endpoints.jade +++ b/views/endpoints.jade @@ -4,7 +4,8 @@ block content h1 #{config.service.name} API h3 Generally - p All endpoints serve both JSON and HTML, controlled by the `Accept` header of your request. Furthermore, any endpoint can be connected to with a WebSocket, and updates to that resource will be served to you as messages. + p All endpoints serve both JSON and HTML, controlled by the Accept header of your request. You can force the formatting of any resource using the format parameter: i.e., append ?format=json to any URL to retrieve the underlying resource as JSON. + p Events: Any endpoint can be connected to with a WebSocket, and updates to that resource will be served to you as messages. These messages will be formatted as JSON-PATCH arrays, wrapped in JSON-RPC with the method name patch. The channel parameter will correspond to the resource's route. For more information, see the Maki documentation on PubSub & Messaging. p All endpoints can also be interacted with via the following HTTP verbs: pre code. From 0148b5012bb933366f3d3b7c69910e56b2ba5dbe Mon Sep 17 00:00:00 2001 From: Eric Martindale Date: Wed, 5 Aug 2015 20:32:21 -0700 Subject: [PATCH 5/5] Add labels to each general section. --- views/endpoints.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/endpoints.jade b/views/endpoints.jade index 7e826966..207f7668 100644 --- a/views/endpoints.jade +++ b/views/endpoints.jade @@ -4,7 +4,7 @@ block content h1 #{config.service.name} API h3 Generally - p All endpoints serve both JSON and HTML, controlled by the Accept header of your request. You can force the formatting of any resource using the format parameter: i.e., append ?format=json to any URL to retrieve the underlying resource as JSON. + p Formatting: All endpoints serve both JSON and HTML, controlled by the Accept header of your request. You can force the formatting of any resource using the format parameter: i.e., append ?format=json to any URL to retrieve the underlying resource as JSON. p Events: Any endpoint can be connected to with a WebSocket, and updates to that resource will be served to you as messages. These messages will be formatted as JSON-PATCH arrays, wrapped in JSON-RPC with the method name patch. The channel parameter will correspond to the resource's route. For more information, see the Maki documentation on PubSub & Messaging. p All endpoints can also be interacted with via the following HTTP verbs: pre