loopback-component-explorer/lib/swagger.js

132 lines
3.9 KiB
JavaScript
Raw Normal View History

'use strict';
/**
* Expose the `Swagger` plugin.
*/
module.exports = Swagger;
/**
* Module dependencies.
*/
var Remoting = require('strong-remoting');
var debug = require('debug')('loopback-explorer:swagger');
var path = require('path');
var _defaults = require('lodash.defaults');
var classHelper = require('./class-helper');
var modelHelper = require('./model-helper');
var routeHelper = require('./route-helper');
/**
* Create a remotable Swagger module for plugging into `RemoteObjects`.
*/
function Swagger(remotes, opts) {
opts = _defaults({}, opts, {
name: 'swagger',
swaggerVersion: '1.2',
resourcePath: 'resources',
version: getVersion(),
basePath: '/'
});
// 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();
// Create a new Remoting instance to host the swagger docs.
var extension = {};
var helper = Remoting.extend(extension);
// These are the docs we will be sending from the /swagger endpoints.
var resourceDoc = generateResourceDoc(opts);
var apiDocs = {};
// A class is an endpoint root; e.g. /users, /products, and so on.
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]);
});
// A route is an endpoint, such as /users/findOne.
routes.forEach(function(route) {
// Get the API doc matching this class name.
var className = route.method.split('.')[0];
var doc = apiDocs[className];
if (!doc) {
console.error('Route exists with no class: %j', route);
return;
}
// Get the class definition matching this route.
var classDef = classes.filter(function (item) {
return item.name === className;
})[0];
routeHelper.addRouteToAPIDeclaration(route, classDef, doc);
});
/**
* The topmost Swagger resource is a description of all (non-Swagger) resources
* available on the system, and where to find more information about them.
*/
addRoute(helper, resourceDoc, opts.resourcePath);
// Bind all the above routes to the endpoint at /#{name}.
remotes.exports[opts.name] = extension;
return extension;
}
/**
* 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.
*/
function addRoute(helper, doc, path) {
helper.method(getDoc, {
path: path,
returns: { type: 'object', root: true }
});
function getDoc(callback) {
callback(null, doc);
}
}
/**
* 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.
*/
function generateResourceDoc(opts) {
return {
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
};
}
/**
* Attempt to get the current API version from package.json.
* @return {String} API Version.
*/
function getVersion() {
var version;
try {
version = require(path.join(process.cwd(), 'package.json')).version;
} catch(e) {
version = '';
}
return version;
}