Skip to content

Commit 0865a4f

Browse files
authored
Merge pull request #309 from p1c2u/feature/response-finder
Response finder
2 parents 086e0b1 + 601f609 commit 0865a4f

File tree

10 files changed

+88
-66
lines changed

10 files changed

+88
-66
lines changed

openapi_core/schema/operations/models.py

-16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# -*- coding: utf-8 -*-
22
"""OpenAPI core operations models module"""
3-
from openapi_core.schema.responses.exceptions import InvalidResponse
43

54

65
class Operation(object):
@@ -29,18 +28,3 @@ def __init__(
2928

3029
def __getitem__(self, name):
3130
return self.parameters[name]
32-
33-
def get_response(self, http_status='default'):
34-
# @todo: move to Responses object
35-
try:
36-
return self.responses[http_status]
37-
except KeyError:
38-
# try range
39-
http_status_range = '{0}XX'.format(http_status[0])
40-
if http_status_range in self.responses:
41-
return self.responses[http_status_range]
42-
43-
if 'default' not in self.responses:
44-
raise InvalidResponse(http_status, self.responses)
45-
46-
return self.responses['default']

openapi_core/schema/responses/exceptions.py

-10
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,6 @@ class OpenAPIResponseError(OpenAPIMappingError):
77
pass
88

99

10-
@attr.s(hash=True)
11-
class InvalidResponse(OpenAPIResponseError):
12-
http_status = attr.ib()
13-
responses = attr.ib()
14-
15-
def __str__(self):
16-
return "Unknown response http status: {0}".format(
17-
str(self.http_status))
18-
19-
2010
@attr.s(hash=True)
2111
class MissingResponseContent(OpenAPIResponseError):
2212
response = attr.ib()

openapi_core/templating/responses/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import attr
2+
3+
from openapi_core.exceptions import OpenAPIError
4+
5+
6+
class ResponseFinderError(OpenAPIError):
7+
"""Response finder error"""
8+
9+
10+
@attr.s(hash=True)
11+
class ResponseNotFound(ResponseFinderError):
12+
"""Find response error"""
13+
http_status = attr.ib()
14+
responses = attr.ib()
15+
16+
def __str__(self):
17+
return "Unknown response http status: {0}".format(
18+
str(self.http_status))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from openapi_core.templating.responses.exceptions import ResponseNotFound
2+
3+
4+
class ResponseFinder(object):
5+
6+
def __init__(self, responses):
7+
self.responses = responses
8+
9+
def find(self, http_status='default'):
10+
try:
11+
return self.responses[http_status]
12+
except KeyError:
13+
pass
14+
15+
# try range
16+
http_status_range = '{0}XX'.format(http_status[0])
17+
if http_status_range in self.responses:
18+
return self.responses[http_status_range]
19+
20+
if 'default' not in self.responses:
21+
raise ResponseNotFound(http_status, self.responses)
22+
23+
return self.responses['default']

openapi_core/validation/response/validators.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
"""OpenAPI core validation response validators module"""
22
from openapi_core.casting.schemas.exceptions import CastError
33
from openapi_core.deserializing.exceptions import DeserializeError
4-
from openapi_core.schema.responses.exceptions import (
5-
InvalidResponse, MissingResponseContent,
6-
)
4+
from openapi_core.schema.responses.exceptions import MissingResponseContent
75
from openapi_core.templating.media_types.exceptions import MediaTypeFinderError
86
from openapi_core.templating.paths.exceptions import PathError
7+
from openapi_core.templating.responses.exceptions import ResponseFinderError
98
from openapi_core.unmarshalling.schemas.enums import UnmarshalContext
109
from openapi_core.unmarshalling.schemas.exceptions import (
1110
UnmarshalError, ValidateError,
@@ -27,7 +26,7 @@ def validate(self, request, response):
2726
operation_response = self._get_operation_response(
2827
operation, response)
2928
# don't process if operation errors
30-
except InvalidResponse as exc:
29+
except ResponseFinderError as exc:
3130
return ResponseValidationResult(errors=[exc, ])
3231

3332
data, data_errors = self._get_data(response, operation_response)
@@ -43,7 +42,9 @@ def validate(self, request, response):
4342
)
4443

4544
def _get_operation_response(self, operation, response):
46-
return operation.get_response(str(response.status_code))
45+
from openapi_core.templating.responses.finders import ResponseFinder
46+
finder = ResponseFinder(operation.responses)
47+
return finder.find(str(response.status_code))
4748

4849
def _validate_data(self, request, response):
4950
try:
@@ -56,7 +57,7 @@ def _validate_data(self, request, response):
5657
operation_response = self._get_operation_response(
5758
operation, response)
5859
# don't process if operation errors
59-
except InvalidResponse as exc:
60+
except ResponseFinderError as exc:
6061
return ResponseValidationResult(errors=[exc, ])
6162

6263
data, data_errors = self._get_data(response, operation_response)

tests/integration/schema/test_link_spec.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class TestLinkSpec(object):
66
def test_no_param(self, factory):
77
spec_dict = factory.spec_from_file("data/v3.0/links.yaml")
88
spec = create_spec(spec_dict)
9-
resp = spec['/status']['get'].get_response()
9+
resp = spec['/status']['get'].responses['default']
1010

1111
assert len(resp.links) == 1
1212

@@ -20,7 +20,7 @@ def test_no_param(self, factory):
2020
def test_param(self, factory):
2121
spec_dict = factory.spec_from_file("data/v3.0/links.yaml")
2222
spec = create_spec(spec_dict)
23-
resp = spec['/status/{resourceId}']['get'].get_response()
23+
resp = spec['/status/{resourceId}']['get'].responses['default']
2424

2525
assert len(resp.links) == 1
2626

tests/integration/validation/test_validators.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,13 @@
88
from openapi_core.extensions.models.models import BaseModel
99
from openapi_core.schema.parameters.exceptions import MissingRequiredParameter
1010
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
11-
from openapi_core.schema.responses.exceptions import (
12-
MissingResponseContent, InvalidResponse,
13-
)
11+
from openapi_core.schema.responses.exceptions import MissingResponseContent
1412
from openapi_core.shortcuts import create_spec
1513
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
1614
from openapi_core.templating.paths.exceptions import (
1715
PathNotFound, OperationNotFound,
1816
)
17+
from openapi_core.templating.responses.exceptions import ResponseNotFound
1918
from openapi_core.testing import MockRequest, MockResponse
2019
from openapi_core.unmarshalling.schemas.exceptions import InvalidSchemaValue
2120
from openapi_core.validation.exceptions import InvalidSecurity
@@ -450,7 +449,7 @@ def test_invalid_response(self, validator):
450449
result = validator.validate(request, response)
451450

452451
assert len(result.errors) == 1
453-
assert type(result.errors[0]) == InvalidResponse
452+
assert type(result.errors[0]) == ResponseNotFound
454453
assert result.data is None
455454
assert result.headers == {}
456455

tests/unit/schema/test_operations.py

-28
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,3 @@ def operation(self):
1717
def test_iteritems(self, operation):
1818
for name in operation.parameters:
1919
assert operation[name] == operation.parameters[name]
20-
21-
22-
class TestResponses(object):
23-
24-
@pytest.fixture
25-
def operation(self):
26-
responses = {
27-
'200': mock.sentinel.response_200,
28-
'299': mock.sentinel.response_299,
29-
'2XX': mock.sentinel.response_2XX,
30-
'default': mock.sentinel.response_default,
31-
}
32-
return Operation('get', '/path', responses, parameters={})
33-
34-
def test_default(self, operation):
35-
response = operation.get_response()
36-
37-
assert response == operation.responses['default']
38-
39-
def test_range(self, operation):
40-
response = operation.get_response('201')
41-
42-
assert response == operation.responses['2XX']
43-
44-
def test_exact(self, operation):
45-
response = operation.get_response('200')
46-
47-
assert response == operation.responses['200']
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import mock
2+
import pytest
3+
4+
from openapi_core.templating.responses.finders import ResponseFinder
5+
6+
7+
class TestResponses(object):
8+
9+
@pytest.fixture(scope='class')
10+
def responses(self):
11+
return {
12+
'200': mock.sentinel.response_200,
13+
'299': mock.sentinel.response_299,
14+
'2XX': mock.sentinel.response_2XX,
15+
'default': mock.sentinel.response_default,
16+
}
17+
18+
@pytest.fixture(scope='class')
19+
def finder(self, responses):
20+
return ResponseFinder(responses)
21+
22+
def test_default(self, finder, responses):
23+
response = finder.find()
24+
25+
assert response == responses['default']
26+
27+
def test_range(self, finder, responses):
28+
response = finder.find('201')
29+
30+
assert response == responses['2XX']
31+
32+
def test_exact(self, finder, responses):
33+
response = finder.find('200')
34+
35+
assert response == responses['200']

0 commit comments

Comments
 (0)