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());
app.model(Product);
app.use('/explorer', explorer(app, {basePath: '/api'}));
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");
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
Many aspects of the explorer are configurable.
@ -32,7 +62,7 @@ See [options](#options) for a description of these options:
```js
// Mount middleware before calling `explorer()` to add custom headers, auth, etc.
app.use('/explorer', loopback.basicAuth('user', 'password'));
app.use('/explorer', explorer(app, {
explorer(app, {
basePath: '/custom-api-root',
uiDirs: [
path.resolve(__dirname, 'public'),
@ -60,6 +90,12 @@ Options are passed to `explorer(app, options)`.
> to a path different than '/api', e.g. with
> `loopback.use('/custom-api-root', loopback.rest());
`mountPath`: **String**
> Default: `/explorer`
> Set the path where to mount the explorer component.
`protocol`: **String**
> Default: `null`

View File

@ -6,35 +6,46 @@ var url = require('url');
var path = require('path');
var urlJoin = require('./lib/url-join');
var _defaults = require('lodash').defaults;
var express = require('express');
var swagger = require('./lib/swagger');
var SWAGGER_UI_ROOT = require('strong-swagger-ui').dist;
var STATIC_ROOT = path.join(__dirname, 'public');
module.exports = explorer;
explorer.routes = routes;
/**
* Example usage:
*
* var explorer = require('loopback-explorer');
* app.use('/explorer', explorer(app, options));
* explorer(app, 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, {
resourcePath: 'resources',
apiInfo: loopbackApplication.get('apiInfo') || {}
});
var app = express();
var router = new loopback.Router();
swagger(loopbackApplication, app, options);
app.disable('x-powered-by');
swagger(loopbackApplication, router, options);
// config.json is loaded by swagger-ui. The server should respond
// 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
// in case we're proxied at a deep path.
var source = url.parse(req.headers.referer || '').pathname;
@ -53,10 +64,10 @@ function explorer(loopbackApplication, options) {
// to worry about constantly pulling in JS updates.
if (options.uiDirs) {
if (typeof options.uiDirs === 'string') {
app.use(express.static(options.uiDirs));
router.use(loopback.static(options.uiDirs));
} else if (Array.isArray(options.uiDirs)) {
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) {
console.warn('loopback-explorer: `swaggerDistRoot` is deprecated,' +
' 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
app.use(express.static(STATIC_ROOT));
router.use(loopback.static(STATIC_ROOT));
// 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": {
"cors": "^2.7.1",
"debug": "^2.2.0",
"express": "4.x",
"lodash": "^3.10.0",
"strong-swagger-ui": "^20.0.2"
}

View File

@ -106,9 +106,9 @@ describe('explorer', function() {
var app;
beforeEach(function setupExplorerWithUiDirs() {
app = loopback();
app.use('/explorer', explorer(app, {
explorer(app, {
uiDirs: [ path.resolve(__dirname, 'fixtures', 'dummy-swagger-ui') ]
}));
});
});
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() {
var app;
beforeEach(function() {
@ -135,9 +156,9 @@ describe('explorer', function() {
});
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') ]
}));
});
request(app).get('/explorer/')
.expect(200)
@ -147,9 +168,9 @@ describe('explorer', function() {
});
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')
}));
});
request(app).get('/explorer/')
.expect(200)
@ -172,7 +193,7 @@ describe('explorer', function() {
Product.attachTo(loopback.memory());
app.model(Product);
app.use(explorerBase || '/explorer', explorer(app));
explorer(app, { mountPath: explorerBase });
app.use(app.get('restApiRoot') || '/', loopback.rest());
}
});

View File

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