diff --git a/src/middlewares/openapi.request.validator.ts b/src/middlewares/openapi.request.validator.ts index 839f03a0..1e5e91bf 100644 --- a/src/middlewares/openapi.request.validator.ts +++ b/src/middlewares/openapi.request.validator.ts @@ -115,7 +115,7 @@ export class RequestValidator { mutator.modifyRequest(req); if (!this.requestOpts.allowUnknownQueryParameters) { - this.rejectUnknownQueryParams( + this.processQueryParam( req.query, schema.properties.query, securityQueryParam, @@ -141,22 +141,28 @@ export class RequestValidator { }; } - private rejectUnknownQueryParams( - query, - schema, - whiteList: string[] = [], - ): void { + private processQueryParam(query, schema, whiteList: string[] = []) { if (!schema.properties) return; const knownQueryParams = new Set(Object.keys(schema.properties)); whiteList.forEach(item => knownQueryParams.add(item)); const queryParams = Object.keys(query); + const allowedEmpty = schema.allowEmptyValue; for (const q of queryParams) { - if (!knownQueryParams.has(q)) { + if ( + !this.requestOpts.allowUnknownQueryParameters && + !knownQueryParams.has(q) + ) { throw validationError( 400, `.query.${q}`, `Unknown query parameter ${q}`, ); + } else if (!allowedEmpty?.has(q) && (query[q] === '' || null)) { + throw validationError( + 400, + `.query.${q}`, + `query parameter ${q} has empty value`, + ); } } } diff --git a/src/middlewares/parsers/schema.parse.ts b/src/middlewares/parsers/schema.parse.ts index f006a7c9..60819038 100644 --- a/src/middlewares/parsers/schema.parse.ts +++ b/src/middlewares/parsers/schema.parse.ts @@ -47,6 +47,12 @@ export class ParametersSchemaParser { } schemas[reqField].properties[name] = schema; + if (reqField === 'query' && parameter.allowEmptyValue) { + if (!schemas[reqField].allowEmptyValue) { + schemas[reqField].allowEmptyValue = new Set(); + } + schemas[reqField].allowEmptyValue.add(name); + } if (parameter.required) { if (!schemas[reqField].required) { schemas[reqField].required = []; diff --git a/test/query.params.allow.unknown.spec.ts b/test/query.params.allow.unknown.spec.ts index 8f859755..c7894181 100644 --- a/test/query.params.allow.unknown.spec.ts +++ b/test/query.params.allow.unknown.spec.ts @@ -32,6 +32,7 @@ describe(packageJson.name, () => { request(app) .get(`${app.basePath}/pets`) .query({ + name: 'max', tags: 'one,two,three', limit: 10, breed: 'german_shepherd', @@ -43,6 +44,7 @@ describe(packageJson.name, () => { request(app) .get(`${app.basePath}/pets`) .query({ + name: 'max', tags: 'one,two,three', limit: 10, breed: 'german_shepherd', diff --git a/test/query.params.spec.ts b/test/query.params.spec.ts index 0d3e6247..de30fe50 100644 --- a/test/query.params.spec.ts +++ b/test/query.params.spec.ts @@ -29,6 +29,7 @@ describe(packageJson.name, () => { request(app) .get(`${app.basePath}/pets`) .query({ + name: 'max', tags: 'one,two,three', limit: 10, breed: 'german_shepherd', @@ -40,6 +41,7 @@ describe(packageJson.name, () => { request(app) .get(`${app.basePath}/pets`) .query({ + name: 'max', tags: 'one,two,three', limit: 10, breed: 'german_shepherd', @@ -50,4 +52,37 @@ describe(packageJson.name, () => { .then(r => { expect(r.body.errors).to.be.an('array'); })); + + it('should not allow empty query param value', async () => + request(app) + .get(`${app.basePath}/pets`) + .query({ + name: 'max', + tags: 'one,two,three', + limit: 10, + breed: '', + owner_name: 'carmine', + }) + .expect(400) + .then(r => { + expect(r.body) + .to.have.property('message') + .that.equals('query parameter breed has empty value'); + expect(r.body.errors) + .to.be.an('array') + .with.length(1); + expect(r.body.errors[0].path).to.equal('.query.breed'); + })); + + it('should allow empty query param value with allowEmptyValue: true', async () => + request(app) + .get(`${app.basePath}/pets`) + .query({ + name: '', + tags: 'one,two,three', + limit: 10, + breed: 'german_shepherd', + owner_name: 'carmine', + }) + .expect(200)); }); diff --git a/test/resources/query.params.yaml b/test/resources/query.params.yaml index d6e62dd4..6ee2fe2f 100644 --- a/test/resources/query.params.yaml +++ b/test/resources/query.params.yaml @@ -35,6 +35,13 @@ paths: description: | Returns all pets from the system that the user has access tp parameters: + - name: name + in: query + description: name + required: true + schema: + type: string + allowEmptyValue: true - name: tags in: query description: tags to filter by