swagger-tools
Version:
Various tools for using and integrating with Swagger.
170 lines (136 loc) • 6.16 kB
JavaScript
/*
* The MIT License (MIT)
*
* Copyright (c) 2014 Apigee Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
;
var _ = require('lodash');
var debug = require('debug')('swagger-tools:middleware:ui');
var fs = require('fs');
var helpers = require('../lib/helpers');
var parseurl = require('parseurl');
var path = require('path');
var serveStatic = require('serve-static');
var defaultOptions = {
apiDocs: '/api-docs',
swaggerUi: '/docs'
};
var staticOptions = {};
/**
* Middleware for serving the Swagger documents and Swagger UI.
*
* @param {object} rlOrSO - The Resource Listing (Swagger 1.2) or Swagger Object (Swagger 2.0)
* @param {object[]} apiDeclarations - The array of API Declarations (Swagger 1.2)
* @param {object} [options] - The configuration options
* @param {string=/api-docs} [options.apiDocs] - The relative path to serve your Swagger documents from
* @param {string=/docs} [options.swaggerUi] - The relative path to serve Swagger UI from
* @param {string} [options.swaggerUiDir] - The filesystem path to your custom swagger-ui deployment to serve
*
* @returns the middleware function
*/
exports = module.exports = function (rlOrSO, apiDeclarations, options) {
debug('Initializing swagger-ui middleware');
var swaggerVersion = helpers.getSwaggerVersion(rlOrSO);
var apiDocsCache = {}; // Swagger document endpoints cache
var apiDocsPaths = [];
var staticMiddleware;
var swaggerApiDocsURL;
var swaggerUiPath;
if (swaggerVersion !== '1.2') {
options = apiDeclarations;
apiDeclarations = [];
}
// Set the defaults
options = _.defaults(options || {}, defaultOptions);
if (_.isUndefined(rlOrSO)) {
throw new Error('rlOrSO is required');
} else if (!_.isPlainObject(rlOrSO)) {
throw new TypeError('rlOrSO must be an object');
}
if (swaggerVersion === '1.2') {
if (_.isUndefined(apiDeclarations)) {
throw new Error('apiDeclarations is required');
} else if (!_.isPlainObject(apiDeclarations)) {
throw new TypeError('apiDeclarations must be an object');
}
}
swaggerUiPath = options.swaggerUiDir ?
path.resolve(options.swaggerUiDir) :
path.join(__dirname, 'swagger-ui');
if (options.swaggerUiDir) {
if (!fs.existsSync(swaggerUiPath)) {
throw new Error('options.swaggerUiDir path does not exist: ' + swaggerUiPath);
} else if (!fs.statSync(swaggerUiPath).isDirectory()) {
throw new Error('options.swaggerUiDir path is not a directory: ' + swaggerUiPath);
}
}
staticMiddleware = serveStatic(swaggerUiPath, staticOptions);
// Sanitize values
if (options.apiDocs.charAt(options.apiDocs.length -1) === '/') {
options.apiDocs = options.apiDocs.substring(0, options.apiDocs.length - 1);
}
if (options.swaggerUi.charAt(options.swaggerUi.length -1) === '/') {
options.swaggerUi = options.swaggerUi.substring(0, options.swaggerUi.length - 1);
}
debug(' Using swagger-ui from: %s', options.swaggerUiDir ? swaggerUiPath : 'internal');
debug(' API Docs path: %s', options.apiDocs);
// Add the Resource Listing or SwaggerObject to the response cache
apiDocsCache[options.apiDocs] = JSON.stringify(rlOrSO, null, 2);
// Add API Declarations to the response cache
_.each(apiDeclarations, function (resource, resourcePath) {
var adPath = options.apiDocs + resourcePath;
// Respond with pretty JSON (Configurable?)
apiDocsCache[adPath] = JSON.stringify(resource, null, 2);
debug(' API Declaration path: %s', adPath);
});
apiDocsPaths = Object.keys(apiDocsCache);
debug(' swagger-ui path: %s', options.swaggerUi);
return function swaggerUI (req, res, next) {
var path = parseurl(req).pathname;
var isApiDocsPath = apiDocsPaths.indexOf(path) > -1 || (swaggerVersion !== '1.2' && path === options.apiDocsPath);
var isSwaggerUiPath = path === options.swaggerUi || path.indexOf(options.swaggerUi + '/') === 0;
if (_.isUndefined(swaggerApiDocsURL)) {
// Start with the original path
swaggerApiDocsURL = parseurl.original(req).pathname;
// Remove the part after the mount point
swaggerApiDocsURL = swaggerApiDocsURL.substring(0, swaggerApiDocsURL.indexOf(req.url));
// Add the API docs path and remove any double dashes
swaggerApiDocsURL = ((options.swaggerUiPrefix ? options.swaggerUiPrefix : '') + swaggerApiDocsURL + options.apiDocs).replace(/\/\//g, '/');
}
debug('%s %s', req.method, req.url);
debug(' Will process: %s', isApiDocsPath || isSwaggerUiPath ? 'yes' : 'no');
if (isApiDocsPath) {
debug(' Serving API Docs');
res.setHeader('Content-Type', 'application/json');
return res.end(apiDocsCache[path]);
} else if (isSwaggerUiPath) {
debug(' Serving swagger-ui');
res.setHeader('Swagger-API-Docs-URL', swaggerApiDocsURL);
if (path === options.swaggerUi || path === options.swaggerUi + '/') {
req.url = '/';
} else {
req.url = req.url.substring(options.swaggerUi.length);
}
return staticMiddleware(req, res, next);
}
return next();
};
};