Skip to content

Commit 56450d3

Browse files
authored
Merge pull request #325 from p1c2u/fix/non-required-request-body-fix
Non required request body fix
2 parents 3798362 + 4b55054 commit 56450d3

File tree

5 files changed

+96
-12
lines changed

5 files changed

+96
-12
lines changed

Diff for: openapi_core/exceptions.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,21 @@ class OpenAPIRequestBodyError(OpenAPIError):
3636
pass
3737

3838

39+
class MissingRequestBodyError(OpenAPIRequestBodyError):
40+
"""Missing request body error"""
41+
pass
42+
43+
44+
@attr.s(hash=True)
45+
class MissingRequestBody(MissingRequestBodyError):
46+
request = attr.ib()
47+
48+
def __str__(self):
49+
return "Missing request body"
50+
51+
3952
@attr.s(hash=True)
40-
class MissingRequestBody(OpenAPIRequestBodyError):
53+
class MissingRequiredRequestBody(MissingRequestBodyError):
4154
request = attr.ib()
4255

4356
def __str__(self):

Diff for: openapi_core/validation/request/validators.py

+13-8
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
from openapi_core.casting.schemas.exceptions import CastError
66
from openapi_core.deserializing.exceptions import DeserializeError
77
from openapi_core.exceptions import (
8-
MissingRequiredParameter, MissingParameter, MissingRequestBody,
8+
MissingRequiredParameter, MissingParameter,
9+
MissingRequiredRequestBody, MissingRequestBody,
910
)
1011
from openapi_core.security.exceptions import SecurityError
1112
from openapi_core.schema.parameters import get_aslist, get_explode
@@ -175,15 +176,18 @@ def _get_body(self, request, operation):
175176
return None, []
176177

177178
request_body = operation / 'requestBody'
179+
178180
try:
179-
media_type, mimetype = self._get_media_type(
180-
request_body / 'content', request)
181-
except MediaTypeFinderError as exc:
181+
raw_body = self._get_body_value(request_body, request)
182+
except MissingRequiredRequestBody as exc:
182183
return None, [exc, ]
184+
except MissingRequestBody:
185+
return None, []
183186

184187
try:
185-
raw_body = self._get_body_value(request_body, request)
186-
except MissingRequestBody as exc:
188+
media_type, mimetype = self._get_media_type(
189+
request_body / 'content', request)
190+
except MediaTypeFinderError as exc:
187191
return None, [exc, ]
188192

189193
try:
@@ -233,8 +237,9 @@ def _get_parameter_value(self, param, request):
233237
return location[param['name']]
234238

235239
def _get_body_value(self, request_body, request):
236-
required = request_body.getkey('required', False)
237-
if not request.body and required:
240+
if not request.body:
241+
if request_body.getkey('required', False):
242+
raise MissingRequiredRequestBody(request)
238243
raise MissingRequestBody(request)
239244
return request.body
240245

Diff for: tests/integration/data/v3.0/petstore.yaml

+28
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,22 @@ paths:
202202
description: Null response
203203
default:
204204
$ref: "#/components/responses/ErrorResponse"
205+
delete:
206+
summary: Delete tags
207+
operationId: deleteTag
208+
tags:
209+
- tags
210+
requestBody:
211+
required: false
212+
content:
213+
application/json:
214+
schema:
215+
$ref: '#/components/schemas/TagDelete'
216+
responses:
217+
'200':
218+
description: Null response
219+
default:
220+
$ref: "#/components/responses/ErrorResponse"
205221
components:
206222
schemas:
207223
Utctime:
@@ -335,6 +351,18 @@ components:
335351
name:
336352
type: string
337353
additionalProperties: false
354+
TagDelete:
355+
type: object
356+
x-model: TagDelete
357+
required:
358+
- ids
359+
properties:
360+
ids:
361+
type: array
362+
items:
363+
type: integer
364+
format: int64
365+
additionalProperties: false
338366
TagList:
339367
type: array
340368
items:

Diff for: tests/integration/validation/test_petstore.py

+36
Original file line numberDiff line numberDiff line change
@@ -1230,3 +1230,39 @@ def test_post_tags_created_invalid_type(
12301230
assert response_result.data.correlationId == correlationId
12311231
assert response_result.data.rootCause == rootCause
12321232
assert response_result.data.additionalinfo == additionalinfo
1233+
1234+
def test_delete_tags_with_requestbody(
1235+
self, spec, response_validator):
1236+
host_url = 'http://petstore.swagger.io/v1'
1237+
path_pattern = '/v1/tags'
1238+
ids = [1, 2, 3]
1239+
data_json = {
1240+
'ids': ids,
1241+
}
1242+
data = json.dumps(data_json)
1243+
request = MockRequest(
1244+
host_url, 'DELETE', '/tags',
1245+
path_pattern=path_pattern, data=data,
1246+
)
1247+
1248+
parameters = validate_parameters(spec, request)
1249+
body = validate_body(spec, request)
1250+
1251+
assert parameters == RequestParameters()
1252+
assert isinstance(body, BaseModel)
1253+
assert body.ids == ids
1254+
1255+
def test_delete_tags_no_requestbody(
1256+
self, spec, response_validator):
1257+
host_url = 'http://petstore.swagger.io/v1'
1258+
path_pattern = '/v1/tags'
1259+
request = MockRequest(
1260+
host_url, 'DELETE', '/tags',
1261+
path_pattern=path_pattern,
1262+
)
1263+
1264+
parameters = validate_parameters(spec, request)
1265+
body = validate_body(spec, request)
1266+
1267+
assert parameters == RequestParameters()
1268+
assert body is None

Diff for: tests/integration/validation/test_validators.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
from openapi_core.deserializing.exceptions import DeserializeError
88
from openapi_core.extensions.models.models import BaseModel
99
from openapi_core.exceptions import (
10-
MissingRequiredParameter, MissingRequestBody, MissingResponseContent,
10+
MissingRequiredParameter, MissingRequiredRequestBody,
11+
MissingResponseContent,
1112
)
1213
from openapi_core.shortcuts import create_spec
1314
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
@@ -154,7 +155,7 @@ def test_missing_body(self, validator):
154155
result = validator.validate(request)
155156

156157
assert len(result.errors) == 1
157-
assert type(result.errors[0]) == MissingRequestBody
158+
assert type(result.errors[0]) == MissingRequiredRequestBody
158159
assert result.body is None
159160
assert result.parameters == RequestParameters(
160161
header={
@@ -166,6 +167,7 @@ def test_missing_body(self, validator):
166167
)
167168

168169
def test_invalid_content_type(self, validator):
170+
data = "csv,data"
169171
headers = {
170172
'api_key': self.api_key_encoded,
171173
}
@@ -174,7 +176,7 @@ def test_invalid_content_type(self, validator):
174176
}
175177
request = MockRequest(
176178
'https://development.gigantic-server.com', 'post', '/v1/pets',
177-
path_pattern='/v1/pets', mimetype='text/csv',
179+
path_pattern='/v1/pets', mimetype='text/csv', data=data,
178180
headers=headers, cookies=cookies,
179181
)
180182

0 commit comments

Comments
 (0)