Skip to content

Commit

Permalink
Support a wider range of query param names
Browse files Browse the repository at this point in the history
  • Loading branch information
ckeboss committed Nov 25, 2019
1 parent 9c4c57f commit 9ecdbb5
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 2 deletions.
38 changes: 37 additions & 1 deletion src/middlewares/openapi.request.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,27 @@ export class RequestValidator {

const validator = this.ajv.compile(schema);
return (req, res, next) => {
const queryIndex = req.originalUrl.indexOf('?');
const queryString = (queryIndex >= 0) ? req.originalUrl.slice(queryIndex + 1) : '';
const searchParams = new URLSearchParams(queryString);

const queryParamsToValidate = {}

searchParams.forEach((value, key) => {
if (queryParamsToValidate[key]) {
if (queryParamsToValidate[key] instanceof Array) {
queryParamsToValidate[key].push(value)
} else {
queryParamsToValidate[key] = [queryParamsToValidate[key], value]
}
} else {
queryParamsToValidate[key] = value
}
});

if (!this._requestOpts.allowUnknownQueryParameters) {
this.rejectUnknownQueryParams(
req.query,
queryParamsToValidate,
schema.properties.query,
securityQueryParameter,
);
Expand All @@ -141,6 +159,11 @@ export class RequestValidator {
* https://swagger.io/docs/specification/describing-parameters/#schema-vs-content
*/
parameters.parseJson.forEach(item => {
if (item.reqField === 'query' && queryParamsToValidate[item.name]) {
queryParamsToValidate[item.name] = JSON.parse(
queryParamsToValidate[item.name],
);
}
if (req[item.reqField] && req[item.reqField][item.name]) {
req[item.reqField][item.name] = JSON.parse(
req[item.reqField][item.name],
Expand All @@ -155,6 +178,11 @@ export class RequestValidator {
* filter=foo%20bar%20baz
*/
parameters.parseArray.forEach(item => {
if (item.reqField === 'query' && queryParamsToValidate[item.name]) {
queryParamsToValidate[item.name] = queryParamsToValidate[item.name].split(
item.delimiter,
);
}
if (req[item.reqField] && req[item.reqField][item.name]) {
req[item.reqField][item.name] = req[item.reqField][item.name].split(
item.delimiter,
Expand All @@ -166,6 +194,13 @@ export class RequestValidator {
* forcing convert to array if scheme describes param as array + explode
*/
parameters.parseArrayExplode.forEach(item => {
if (
item.reqField === 'query' &&
queryParamsToValidate[item.name] &&
!(queryParamsToValidate[item.name] instanceof Array)
) {
queryParamsToValidate[item.name] = [queryParamsToValidate[item.name]];
}
if (
req[item.reqField] &&
req[item.reqField][item.name] &&
Expand All @@ -177,6 +212,7 @@ export class RequestValidator {

const reqToValidate = {
...req,
query: queryParamsToValidate,
cookies: req.cookies
? { ...req.cookies, ...req.signedCookies }
: undefined,
Expand Down
7 changes: 7 additions & 0 deletions test/common/app.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ export function routes(app) {
});
});

app.get(`${basePath}/pets/with-required-date-filter`, function(req, res, next) {
res.json({
test: 'hi',
...req.body,
});
});

app.get(`${basePath}/pets/:id`, function(req, res, next) {
res.json({
id: req.params.id,
Expand Down
34 changes: 34 additions & 0 deletions test/query.params.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,47 @@ describe(packageJson.name, () => {
request(app)
.get(`${app.basePath}/pets`)
.query({
'filter[date]': '2000-02-29',
tags: 'one,two,three',
limit: 10,
breed: 'german_shepherd',
owner_name: 'carmine',
})
.expect(200));

it('should pass with query params containing []', async () =>
request(app)
.get(`${app.basePath}/pets`)
.query({
'filter[date]': '2000-02-29',
limit: 10,
breed: 'german_shepherd',
owner_name: 'carmine',
})
.expect(200));

it('should fail with invalid query params containing []', async () =>
request(app)
.get(`${app.basePath}/pets`)
.query({
'filter[date]': 'not-a-date',
limit: 10,
breed: 'german_shepherd',
owner_name: 'carmine',
})
.expect(400)
.then(r => {
expect(r.body.errors).to.be.an('array');
}));

it('should pass with required query params containing []', async () =>
request(app)
.get(`${app.basePath}/pets/with-required-date-filter`)
.query({
'filter[date]': '2000-02-29'
})
.expect(200));

it('should fail if unknown query param is specified', async () =>
request(app)
.get(`${app.basePath}/pets`)
Expand Down
32 changes: 31 additions & 1 deletion test/resources/query.params.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,16 @@ paths:
/pets:
get:
description: |
Returns all pets from the system that the user has access tp
Returns all pets from the system that the user has access to
parameters:
- name: filter[date]
in: query
description: date to filter by
required: false
style: form
schema:
type: string
format: date
- name: tags
in: query
description: tags to filter by
Expand Down Expand Up @@ -71,6 +79,28 @@ paths:
type: array
items:
$ref: '#/components/schemas/Pet'
/pets/with-required-date-filter:
get:
description: |
Returns all pets from the system that the user has access to with required date filter
parameters:
- name: filter[date]
in: query
description: date to filter by
required: true
style: form
schema:
type: string
format: date
responses:
'200':
description: pet response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
components:
parameters:
owner_name:
Expand Down

0 comments on commit 9ecdbb5

Please sign in to comment.