Merge remote-tracking branch 'upstream/master'

* upstream/master:
  Bump version
  Set up default consumes/produces media types
  Fix the default opts
  Add required swagger 1.2 items property for property type array
  Allow passing a custom protocol.

Conflicts:
	lib/swagger.js
This commit is contained in:
Shelby Sanders 2014-08-04 18:20:59 -07:00
commit 28d293b6f9
7 changed files with 50 additions and 16 deletions

View File

@ -57,6 +57,16 @@ 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());
`protocol`: **String**
> Default: `null`
> A hard override for the outgoing protocol (`http` or `https`) that is designated in Swagger
> resource documents. By default, `loopback-explorer` will write the protocol that was used to retrieve
> the doc. This option is useful if, for instance, your API sits behind an SSL terminator
> and thus needs to report its endpoints as `https`, even though incoming traffic is auto-detected
> as `http`.
`swaggerDistRoot`: **String** `swaggerDistRoot`: **String**
> Sets a path within your application for overriding Swagger UI files. > Sets a path within your application for overriding Swagger UI files.

View File

@ -28,6 +28,8 @@ var classHelper = module.exports = {
basePath: opts.basePath, basePath: opts.basePath,
resourcePath: urlJoin('/', opts.resourcePath), resourcePath: urlJoin('/', opts.resourcePath),
apis: [], apis: [],
consumes: aClass.http.consumes || opts.consumes,
produces: aClass.http.produces || opts.produces,
models: modelHelper.generateModelDefinition(aClass.ctor, {}) models: modelHelper.generateModelDefinition(aClass.ctor, {})
}; };
}, },

View File

@ -104,12 +104,18 @@ var modelHelper = module.exports = {
out.type = modelHelper.getPropType(out.type); out.type = modelHelper.getPropType(out.type);
if (out.type === 'array') { if (out.type === 'array') {
var hasItemType = typeof prop.type !== 'string' && prop.type[0]; var hasItemType = Array.isArray(prop.type) && prop.type.length;
var arrayItem = hasItemType && prop.type[0];
if (hasItemType) { if (arrayItem) {
var arrayProp = prop.type[0]; if(typeof arrayItem === 'object') {
if (!arrayProp.type) arrayProp = {type: arrayProp}; out.items = modelHelper.LDLPropToSwaggerDataType(arrayItem);
out.items = modelHelper.LDLPropToSwaggerDataType(arrayProp); } else {
out.items = { type: modelHelper.getPropType(arrayItem) };
}
} else {
// NOTE: `any` is not a supported type in swagger 1.2
out.items = { type: 'any' };
} }
} else if (out.type === 'date') { } else if (out.type === 'date') {
out.type = 'string'; out.type = 'string';

View File

@ -7,12 +7,10 @@ module.exports = Swagger;
/** /**
* Module dependencies. * Module dependencies.
*/ */
var debug = require('debug')('loopback:explorer:swagger');
var path = require('path'); var path = require('path');
var urlJoin = require('./url-join'); var urlJoin = require('./url-join');
var _defaults = require('lodash.defaults'); var _defaults = require('lodash.defaults');
var classHelper = require('./class-helper'); var classHelper = require('./class-helper');
var modelHelper = require('./model-helper');
var routeHelper = require('./route-helper'); var routeHelper = require('./route-helper');
/** /**
@ -24,10 +22,13 @@ var routeHelper = require('./route-helper');
* @param {Object} opts Options. * @param {Object} opts Options.
*/ */
function Swagger(loopbackApplication, swaggerApp, opts) { function Swagger(loopbackApplication, swaggerApp, opts) {
opts = _defaults({}, opts, { opts = _defaults(opts || {}, {
swaggerVersion: '1.2', swaggerVersion: '1.2',
basePath: loopbackApplication.get('restApiRoot') || '/api', basePath: loopbackApplication.get('restApiRoot') || '/api',
resourcePath: 'resources', resourcePath: 'resources',
// Default consumes/produces to application/json
consumes: ['application/json'],
produces: ['application/json'],
version: getVersion() version: getVersion()
}); });
@ -48,7 +49,7 @@ function Swagger(loopbackApplication, swaggerApp, opts) {
// Add the getter for this doc. // Add the getter for this doc.
var docPath = urlJoin(opts.resourcePath, aClass.http.path); var docPath = urlJoin(opts.resourcePath, aClass.http.path);
addRoute(swaggerApp, docPath, doc); addRoute(swaggerApp, docPath, doc, opts);
}); });
// A route is an endpoint, such as /users/findOne. // A route is an endpoint, such as /users/findOne.
@ -73,7 +74,7 @@ function Swagger(loopbackApplication, swaggerApp, opts) {
* resources available on the system, and where to find more * resources available on the system, and where to find more
* information about them. * information about them.
*/ */
addRoute(swaggerApp, opts.resourcePath, resourceDoc); addRoute(swaggerApp, opts.resourcePath, resourceDoc, opts);
} }
/** /**
@ -82,7 +83,7 @@ function Swagger(loopbackApplication, swaggerApp, opts) {
* @param {String} uri Path from which to serve the doc. * @param {String} uri Path from which to serve the doc.
* @param {Object} doc Doc to serve. * @param {Object} doc Doc to serve.
*/ */
function addRoute(app, uri, doc) { function addRoute(app, uri, doc, opts) {
var hasBasePath = Object.keys(doc).indexOf('basePath') !== -1; var hasBasePath = Object.keys(doc).indexOf('basePath') !== -1;
var initialPath = doc.basePath || ''; var initialPath = doc.basePath || '';
@ -100,7 +101,7 @@ function addRoute(app, uri, doc) {
if (hasBasePath) { if (hasBasePath) {
var headers = req.headers; var headers = req.headers;
var host = headers.Host || headers.host; var host = headers.Host || headers.host;
var protocol = headers['x-forwarded-proto'] || headers['X-Forwarded-Proto'] || ctx.req.protocol var protocol = headers['x-forwarded-proto'] || headers['X-Forwarded-Proto'] || opts.protocol || req.protocol
doc.basePath = protocol + '://' + host + initialPath; doc.basePath = protocol + '://' + host + initialPath;
} }
res.status(200).send(doc); res.status(200).send(doc);

View File

@ -1,6 +1,6 @@
{ {
"name": "loopback-explorer", "name": "loopback-explorer",
"version": "1.2.6", "version": "1.2.7",
"description": "Browse and test your LoopBack app's APIs", "description": "Browse and test your LoopBack app's APIs",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -86,7 +86,10 @@ describe('model-helper', function() {
array: [] array: []
}); });
var prop = def.properties.array; var prop = def.properties.array;
expect(prop).to.eql({ type: 'array' }); expect(prop).to.eql({
type: 'array',
items: { type: 'any' }
});
}); });
it('converts [undefined] type', function() { it('converts [undefined] type', function() {
@ -96,7 +99,7 @@ describe('model-helper', function() {
array: [undefined] array: [undefined]
}); });
var prop = def.properties.array; var prop = def.properties.array;
expect(prop).to.eql({ type: 'array' }); expect(prop).to.eql({ type: 'array', items: { type: 'any' } });
}); });
it('converts "array" type', function() { it('converts "array" type', function() {
@ -104,7 +107,7 @@ describe('model-helper', function() {
array: 'array' array: 'array'
}); });
var prop = def.properties.array; var prop = def.properties.array;
expect(prop).to.eql({ type: 'array' }); expect(prop).to.eql({ type: 'array', items: { type: 'any' } });
}); });
}); });
}); });

View File

@ -69,6 +69,18 @@ describe('swagger definition', function() {
done(); done();
}); });
}); });
it('respects a hardcoded protocol (behind SSL terminator)', function(done){
var app = mountSwagger({protocol: 'https'});
var getReq = getAPIDeclaration(app, 'products');
getReq.end(function(err, res) {
if (err) return done(err);
var parsed = url.parse(res.body.basePath);
expect(parsed.protocol).to.equal('https:');
done();
});
});
}); });
describe('Model definition attributes', function() { describe('Model definition attributes', function() {