diff --git a/example/simple.js b/example/simple.js index 70937cd..9ac1ccd 100644 --- a/example/simple.js +++ b/example/simple.js @@ -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); diff --git a/lib/swagger.js b/lib/swagger.js index 32843b7..9794300 100644 --- a/lib/swagger.js +++ b/lib/swagger.js @@ -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`. @@ -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]; @@ -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) { @@ -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)); + }); } function setupCors(swaggerApp, remotes) { diff --git a/test/swagger.test.js b/test/swagger.test.js index 9a79082..0ce8732 100644 --- a/test/swagger.test.js +++ b/test/swagger.test.js @@ -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) { if (err) return done(err); var data = res.body.models.product; expect(data.id).to.equal('product'); @@ -326,6 +326,27 @@ describe('swagger definition', function() { }); }); + describe('Allow model remoting after initiation', function () { + it('should be able to access models remoted after initiation', function (done) { + 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 || ''))