diff --git a/src/middlewares/openapi.request.validator.ts b/src/middlewares/openapi.request.validator.ts index 8a3cf607..2060f534 100644 --- a/src/middlewares/openapi.request.validator.ts +++ b/src/middlewares/openapi.request.validator.ts @@ -164,8 +164,16 @@ export class RequestValidator { }; } - private processQueryParam(query, schema, whiteList: string[] = []) { - const keys = schema.properties ? Object.keys(schema.properties) : []; + private processQueryParam(query: object, schema, whiteList: string[] = []) { + const entries = Object.entries(schema.properties ?? {}); + let keys = []; + for (const [key, prop] of entries) { + if (prop['type'] === 'object' && prop['additionalProperties']) { + // we have an object that allows additional properties + return; + } + keys.push(key); + } const knownQueryParams = new Set(keys); whiteList.forEach((item) => knownQueryParams.add(item)); const queryParams = Object.keys(query); diff --git a/test/additional.props.query.params.spec.ts b/test/additional.props.query.params.spec.ts new file mode 100644 index 00000000..0e5b662e --- /dev/null +++ b/test/additional.props.query.params.spec.ts @@ -0,0 +1,47 @@ +import * as path from 'path'; +import * as express from 'express'; +import * as request from 'supertest'; +import { createApp } from './common/app'; +import * as packageJson from '../package.json'; +import { expect } from 'chai'; + +describe(packageJson.name, () => { + let app = null; + + before(async () => { + // Set up the express app + const apiSpec = path.join( + 'test', + 'resources', + 'additional.props.query.params.yaml', + ); + app = await createApp({ apiSpec }, 3005, (app) => + app.use( + express + .Router() + .get(`/params_with_additional_props`, (req, res) => + res.status(200).json(req.body), + ), + ), + ); + }); + + after(() => { + app.server.close(); + }); + + it('should allow additional / unknown properties properties', async () => + request(app) + .get(`/params_with_additional_props`) + .query({ required: 1, test: 'test' }) + .expect(200)); + + it('should return 400 on missing required prop (when using additional props explode object)', async () => + request(app) + .get(`/params_with_additional_props`) + .query({ test: 'test' }) + .expect(400) + .then((r) => { + expect(r.body.message).to.contain('required'); + })); +}); diff --git a/test/resources/additional.props.query.params.yaml b/test/resources/additional.props.query.params.yaml new file mode 100644 index 00000000..b75d7cd7 --- /dev/null +++ b/test/resources/additional.props.query.params.yaml @@ -0,0 +1,41 @@ +openapi: 3.0.3 +info: + version: 1.0.0 + title: Test + description: A sample API +servers: + - url: / +paths: + /params_with_additional_props: + get: + parameters: + - name: required + in: query + description: maximum number of results to return + required: true + schema: + type: integer + format: int32 + minimum: 1 + maximum: 20 + - name: params + in: query + required: false + schema: + type: object + # If the parameter values are of specific type, e.g. string: + # additionalProperties: + # type: string + # If the parameter values can be of different types + # (e.g. string, number, boolean, ...) + additionalProperties: true + + # `style: form` and `explode: true` is the default serialization method + # for query parameters, so these keywords can be omitted + style: form + explode: true + responses: + '200': + description: the response + +