From 6c4e780ab83a05e03d6419050b1135b1106d60bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 7 Jan 2014 16:13:34 +0100 Subject: [PATCH 1/3] Replace strong-remoting ext/swagger with app.docs Use `app.docs()` to expose Swagger specs. This way we don't have to depend on loopback's dependency strong-remoting. --- index.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 5965e89..eb0ba40 100644 --- a/index.js +++ b/index.js @@ -3,7 +3,6 @@ */ var path = require('path'); var loopback = require('loopback'); -var swagger = requireLoopbackDependency('strong-remoting/ext/swagger'); var express = requireLoopbackDependency('express'); var STATIC_ROOT = path.join(__dirname, 'public'); @@ -13,13 +12,13 @@ module.exports = explorer; * Example usage: * * var explorer = require('loopback-explorer'); - * app.use('/explorer', explorer(app)); + * app.use('/explorer', explorer(app, options)); */ function explorer(loopbackApplication, options) { var options = options || {}; - var remotes = loopbackApplication.remotes(); - swagger(remotes, options); + + loopbackApplication.docs(options); var app = express(); app.get('/config.json', function(req, res) { From 8b6fc55de8eceb4d7880d133aea2b2353d641ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 7 Jan 2014 16:56:58 +0100 Subject: [PATCH 2/3] Use `app.get('restApiRoot')` as default basePath --- index.js | 4 +++- test/explorer.test.js | 55 ++++++++++++++++++++++++++++++------------- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/index.js b/index.js index eb0ba40..a2b012e 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ * Adds dynamically-updated docs as /explorer */ var path = require('path'); +var extend = require('util')._extend; var loopback = require('loopback'); var express = requireLoopbackDependency('express'); var STATIC_ROOT = path.join(__dirname, 'public'); @@ -16,7 +17,8 @@ module.exports = explorer; */ function explorer(loopbackApplication, options) { - var options = options || {}; + options = extend({}, options); + options.basePath = options.basePath || loopbackApplication.get('restApiRoot'); loopbackApplication.docs(options); diff --git a/test/explorer.test.js b/test/explorer.test.js index a208544..6d839d9 100644 --- a/test/explorer.test.js +++ b/test/explorer.test.js @@ -41,7 +41,7 @@ describe('explorer', function() { done(); }); }); - }) + }); describe('with custom baseUrl', function() { beforeEach(givenLoopBackAppWithExplorer('/api')); @@ -60,25 +60,48 @@ describe('explorer', function() { }); }); + describe('with custom app.restApiRoot', function() { + it('should serve correct swagger-ui config', function(done) { + var app = loopback(); + app.set('restApiRoot', '/rest-api-root'); + configureRestApiAndExplorer(app); + + request(app) + .get('/explorer/config.json') + .expect(200) + .end(function(err, res) { + if (err) return done(err); + expect(res.body).to + .have.property('discoveryUrl', '/rest-api-root/swagger/resources'); + done(); + }); + }); + }); + function givenLoopBackAppWithExplorer(restUrlBase) { return function(done) { var app = this.app = loopback(); - var Product = loopback.Model.extend('product'); - Product.attachTo(loopback.memory()); - app.model(Product); - - if (restUrlBase) { - app.use(restUrlBase, loopback.rest()); - app.use('/explorer', explorer(app, { basePath: restUrlBase })); - } else { - // LoopBack REST adapter owns the whole URL space and does not - // let other middleware handle same URLs. - // It's possible to circumvent this measure by installing - // the explorer middleware before the REST middleware. - app.use('/explorer', explorer(app)); - app.use(loopback.rest()); - } + configureRestApiAndExplorer(app, restUrlBase); done(); + }; + } + + function configureRestApiAndExplorer(app, restUrlBase) { + var Product = loopback.Model.extend('product'); + Product.attachTo(loopback.memory()); + app.model(Product); + + if (restUrlBase) { + app.use(restUrlBase, loopback.rest()); + app.use('/explorer', explorer(app, { basePath: restUrlBase })); + } else { + // LoopBack REST adapter owns the whole URL space and does not + // let other middleware handle same URLs. + // It's possible to circumvent this measure by installing + // the explorer middleware before the REST middleware. + // This way we can acess `/explorer` even when REST is mounted at `/` + app.use('/explorer', explorer(app)); + app.use(app.get('restApiRoot') || '/', loopback.rest()); } } }); From 8a471a8fec9acce9d2880f289f9c90daffaeec7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 7 Jan 2014 17:16:08 +0100 Subject: [PATCH 3/3] Report explorer URL when application starts Listen on app's "start" event and report the URL where the user can access the explorer GUI. --- index.js | 10 ++++++++++ test/explorer.test.js | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/index.js b/index.js index a2b012e..b5c4589 100644 --- a/index.js +++ b/index.js @@ -29,6 +29,16 @@ function explorer(loopbackApplication, options) { }); }); app.use(loopback.static(STATIC_ROOT)); + + loopbackApplication.once('start', function() { + var baseUrl = 'http://' + this.get('host') + ':' + this.get('port'); + // `app.route` is filled by `expressApp.use(route, app)`, i.e. + // loopbackApplication.use('/explorer', explorer(loopbackApplication)) + // sets `app.route = '/explorer'` + var explorerPath = app.route; + console.log('Browse your REST API at %s%s', baseUrl, explorerPath); + }); + return app; } diff --git a/test/explorer.test.js b/test/explorer.test.js index 6d839d9..e88dadb 100644 --- a/test/explorer.test.js +++ b/test/explorer.test.js @@ -1,3 +1,4 @@ +var format = require('util').format; var loopback = require('loopback'); var explorer = require('../'); var request = require('supertest'); @@ -78,6 +79,27 @@ describe('explorer', function() { }); }); + it('reports correct explorer URL on app start', function() { + var messages = []; + console._log = console.log; + console.log = function() { + messages.push(format.apply(null, Array.prototype.slice.call(arguments))); + }; + + var app = loopback(); + app.set('host', 'custom-host'); + app.set('port', 12345); + app.use('/custom-api', loopback.rest()); + app.use('/custom-explorer', explorer(app, { basePath: '/custom-root' })); + app.emit('start'); + + console.log = console._log; + delete console._log; + + expect(messages).to.have.length(1); + expect(messages[0]).to.contain('http://custom-host:12345/custom-explorer'); + }); + function givenLoopBackAppWithExplorer(restUrlBase) { return function(done) { var app = this.app = loopback();