Skip to content

Commit

Permalink
Merge branch 'req_validator' of https://github.com/cdimascio/express-…
Browse files Browse the repository at this point in the history
…openapi-validator into req_validator
  • Loading branch information
Carmine DiMascio committed Jul 20, 2019
2 parents 9a5b99e + bfde2f3 commit dc3a6e9
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.vscode
.idea
node_modules
*.orig
dist
Expand Down
28 changes: 27 additions & 1 deletion openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,32 @@ paths:
enum:
- one
- two
- name: testJson
in: query
description: JSON in query params
content:
application/json:
schema:
type: object
properties:
foo:
type: string
enum:
- bar
- baz
- name: testArray
in: query
description: Array in query param
schema:
type: array
items:
type: string
enum:
- foo
- bar
- baz
style: form
explode: false
responses:
'200':
description: pet response
Expand Down Expand Up @@ -110,7 +136,7 @@ paths:
operationId: find pet by id
parameters:
- $ref: '#/components/parameters/id'

# - name: id
# in: path
# description: ID of pet to fetch
Expand Down
36 changes: 33 additions & 3 deletions src/middlewares/openapi.request.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export class RequestValidator {
private _middlewareCache;
private _apiDocs;
private ajv;

constructor(apiDocs, options = {}) {
this._middlewareCache = {};
this._apiDocs = apiDocs;
Expand Down Expand Up @@ -147,10 +148,10 @@ export class RequestValidator {

private extractContentType(req) {
let contentType = req.headers['content-type'] || 'not_provided';
let end = contentType.indexOf(';')
let end = contentType.indexOf(';');
end = end === -1 ? contentType.length : end;
if (contentType) {
return contentType.substring(0, end);
return contentType.substring(0, end);
}
return contentType;
}
Expand Down Expand Up @@ -196,6 +197,18 @@ export class RequestValidator {
}
});

/**
* array deserialization
* filter=foo,bar,baz
* filter=foo|bar|baz
* filter=foo%20bar%20baz
*/
parameters.parseArray.forEach(item => {
if (req[item.reqField] && req[item.reqField][item.name]) {
req[item.reqField][item.name] = req[item.reqField][item.name].split(item.delimiter);
}
});

const reqToValidate = {
...req,
cookies: req.cookies
Expand Down Expand Up @@ -249,7 +262,13 @@ export class RequestValidator {
path: 'params',
cookie: 'cookies',
};
const arrayDelimiter = {
form: ',',
spaceDelimited: ' ',
pipeDelimited: '|',
};
const parseJson = [];
const parseArray = [];

parameters.forEach(parameter => {
if (parameter.hasOwnProperty('$ref')) {
Expand Down Expand Up @@ -280,6 +299,17 @@ export class RequestValidator {
throw ono(err, message);
}

if (parameter.schema && parameter.schema.type === 'array' && !parameter.explode) {
const delimiter = arrayDelimiter[parameter.style];
if (!delimiter) {
const message = `Parameter 'style' has incorrect value '${parameter.style}' for [${parameter.name}]`;
const err = validationError(400, path, message);
throw ono(err, message);
}

parseArray.push({ name, reqField, delimiter });
}

if (!schema[reqField].properties) {
schema[reqField] = {
type: 'object',
Expand All @@ -296,6 +326,6 @@ export class RequestValidator {
}
});

return { schema, parseJson };
return { schema, parseJson, parseArray };
}
}
41 changes: 40 additions & 1 deletion test/resources/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,46 @@
"two"
]
}
}
},
{
"name": "testJson",
"in": "query",
"description": "JSON in query params",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"foo": {
"type": "string",
"enum": [
"bar",
"baz"
]
}
}
}
}
}
},
{
"name": "testArray",
"in": "query",
"description": "Array in query param",
"schema": {
"type": "array",
"items": {
"type": "string",
"enum": [
"foo",
"bar",
"baz"
]
}
},
"style": "form",
"explode": false
}
],
"responses": {
"200": {
Expand Down
44 changes: 44 additions & 0 deletions test/routes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,50 @@ const basePath = (<any>app).basePath;
expect(e[0].path).to.contain('limit');
expect(e[0].message).to.equal('should be >= 5');
}));

it('should return 200 when JSON in query param', async () =>
request(app)
.get(`${basePath}/pets`)
.query(`limit=10&test=one&testJson={"foo": "bar"}`)
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200));

it('should return 400 when improper JSON in query param', async () =>
request(app)
.get(`${basePath}/pets`)
.query(`limit=10&test=one&testJson={"foo": "test"}`)
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(400)
.then(r => {
const e = r.body.errors;
expect(e).to.have.length(1);
expect(e[0].path).to.contain('testJson');
expect(e[0].message).to.equal('should be equal to one of the allowed values');
}));

it('should return 200 when separated array in query param', async () =>
request(app)
.get(`${basePath}/pets`)
.query(`limit=10&test=one&testArray=foo,bar,baz`)
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200));

it('should return 400 when improper separated array in query param', async () =>
request(app)
.get(`${basePath}/pets`)
.query(`limit=10&test=one&testArray=foo,bar,test`)
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(400)
.then(r => {
const e = r.body.errors;
expect(e).to.have.length(1);
expect(e[0].path).to.contain('testArray');
expect(e[0].message).to.equal('should be equal to one of the allowed values');
}));
});

describe('POST /pets', () => {
Expand Down

0 comments on commit dc3a6e9

Please sign in to comment.