Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions example/simple.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,10 @@ app.use('/explorer', explorer(app, {basePath: apiPath}));
app.use(apiPath, loopback.rest());
console.log('Explorer mounted at http://localhost:' + port + '/explorer');

var Catalog = loopback.PersistedModel.extend('catalog', {
name: {type: 'string', required: true}
});
Catalog.attachTo(loopback.memory());
app.model(Catalog);

app.listen(port);
28 changes: 23 additions & 5 deletions lib/swagger.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var classHelper = require('./class-helper');
var routeHelper = require('./route-helper');
var modelHelper = require('./model-helper');
var cors = require('cors');
var debug = require('debug')('loopback:explorer');

/**
* Create a remotable Swagger module for plugging into `RemoteObjects`.
Expand Down Expand Up @@ -58,18 +59,20 @@ function Swagger(loopbackApplication, swaggerApp, opts) {
var resourceDoc = generateResourceDoc(opts);
var apiDocs = {};

// A class is an endpoint root; e.g. /users, /products, and so on.
classes.forEach(function (aClass) {
function publish(aClass) {
var doc = apiDocs[aClass.name] = classHelper.generateAPIDoc(aClass, opts);
resourceDoc.apis.push(classHelper.generateResourceDocAPIEntry(aClass));

// Add the getter for this doc.
var docPath = urlJoin(opts.resourcePath, aClass.http.path);
addRoute(swaggerApp, docPath, doc, opts);
});
}

// A class is an endpoint root; e.g. /users, /products, and so on.
classes.forEach(publish);

// A route is an endpoint, such as /users/findOne.
routes.forEach(function(route) {
function addRouteToAPISpec(classes, route) {
// Get the API doc matching this class name.
var className = route.method.split('.')[0];
var doc = apiDocs[className];
Expand All @@ -83,7 +86,8 @@ function Swagger(loopbackApplication, swaggerApp, opts) {
})[0];

routeHelper.addRouteToAPIDeclaration(route, classDef, doc);
});
}
routes.forEach(addRouteToAPISpec.bind(null, classes));

// Add models referenced from routes (e.g. accepts/returns)
Object.keys(apiDocs).forEach(function(className) {
Expand Down Expand Up @@ -132,6 +136,20 @@ function Swagger(loopbackApplication, swaggerApp, opts) {
* information about them.
*/
addRoute(swaggerApp, opts.resourcePath, resourceDoc, opts);

loopbackApplication.on('modelRemoted', function(sharedClass) {
debug('Adding model %s to swagger', sharedClass.ctor.modelName);
// A hacky way to build a RemoteObjects that only contains the newly
// remoted shared class
var remotes = new (loopbackApplication.remotes().constructor)();
remotes.addClass(sharedClass);
var classes = remotes.classes();
// Get the REST adapter to retrive all routes
var adapter = remotes.handler('rest').adapter;
publish(sharedClass);
var routes = adapter.allRoutes();
routes.forEach(addRouteToAPISpec.bind(null, classes));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it should be easy to filter routes for a given class only - see the code in addRouteToAPISpec which is looking up the class name and shared class using the route metadata.

Once we have a function "return the sub-array of routes that are for the given class", this hack will be no longer necessary.

});
}

function setupCors(swaggerApp, remotes) {
Expand Down
23 changes: 22 additions & 1 deletion test/swagger.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ describe('swagger definition', function() {
var app = givenAppWithSwagger();

var getReq = getAPIDeclaration(app, 'products');
getReq.end(function(err, res) {
getReq.end(function (err, res) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: we use function(), not function (). please fix your IDE settings and revert this change.

if (err) return done(err);
var data = res.body.models.product;
expect(data.id).to.equal('product');
Expand Down Expand Up @@ -326,6 +326,27 @@ describe('swagger definition', function() {
});
});

describe('Allow model remoting after initiation', function () {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: we use function(), not function ().

it('should be able to access models remoted after initiation', function (done) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: we use function(), not function ().

var app = givenAppWithSwagger();

var Catalog = loopback.PersistedModel.extend('catalog', {
name: {type: 'string', required: true}
});
Catalog.attachTo(loopback.memory());
app.model(Catalog);

var getReq = getAPIDeclaration(app, 'catalogs');

getReq.end(function (err, res) {
if (err) return done(err);
var data = res.body.models.catalog;
expect(data.id).to.equal('catalog');
done();
});
});
});

function getSwaggerResources(app, restPath, classPath) {
return request(app)
.get(urlJoin(restPath || '/explorer', '/resources', classPath || ''))
Expand Down