Merge pull request #104 from strongloop/feature/refactor-as-component

[SEMVER-MAJOR] Rework the module to a loopback component
This commit is contained in:
Miroslav Bajtoš 2015-08-10 16:54:38 +02:00
commit 75ea3f928a
5 changed files with 91 additions and 25 deletions

View File

@ -16,13 +16,43 @@ var Product = loopback.Model.extend('product');
Product.attachTo(loopback.memory()); Product.attachTo(loopback.memory());
app.model(Product); app.model(Product);
app.use('/explorer', explorer(app, {basePath: '/api'}));
app.use('/api', loopback.rest()); app.use('/api', loopback.rest());
// Register explorer using component-centric API:
explorer(app, { basePath: '/api', mountPath: '/explorer' });
// Alternatively, register as a middleware:
app.use('/explorer', explorer.routes(app, { basePath: '/api' }));
console.log("Explorer mounted at localhost:" + port + "/explorer"); console.log("Explorer mounted at localhost:" + port + "/explorer");
app.listen(port); app.listen(port);
``` ```
## Upgrading from v1.x
To upgrade your application using loopback-explorer version 1.x, just replace
`explorer()` with `explorer.routes()` in your server script:
```js
var explorer = require('loopback-explorer');
// v1.x - does not work anymore
app.use('/explorer', explorer(app, options);
// v2.x
app.use('/explorer', explorer.routes(app, options));
```
In applications scaffolded by `slc loopback`, the idiomatic way is to register
loopback-explorer via `component-config.json`:
```json
{
"loopback-explorer": {
"mountPath": "/explorer"
}
}
```
## Advanced Usage ## Advanced Usage
Many aspects of the explorer are configurable. Many aspects of the explorer are configurable.
@ -32,7 +62,7 @@ See [options](#options) for a description of these options:
```js ```js
// Mount middleware before calling `explorer()` to add custom headers, auth, etc. // Mount middleware before calling `explorer()` to add custom headers, auth, etc.
app.use('/explorer', loopback.basicAuth('user', 'password')); app.use('/explorer', loopback.basicAuth('user', 'password'));
app.use('/explorer', explorer(app, { explorer(app, {
basePath: '/custom-api-root', basePath: '/custom-api-root',
uiDirs: [ uiDirs: [
path.resolve(__dirname, 'public'), path.resolve(__dirname, 'public'),
@ -60,6 +90,12 @@ Options are passed to `explorer(app, options)`.
> to a path different than '/api', e.g. with > to a path different than '/api', e.g. with
> `loopback.use('/custom-api-root', loopback.rest()); > `loopback.use('/custom-api-root', loopback.rest());
`mountPath`: **String**
> Default: `/explorer`
> Set the path where to mount the explorer component.
`protocol`: **String** `protocol`: **String**
> Default: `null` > Default: `null`

View File

@ -6,35 +6,46 @@ var url = require('url');
var path = require('path'); var path = require('path');
var urlJoin = require('./lib/url-join'); var urlJoin = require('./lib/url-join');
var _defaults = require('lodash').defaults; var _defaults = require('lodash').defaults;
var express = require('express');
var swagger = require('./lib/swagger'); var swagger = require('./lib/swagger');
var SWAGGER_UI_ROOT = require('strong-swagger-ui').dist; var SWAGGER_UI_ROOT = require('strong-swagger-ui').dist;
var STATIC_ROOT = path.join(__dirname, 'public'); var STATIC_ROOT = path.join(__dirname, 'public');
module.exports = explorer; module.exports = explorer;
explorer.routes = routes;
/** /**
* Example usage: * Example usage:
* *
* var explorer = require('loopback-explorer'); * var explorer = require('loopback-explorer');
* app.use('/explorer', explorer(app, options)); * explorer(app, options);
*/ */
function explorer(loopbackApplication, options) { function explorer(loopbackApplication, options) {
var mountPath = options.mountPath || '/explorer';
loopbackApplication.use(mountPath, routes(loopbackApplication, options));
}
function routes(loopbackApplication, options) {
var loopback = loopbackApplication.loopback;
var loopbackMajor = loopback && loopback.version &&
loopback.version.split('.')[0] || 1;
if (loopbackMajor < 2) {
throw new Error('loopback-explorer requires loopback 2.0 or newer');
}
options = _defaults({}, options, { options = _defaults({}, options, {
resourcePath: 'resources', resourcePath: 'resources',
apiInfo: loopbackApplication.get('apiInfo') || {} apiInfo: loopbackApplication.get('apiInfo') || {}
}); });
var app = express(); var router = new loopback.Router();
swagger(loopbackApplication, app, options); swagger(loopbackApplication, router, options);
app.disable('x-powered-by');
// config.json is loaded by swagger-ui. The server should respond // config.json is loaded by swagger-ui. The server should respond
// with the relative URI of the resource doc. // with the relative URI of the resource doc.
app.get('/config.json', function(req, res) { router.get('/config.json', function(req, res) {
// Get the path we're mounted at. It's best to get this from the referer // Get the path we're mounted at. It's best to get this from the referer
// in case we're proxied at a deep path. // in case we're proxied at a deep path.
var source = url.parse(req.headers.referer || '').pathname; var source = url.parse(req.headers.referer || '').pathname;
@ -53,10 +64,10 @@ function explorer(loopbackApplication, options) {
// to worry about constantly pulling in JS updates. // to worry about constantly pulling in JS updates.
if (options.uiDirs) { if (options.uiDirs) {
if (typeof options.uiDirs === 'string') { if (typeof options.uiDirs === 'string') {
app.use(express.static(options.uiDirs)); router.use(loopback.static(options.uiDirs));
} else if (Array.isArray(options.uiDirs)) { } else if (Array.isArray(options.uiDirs)) {
options.uiDirs.forEach(function(dir) { options.uiDirs.forEach(function(dir) {
app.use(express.static(dir)); router.use(loopback.static(dir));
}); });
} }
} }
@ -64,14 +75,14 @@ function explorer(loopbackApplication, options) {
if (options.swaggerDistRoot) { if (options.swaggerDistRoot) {
console.warn('loopback-explorer: `swaggerDistRoot` is deprecated,' + console.warn('loopback-explorer: `swaggerDistRoot` is deprecated,' +
' use `uiDirs` instead'); ' use `uiDirs` instead');
app.use(express.static(options.swaggerDistRoot)); router.use(loopback.static(options.swaggerDistRoot));
} }
// File in node_modules are overridden by a few customizations // File in node_modules are overridden by a few customizations
app.use(express.static(STATIC_ROOT)); router.use(loopback.static(STATIC_ROOT));
// Swagger UI distribution // Swagger UI distribution
app.use(express.static(SWAGGER_UI_ROOT)); router.use(loopback.static(SWAGGER_UI_ROOT));
return app; return router;
} }

View File

@ -33,7 +33,6 @@
"dependencies": { "dependencies": {
"cors": "^2.7.1", "cors": "^2.7.1",
"debug": "^2.2.0", "debug": "^2.2.0",
"express": "4.x",
"lodash": "^3.10.0", "lodash": "^3.10.0",
"strong-swagger-ui": "^20.0.2" "strong-swagger-ui": "^20.0.2"
} }

View File

@ -106,9 +106,9 @@ describe('explorer', function() {
var app; var app;
beforeEach(function setupExplorerWithUiDirs() { beforeEach(function setupExplorerWithUiDirs() {
app = loopback(); app = loopback();
app.use('/explorer', explorer(app, { explorer(app, {
uiDirs: [ path.resolve(__dirname, 'fixtures', 'dummy-swagger-ui') ] uiDirs: [ path.resolve(__dirname, 'fixtures', 'dummy-swagger-ui') ]
})); });
}); });
it('overrides swagger-ui files', function(done) { it('overrides swagger-ui files', function(done) {
@ -128,6 +128,27 @@ describe('explorer', function() {
}); });
}); });
describe('explorer.routes API', function() {
var app;
beforeEach(function() {
app = loopback();
var Product = loopback.PersistedModel.extend('product');
Product.attachTo(loopback.memory());
app.model(Product);
});
it('creates explorer routes', function(done) {
app.use('/explorer', explorer.routes(app));
app.use(app.get('restApiRoot') || '/', loopback.rest());
request(app)
.get('/explorer/config.json')
.expect('Content-Type', /json/)
.expect(200)
.end(done);
});
});
describe('when specifying custom static file root directories', function() { describe('when specifying custom static file root directories', function() {
var app; var app;
beforeEach(function() { beforeEach(function() {
@ -135,9 +156,9 @@ describe('explorer', function() {
}); });
it('should allow `uiDirs` to be defined as an Array', function(done) { it('should allow `uiDirs` to be defined as an Array', function(done) {
app.use('/explorer', explorer(app, { explorer(app, {
uiDirs: [ path.resolve(__dirname, 'fixtures', 'dummy-swagger-ui') ] uiDirs: [ path.resolve(__dirname, 'fixtures', 'dummy-swagger-ui') ]
})); });
request(app).get('/explorer/') request(app).get('/explorer/')
.expect(200) .expect(200)
@ -147,9 +168,9 @@ describe('explorer', function() {
}); });
it('should allow `uiDirs` to be defined as an String', function(done) { it('should allow `uiDirs` to be defined as an String', function(done) {
app.use('/explorer', explorer(app, { explorer(app, {
uiDirs: path.resolve(__dirname, 'fixtures', 'dummy-swagger-ui') uiDirs: path.resolve(__dirname, 'fixtures', 'dummy-swagger-ui')
})); });
request(app).get('/explorer/') request(app).get('/explorer/')
.expect(200) .expect(200)
@ -172,7 +193,7 @@ describe('explorer', function() {
Product.attachTo(loopback.memory()); Product.attachTo(loopback.memory());
app.model(Product); app.model(Product);
app.use(explorerBase || '/explorer', explorer(app)); explorer(app, { mountPath: explorerBase });
app.use(app.get('restApiRoot') || '/', loopback.rest()); app.use(app.get('restApiRoot') || '/', loopback.rest());
} }
}); });

View File

@ -3,7 +3,6 @@
var url = require('url'); var url = require('url');
var urlJoin = require('../lib/url-join'); var urlJoin = require('../lib/url-join');
var loopback = require('loopback'); var loopback = require('loopback');
var express = require('express');
var swagger = require('../lib/swagger'); var swagger = require('../lib/swagger');
var request = require('supertest'); var request = require('supertest');
@ -387,7 +386,7 @@ describe('swagger definition', function() {
} }
function mountExplorer(app, options) { function mountExplorer(app, options) {
var swaggerApp = express(); var swaggerApp = loopback();
swagger(app, swaggerApp, options); swagger(app, swaggerApp, options);
app.use(app.get('explorerRoot') || '/explorer', swaggerApp); app.use(app.get('explorerRoot') || '/explorer', swaggerApp);
return app; return app;