Skip to content

Commit

Permalink
Doesen't support readOnly + required combination #145
Browse files Browse the repository at this point in the history
  • Loading branch information
Carmine DiMascio committed Nov 27, 2019
1 parent 6d2f89f commit 91fb31c
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 16 deletions.
20 changes: 10 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "express-openapi-validator",
"version": "2.17.0",
"version": "2.17.1",
"description": "Automatically validate API requests and responses with OpenAPI 3 and Express.",
"main": "dist/index.js",
"scripts": {
Expand Down Expand Up @@ -60,6 +60,6 @@
"supertest": "^4.0.2",
"ts-node": "^8.3.0",
"tsc": "^1.20150623.0",
"typescript": "^3.6.4"
"typescript": "^3.7.2"
}
}
33 changes: 33 additions & 0 deletions src/middlewares/openapi.request.validator.ts
Expand Up @@ -233,13 +233,46 @@ export class RequestValidator {
content = requestBody.content[type];
if (content) break;
}

if (!content) {
const msg =
contentType.contentType === 'not_provided'
? 'media type not specified'
: `unsupported media type ${contentType.contentType}`;
throw validationError(415, path, msg);
}

let bodyContentSchema =
requestBody.content[contentType.contentType] &&
requestBody.content[contentType.contentType].schema;
if (bodyContentSchema && '$ref' in bodyContentSchema) {
const objectSchema = this.ajv.getSchema(bodyContentSchema.$ref);
if (
objectSchema &&
objectSchema.schema &&
(<any>objectSchema.schema).properties
) {
// handle readonly / required request body refs
// don't need to copy schema if validator gets its own copy of the api spec
// currently all middlware i.e. req and res validators share the spec
const schema = { ...(<any>objectSchema).schema };
Object.keys(schema.properties).forEach(prop => {
const propertyValue = schema.properties[prop];

const required = schema.required;
if (propertyValue.readOnly && required) {
const index = required.indexOf(prop);
if (index > -1) {
schema.required = required
.slice(0, index)
.concat(required.slice(index + 1));
}
}
});
return schema;
}
}

return content.schema || {};
}
return {};
Expand Down
38 changes: 38 additions & 0 deletions test/read.only.spec.ts
Expand Up @@ -28,6 +28,12 @@ describe(packageJson.name, () => {
.post(`${app.basePath}/products/inlined`, (req, res) =>
res.json(req.body),
)
.post(`${app.basePath}/user`, (req, res) =>
res.json({
...req.body,
...(req.query.include_id ? { id: 'test_id' } : {}),
}),
)
.post(`${app.basePath}/products/nested`, (req, res) => {
const body = req.body;
body.id = 'test';
Expand Down Expand Up @@ -130,4 +136,36 @@ describe(packageJson.name, () => {
// id is a readonly property and should not be allowed in the request
expect(body.message).to.contain('request.body.reviews[0].id');
}));

it('should pass validation if required read only properties to be missing from request', async () =>
request(app)
.post(`${app.basePath}/user`)
.set('content-type', 'application/json')
.query({
include_id: true,
})
.send({
username: 'test',
})
.expect(200)
.then(r => {
expect(r.body)
.to.be.an('object')
.with.property('id');
expect(r.body).to.have.property('username');
}));

it('should fail validation if required read only properties is missing from the response', async () =>
request(app)
.post(`${app.basePath}/user`)
.set('content-type', 'application/json')
.send({
username: 'test',
})
.expect(500)
.then(r => {
expect(r.body.errors[0])
.to.have.property('message')
.equals("should have required property 'id'");
}));
});
38 changes: 34 additions & 4 deletions test/resources/read.only.yaml
Expand Up @@ -8,9 +8,26 @@ info:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
servers:
- url: http://petstore.swagger.io/v1
- url: /v1

paths:
/user:
post:
description: get user
operationId: getUser
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/User'
responses:
'200':
description: user response
content:
application/json:
schema:
$ref: '#/components/schemas/User'
/products:
get:
description: get products
Expand Down Expand Up @@ -65,7 +82,7 @@ paths:
format: date-time
readOnly: true
responses:
'200':
'200':
description: pet response
content:
application/json:
Expand All @@ -90,7 +107,6 @@ paths:
schema:
$ref: '#/components/schemas/ProductNested'


components:
schemas:
Product:
Expand Down Expand Up @@ -136,4 +152,18 @@ components:
readOnly: true
rating:
type: integer


User:
description: default
type: object
required:
- id
- username
properties:
id:
type: string
readOnly: true
username:
type: string
name:
type: string

0 comments on commit 91fb31c

Please sign in to comment.