lib/path-parser.js
"use strict";
module.exports = pathParser;
const _ = require("lodash");
const util = require("./helpers/util");
const paramParser = require("./param-parser");
/**
* Parses Swagger path parameters in the HTTP request.
* This middleware populates {@link Request#params} and {@link Request#pathParams}.
*
* NOTE: Express uses a special type of middleware for parsing path parameters.
* This middleware must be registered using {@link Router#param} rather than {@link Router#use}, {@link Router#get}, etc.
* In addition, path-parsing middleware must be registered for EACH path parameter in the Swagger API.
* See http://expressjs.com/4x/api.html#router.param for more info.
*
* @param {MiddlewareContext} context
* @param {express#Router} [router]
* @returns {function[]}
*/
function pathParser (context, router) {
router = util.isExpressRouter(router) ? router : context.router;
if (util.isExpressRouter(router)) {
// This is special path-param middleware, which sets `req.params`
registerPathParamMiddleware();
// If the API changes, register any new path-params
context.on("change", registerPathParamMiddleware);
}
else {
util.debug(
"WARNING! An Express Router/Application was not passed to the requestParser middleware. " +
"req.params will not be parsed. Use req.pathParams instead."
);
}
// This is normal middleware, which sets `req.pathParams`
return [parsePathParams];
/**
* Registers middleware to parse path parameters.
*/
function registerPathParamMiddleware () {
let pathParams = getAllPathParamNames();
pathParams.forEach((param) => {
if (!alreadyRegistered(param)) {
router.param(param, pathParamMiddleware);
}
});
}
/**
* Returns the unique names of all path params in the Swagger API.
*
* @returns {string[]}
*/
function getAllPathParamNames () {
let params = [];
function addParam (param) {
if (param.in === "path") {
params.push(param.name);
}
}
if (context.api) {
_.each(context.api.paths, (path) => {
// Add each path parameter
_.each(path.parameters, addParam);
// Add each operation parameter
_.each(path, (operation) => {
_.each(operation.parameters, addParam);
});
});
}
return _.uniq(params);
}
/**
* Determines whether we've already registered path-param middleware for the given parameter.
*
* @param {string} paramName
* @returns {boolean}
*/
function alreadyRegistered (paramName) {
let params = router.params;
if (!params && router._router) {
params = router._router.params;
}
return params && params[paramName] &&
(params[paramName].indexOf(pathParamMiddleware) >= 0);
}
/**
* This is a special type of Express middleware that specifically parses path parameters and sets `req.params`.
* See http://expressjs.com/4x/api.html#router.param
*/
function pathParamMiddleware (req, res, next, value, name) {
if (req.pathParams) {
// Path parameters have already been parsed by
req.params[name] = req.pathParams[name] || req.params[name];
}
next();
}
/**
* Parses all Swagger path parameters and sets `req.pathParams`.
* NOTE: This middleware cannot set `req.params`. That requires special path-param middleware (see above)
*/
function parsePathParams (req, res, next) {
if (util.isSwaggerRequest(req)) {
req.pathParams = {};
if (req.swagger.pathName.indexOf("{") >= 0) {
// Convert the Swagger path to a RegExp
let paramNames = [];
let pathPattern = req.swagger.pathName.replace(util.swaggerParamRegExp, (match, paramName) => {
paramNames.push(paramName);
return "([^\/]+)";
});
// Exec the RegExp to get the path param values from the URL
let values = new RegExp(pathPattern + "\/?$", "i").exec(req.path);
// Parse each path param
for (let i = 1; i < values.length; i++) {
let paramName = paramNames[i - 1];
let paramValue = decodeURIComponent(values[i]);
let param = _.find(req.swagger.params, { in: "path", name: paramName });
util.debug(' Parsing the "%s" path parameter', paramName);
req.pathParams[paramName] = paramParser.parseParameter(param, paramValue, param);
}
}
}
next();
}
}