2014-07-05 19:32:00 +00:00
|
|
|
'use strict';
|
2014-07-04 22:09:03 +00:00
|
|
|
/**
|
|
|
|
* Expose the `Swagger` plugin.
|
|
|
|
*/
|
|
|
|
module.exports = Swagger;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Module dependencies.
|
|
|
|
*/
|
|
|
|
var Remoting = require('strong-remoting');
|
|
|
|
var debug = require('debug')('loopback-explorer:swagger');
|
2014-07-05 19:32:00 +00:00
|
|
|
var path = require('path');
|
|
|
|
var _defaults = require('lodash.defaults');
|
|
|
|
var classHelper = require('./class-helper');
|
|
|
|
var modelHelper = require('./model-helper');
|
|
|
|
var routeHelper = require('./route-helper');
|
2014-07-04 22:09:03 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a remotable Swagger module for plugging into `RemoteObjects`.
|
|
|
|
*/
|
2014-07-05 19:32:00 +00:00
|
|
|
function Swagger(remotes, opts) {
|
|
|
|
opts = _defaults({}, opts, {
|
|
|
|
name: 'swagger',
|
|
|
|
swaggerVersion: '1.2',
|
|
|
|
resourcePath: 'resources',
|
|
|
|
version: getVersion(),
|
|
|
|
basePath: '/'
|
|
|
|
});
|
2014-07-04 22:09:03 +00:00
|
|
|
|
|
|
|
// We need a temporary REST adapter to discover our available routes.
|
|
|
|
var adapter = remotes.handler('rest').adapter;
|
|
|
|
var routes = adapter.allRoutes();
|
|
|
|
var classes = remotes.classes();
|
|
|
|
|
2014-07-05 19:32:00 +00:00
|
|
|
// Create a new Remoting instance to host the swagger docs.
|
2014-07-04 22:09:03 +00:00
|
|
|
var extension = {};
|
|
|
|
var helper = Remoting.extend(extension);
|
|
|
|
|
2014-07-05 19:32:00 +00:00
|
|
|
// These are the docs we will be sending from the /swagger endpoints.
|
|
|
|
var resourceDoc = generateResourceDoc(opts);
|
2014-07-04 22:09:03 +00:00
|
|
|
var apiDocs = {};
|
|
|
|
|
|
|
|
// A class is an endpoint root; e.g. /users, /products, and so on.
|
2014-07-05 19:32:00 +00:00
|
|
|
classes.forEach(function (aClass) {
|
|
|
|
apiDocs[aClass.name] = classHelper.generateAPIDoc(aClass, opts);
|
|
|
|
resourceDoc.apis.push(classHelper.generateResourceDocAPIEntry(aClass));
|
|
|
|
|
|
|
|
// Add the getter for this doc.
|
|
|
|
var docPath = path.join(opts.resourcePath, aClass.http.path);
|
|
|
|
addRoute(helper, apiDocs[aClass.name], docPath);
|
|
|
|
classHelper.addDynamicBasePathGetter(remotes, opts.name + '.' + docPath, apiDocs[aClass.name]);
|
2014-07-04 22:09:03 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// A route is an endpoint, such as /users/findOne.
|
2014-07-05 19:32:00 +00:00
|
|
|
routes.forEach(function(route) {
|
|
|
|
// Get the API doc matching this class name.
|
|
|
|
var className = route.method.split('.')[0];
|
|
|
|
var doc = apiDocs[className];
|
2014-07-04 22:09:03 +00:00
|
|
|
if (!doc) {
|
|
|
|
console.error('Route exists with no class: %j', route);
|
|
|
|
return;
|
|
|
|
}
|
2014-07-05 15:54:19 +00:00
|
|
|
// Get the class definition matching this route.
|
2014-07-05 19:32:00 +00:00
|
|
|
var classDef = classes.filter(function (item) {
|
|
|
|
return item.name === className;
|
2014-07-04 22:09:03 +00:00
|
|
|
})[0];
|
|
|
|
|
2014-07-05 19:32:00 +00:00
|
|
|
routeHelper.addRouteToAPIDeclaration(route, classDef, doc);
|
2014-07-04 22:09:03 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The topmost Swagger resource is a description of all (non-Swagger) resources
|
|
|
|
* available on the system, and where to find more information about them.
|
|
|
|
*/
|
2014-07-05 19:32:00 +00:00
|
|
|
addRoute(helper, resourceDoc, opts.resourcePath);
|
2014-07-04 22:09:03 +00:00
|
|
|
|
2014-07-05 19:32:00 +00:00
|
|
|
// Bind all the above routes to the endpoint at /#{name}.
|
|
|
|
remotes.exports[opts.name] = extension;
|
2014-07-04 22:09:03 +00:00
|
|
|
|
2014-07-05 19:32:00 +00:00
|
|
|
return extension;
|
2014-07-04 22:09:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-07-05 19:32:00 +00:00
|
|
|
* Add a route to this remoting extension.
|
|
|
|
* @param {Remote} helper Remoting extension.
|
|
|
|
* @param {Object} doc Doc to serve.
|
|
|
|
* @param {String} path Path from which to serve the doc.
|
2014-07-04 22:09:03 +00:00
|
|
|
*/
|
2014-07-05 19:32:00 +00:00
|
|
|
function addRoute(helper, doc, path) {
|
|
|
|
helper.method(getDoc, {
|
|
|
|
path: path,
|
|
|
|
returns: { type: 'object', root: true }
|
2014-07-04 22:09:03 +00:00
|
|
|
});
|
2014-07-05 19:32:00 +00:00
|
|
|
function getDoc(callback) {
|
|
|
|
callback(null, doc);
|
2014-07-05 15:54:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-04 22:09:03 +00:00
|
|
|
/**
|
2014-07-05 19:32:00 +00:00
|
|
|
* Generate a top-level resource doc. This is the entry point for swagger UI
|
|
|
|
* and lists all of the available APIs.
|
|
|
|
* @param {Object} opts Swagger options.
|
|
|
|
* @return {Object} Resource doc.
|
2014-07-04 22:09:03 +00:00
|
|
|
*/
|
2014-07-05 19:32:00 +00:00
|
|
|
function generateResourceDoc(opts) {
|
2014-07-04 22:09:03 +00:00
|
|
|
return {
|
2014-07-05 19:32:00 +00:00
|
|
|
swaggerVersion: opts.swaggerVersion,
|
|
|
|
apiVersion: opts.version,
|
|
|
|
apis: [],
|
|
|
|
// See https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#513-info-object
|
|
|
|
info: opts.apiInfo
|
|
|
|
// TODO Authorizations
|
|
|
|
// https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#514-authorizations-object
|
|
|
|
// TODO Produces/Consumes
|
|
|
|
// https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#52-api-declaration
|
2014-07-04 22:09:03 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-07-05 19:32:00 +00:00
|
|
|
* Attempt to get the current API version from package.json.
|
|
|
|
* @return {String} API Version.
|
2014-07-04 22:09:03 +00:00
|
|
|
*/
|
2014-07-05 19:32:00 +00:00
|
|
|
function getVersion() {
|
|
|
|
var version;
|
|
|
|
try {
|
|
|
|
version = require(path.join(process.cwd(), 'package.json')).version;
|
|
|
|
} catch(e) {
|
|
|
|
version = '';
|
2014-07-04 22:09:03 +00:00
|
|
|
}
|
2014-07-05 19:32:00 +00:00
|
|
|
return version;
|
2014-07-04 22:09:03 +00:00
|
|
|
}
|