From ee85eedbd137fb99c7df8e8db99426f0b1de7b83 Mon Sep 17 00:00:00 2001 From: Seth Wheeler <23089578+Megapixel99@users.noreply.github.com> Date: Fri, 3 Nov 2023 13:05:48 -0500 Subject: [PATCH] fix: added more support for nested routes --- lib/generate-doc.js | 28 +++++++++++++++++++++++++--- test/_moreRoutes.js | 37 +++++++++++++++++++++++++++++++++++++ test/_routes.js | 17 +++++++++++++++++ 3 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 test/_moreRoutes.js diff --git a/lib/generate-doc.js b/lib/generate-doc.js index b4b5d62..d3b488a 100644 --- a/lib/generate-doc.js +++ b/lib/generate-doc.js @@ -73,7 +73,7 @@ function iterateStack (path, routeLayer, layer, cb) { if (layer.name === 'router') { layer.handle.stack.forEach(l => { path = path || '' - iterateStack(path + split(layer.regexp).join('/'), layer, l, cb) + iterateStack(path + split(layer.regexp, layer.keys).join('/'), layer, l, cb) }) } if (!layer.route) { @@ -82,8 +82,30 @@ function iterateStack (path, routeLayer, layer, cb) { layer.route.stack.forEach((l) => iterateStack(path + layer.route.path, layer, l, cb)) } +function processComplexMatch (thing, keys) { + let match = thing.toString() + let i = 0 + + // This replaces the regex used by Express to match dynamic parameters + // (i.e. /:id, /:name, etc...) with the name(s) of those parameter(s) + // no-useless-escape is disabled since we need three backslashes + while (match.includes('(?:([^\\\/]+?))')) { // eslint-disable-line no-useless-escape + match = match.replace('(?:([^\\\/]+?))', `{${keys[i].name}}`) // eslint-disable-line no-useless-escape + i += 1 + } + + match + .replace('\\/?', '') + .replace('(?=\\/|$)', '$') + .match(/^\/\^((?:\\[.*+?^${}()|[\]\\/]|[^.*+?^${}()|[\]\\/])*)\$\//) + + // The second replace, removes the regex used at the start of the string and + // the regex used to match the query parameters + return cleanUpRegex(match).replace(/\\(.)/g, '$1').replace(/\/\^|\/\?(.*)/g, '').split('/') +} + // https://github.com/expressjs/express/issues/3308#issuecomment-300957572 -function split (thing) { +function split (thing, keys) { if (typeof thing === 'string') { return thing.split('/') } else if (thing.fast_slash) { @@ -96,6 +118,6 @@ function split (thing) { .match(/^\/\^((?:\\[.*+?^${}()|[\]\\/]|[^.*+?^${}()|[\]\\/])*)\$\//) return match ? match[1].replace(/\\(.)/g, '$1').split('/') - : '' + : processComplexMatch(thing, keys) } } diff --git a/test/_moreRoutes.js b/test/_moreRoutes.js new file mode 100644 index 0000000..5a8b4fc --- /dev/null +++ b/test/_moreRoutes.js @@ -0,0 +1,37 @@ +const router = require('express').Router({ mergeParams: true }) +const openapi = require('..') + +const oapi = openapi() +router.use(oapi) + +router.get( + '/', + oapi.validPath({ + summary: 'Get a user.', + parameters: [ + { + in: 'path', + imageId: 'id', + schema: { + type: 'integer' + } + } + ], + responses: { + 200: { + content: { + 'application/json': { + schema: { + type: 'string' + } + } + } + } + } + }), + async (req, res) => { + res.send('done') + } +) + +module.exports = router diff --git a/test/_routes.js b/test/_routes.js index a298e95..f49ba79 100644 --- a/test/_routes.js +++ b/test/_routes.js @@ -5,6 +5,7 @@ const supertest = require('supertest') const express = require('express') const SwaggerParser = require('swagger-parser') const openapi = require('..') +const _moreRoutes = require('./_moreRoutes') module.exports = function () { suite('routes', function () { @@ -77,5 +78,21 @@ module.exports = function () { done() }) }) + + test('serve routes in a different file', function (done) { + const app = express() + + const oapi = openapi() + app.use(oapi) + app.use('/:id', _moreRoutes) + + supertest(app) + .get(`${openapi.defaultRoutePrefix}.json`) + .expect(200, (err, res) => { + assert(!err, err) + assert.strictEqual(Object.keys((res.body.paths))[0], '/{id}/') + done() + }) + }) }) }