diff --git a/README.md b/README.md index de1c32cb..b1c709d9 100644 --- a/README.md +++ b/README.md @@ -561,7 +561,7 @@ Determines whether the validator should validate requests. responses: 200: description: success - ``` + ``` **coerceTypes:** @@ -774,10 +774,13 @@ Defines a regular expression or function that determines whether a path(s) shoul The following ignores any path that ends in `/pets` e.g. `/v1/pets`. As a regular expression: + ``` ignorePaths: /.*\/pets$/ ``` + or as a function: + ``` ignorePaths: (path) => path.endsWith('/pets') ``` @@ -1048,6 +1051,32 @@ module.exports = app; ## FAQ +**Q:** How do i handle wildcard path, like those described in RFC 6570? +**A:** OpenAPI 3.0 does not support RFC 6570. That said, we provide a minimalistic mechanism that conforms syntactically to OpenAPI 3 and accomplishes a common use case. For example, matching file paths + +Using the following OpenAPI defintion + +```yaml +/files/{path}*: + get: + tags: + - Data + parameters: + - name: path + in: path + required: true + schema: + type: string +``` + +With the following route definition + +```javascript + app.get(`/files/:path(*)`, (req, res) => { /* do stuff */ }` +``` + +You can match `/files/some/long/path` and `req.params.path` will contain `some/long/path` + **Q:** What happened to the `securityHandlers` property? **A:** In v3, `securityHandlers` have been replaced by `validateSecurity.handlers`. To use v3 security handlers, move your existing security handlers to the new property. No other change is required. Note that the v2 `securityHandlers` property is supported in v3, but deprecated @@ -1172,6 +1201,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d + This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! diff --git a/src/framework/openapi.spec.loader.ts b/src/framework/openapi.spec.loader.ts index 62770b6d..c81cbff0 100644 --- a/src/framework/openapi.spec.loader.ts +++ b/src/framework/openapi.spec.loader.ts @@ -98,6 +98,16 @@ export class OpenApiSpecLoader { } private toExpressParams(part: string): string { - return part.replace(/\{([^}]+)}/g, ':$1'); + // substitute wildcard path with express equivalent + // {/path} => /path(*) <--- RFC 6570 format (not supported by openapi) + // const pass1 = part.replace(/\{(\/)([^\*]+)(\*)}/g, '$1:$2$3'); + + // instead create our own syntax that is compatible with express' pathToRegex + // /{path}* => /:path* + const pass1 = part.replace(/\/{([^\*]+)}(\*)/g, '/:$1$2'); + + // substitute params with express equivalent + // /path/{id} => /path/:id + return pass1.replace(/\{([^}]+)}/g, ':$1'); } } diff --git a/test/resources/wildcard.path.params.yaml b/test/resources/wildcard.path.params.yaml new file mode 100644 index 00000000..7752fe0f --- /dev/null +++ b/test/resources/wildcard.path.params.yaml @@ -0,0 +1,43 @@ +openapi: 3.0.1 +info: + title: api-server + description: Pharmacy Tools API server + version: 1.0.0 +servers: +- url: /v1 +tags: +- name: Data + description: Data endpoints +- name: Specification + description: The swagger API specification +paths: + /d1/{id}: + get: + tags: + - Data + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + 200: + description: Returns the latest tracked s3 keys + content: {} + + /d2/{path}*: + get: + tags: + - Data + parameters: + - name: path + in: path + required: true + schema: + type: string + + responses: + 200: + description: Returns the latest tracked s3 keys + content: {} \ No newline at end of file diff --git a/test/wildcard.path.params.spec.ts b/test/wildcard.path.params.spec.ts new file mode 100644 index 00000000..ade9d329 --- /dev/null +++ b/test/wildcard.path.params.spec.ts @@ -0,0 +1,49 @@ +import * as path from 'path'; +import { expect } from 'chai'; +import * as request from 'supertest'; +import { createApp } from './common/app'; + +describe('wildcard path params', () => { + let app = null; + + before(async () => { + const apiSpec = path.join('test', 'resources', 'wildcard.path.params.yaml'); + app = await createApp( + { + apiSpec, + }, + 3001, + (app) => { + app + .get(`${app.basePath}/d1/:id`, (req, res) => + res.json({ + ...req.params, + }), + ) + .get(`${app.basePath}/d2/:path(*)`, (req, res) => + res.json({ + ...req.params, + }), + ); + }, + ); + }); + + after(() => app.server.close()); + + it('should allow path param without wildcard', async () => + request(app) + .get(`${app.basePath}/d1/my-id`) + .expect(200) + .then((r) => { + expect(r.body.id).to.equal('my-id'); + })); + + it('should allow path param with slashes "/" using wildcard', async () => + request(app) + .get(`${app.basePath}/d2/some/long/path`) + .expect(200) + .then((r) => { + expect(r.body.path).to.equal('some/long/path'); + })); +});