Skip to content

Commit 6a44ff1

Browse files
authored
Merge pull request #407 from p1c2u/feature/request-response-protocols
request and response protocols
2 parents e385101 + 91959ef commit 6a44ff1

40 files changed

+675
-1125
lines changed
+2-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
"""OpenAPI core contrib django module"""
2-
from openapi_core.contrib.django.requests import DjangoOpenAPIRequestFactory
3-
from openapi_core.contrib.django.responses import DjangoOpenAPIResponseFactory
4-
5-
DjangoOpenAPIRequest = DjangoOpenAPIRequestFactory().create
6-
DjangoOpenAPIResponse = DjangoOpenAPIResponseFactory().create
2+
from openapi_core.contrib.django.requests import DjangoOpenAPIRequest
3+
from openapi_core.contrib.django.responses import DjangoOpenAPIResponse
74

85
__all__ = [
9-
"DjangoOpenAPIRequestFactory",
10-
"DjangoOpenAPIResponseFactory",
116
"DjangoOpenAPIRequest",
127
"DjangoOpenAPIResponse",
138
]

openapi_core/contrib/django/middlewares.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@
33
from django.core.exceptions import ImproperlyConfigured
44

55
from openapi_core.contrib.django.handlers import DjangoOpenAPIErrorsHandler
6-
from openapi_core.contrib.django.requests import DjangoOpenAPIRequestFactory
7-
from openapi_core.contrib.django.responses import DjangoOpenAPIResponseFactory
6+
from openapi_core.contrib.django.requests import DjangoOpenAPIRequest
7+
from openapi_core.contrib.django.responses import DjangoOpenAPIResponse
88
from openapi_core.validation.processors import OpenAPIProcessor
99
from openapi_core.validation.request.validators import RequestValidator
1010
from openapi_core.validation.response.validators import ResponseValidator
1111

1212

1313
class DjangoOpenAPIMiddleware:
1414

15-
request_factory = DjangoOpenAPIRequestFactory()
16-
response_factory = DjangoOpenAPIResponseFactory()
15+
request_class = DjangoOpenAPIRequest
16+
response_class = DjangoOpenAPIResponse
1717
errors_handler = DjangoOpenAPIErrorsHandler()
1818

1919
def __init__(self, get_response):
@@ -53,7 +53,7 @@ def _handle_response_errors(self, response_result, req, resp):
5353
return self.errors_handler.handle(response_result.errors, req, resp)
5454

5555
def _get_openapi_request(self, request):
56-
return self.request_factory.create(request)
56+
return self.request_class(request)
5757

5858
def _get_openapi_response(self, response):
59-
return self.response_factory.create(response)
59+
return self.response_class(response)

openapi_core/contrib/django/requests.py

+42-51
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from werkzeug.datastructures import Headers
66
from werkzeug.datastructures import ImmutableMultiDict
77

8-
from openapi_core.validation.request.datatypes import OpenAPIRequest
98
from openapi_core.validation.request.datatypes import RequestParameters
109

1110
# https://docs.djangoproject.com/en/2.2/topics/http/urls/
@@ -21,59 +20,51 @@
2120
PATH_PARAMETER_PATTERN = r"(?:[^\/]*?)<(?:(?:.*?:))*?(\w+)>(?:[^\/]*)"
2221

2322

24-
class DjangoOpenAPIRequestFactory:
23+
class DjangoOpenAPIRequest:
2524

2625
path_regex = re.compile(PATH_PARAMETER_PATTERN)
2726

28-
def create(self, request):
29-
return OpenAPIRequest(
30-
full_url_pattern=self._get_full_url_pattern(request),
31-
method=self._get_method(request),
32-
parameters=self._get_parameters(request),
33-
body=self._get_body(request),
34-
mimetype=self._get_mimetype(request),
35-
)
27+
def __init__(self, request):
28+
self.request = request
3629

37-
def _get_parameters(self, request):
38-
return RequestParameters(
39-
path=self._get_path(request),
40-
query=self._get_query(request),
41-
header=self._get_header(request),
42-
cookie=self._get_cookie(request),
30+
self.parameters = RequestParameters(
31+
path=self.request.resolver_match
32+
and self.request.resolver_match.kwargs
33+
or {},
34+
query=ImmutableMultiDict(self.request.GET),
35+
header=Headers(self.request.headers.items()),
36+
cookie=ImmutableMultiDict(dict(self.request.COOKIES)),
4337
)
4438

45-
def _get_path(self, request):
46-
return request.resolver_match and request.resolver_match.kwargs or {}
47-
48-
def _get_query(self, request):
49-
return ImmutableMultiDict(request.GET)
50-
51-
def _get_header(self, request):
52-
return Headers(request.headers.items())
53-
54-
def _get_cookie(self, request):
55-
return ImmutableMultiDict(dict(request.COOKIES))
56-
57-
def _get_full_url_pattern(self, request):
58-
if request.resolver_match is None:
59-
path_pattern = request.path
60-
else:
61-
route = self.path_regex.sub(r"{\1}", request.resolver_match.route)
62-
# Delete start and end marker to allow concatenation.
63-
if route[:1] == "^":
64-
route = route[1:]
65-
if route[-1:] == "$":
66-
route = route[:-1]
67-
path_pattern = "/" + route
68-
69-
current_scheme_host = request._current_scheme_host
70-
return urljoin(current_scheme_host, path_pattern)
71-
72-
def _get_method(self, request):
73-
return request.method.lower()
74-
75-
def _get_body(self, request):
76-
return request.body
77-
78-
def _get_mimetype(self, request):
79-
return request.content_type
39+
@property
40+
def host_url(self):
41+
return self.request._current_scheme_host
42+
43+
@property
44+
def path(self):
45+
return self.request.path
46+
47+
@property
48+
def path_pattern(self):
49+
if self.request.resolver_match is None:
50+
return None
51+
52+
route = self.path_regex.sub(r"{\1}", self.request.resolver_match.route)
53+
# Delete start and end marker to allow concatenation.
54+
if route[:1] == "^":
55+
route = route[1:]
56+
if route[-1:] == "$":
57+
route = route[:-1]
58+
return "/" + route
59+
60+
@property
61+
def method(self):
62+
return self.request.method.lower()
63+
64+
@property
65+
def body(self):
66+
return self.request.body
67+
68+
@property
69+
def mimetype(self):
70+
return self.request.content_type
+15-18
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,23 @@
11
"""OpenAPI core contrib django responses module"""
22
from werkzeug.datastructures import Headers
33

4-
from openapi_core.validation.response.datatypes import OpenAPIResponse
54

5+
class DjangoOpenAPIResponse:
6+
def __init__(self, response):
7+
self.response = response
68

7-
class DjangoOpenAPIResponseFactory:
8-
def create(self, response):
9-
return OpenAPIResponse(
10-
data=self._get_data(response),
11-
status_code=self._get_status_code(response),
12-
headers=self._get_header(response),
13-
mimetype=self._get_mimetype(response),
14-
)
9+
@property
10+
def data(self):
11+
return self.response.content
1512

16-
def _get_data(self, response):
17-
return response.content
13+
@property
14+
def status_code(self):
15+
return self.response.status_code
1816

19-
def _get_status_code(self, response):
20-
return response.status_code
17+
@property
18+
def headers(self):
19+
return Headers(self.response.headers.items())
2120

22-
def _get_header(self, response):
23-
return Headers(response.headers.items())
24-
25-
def _get_mimetype(self, response):
26-
return response["Content-Type"]
21+
@property
22+
def mimetype(self):
23+
return self.response["Content-Type"]
+6-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
from openapi_core.contrib.falcon.requests import FalconOpenAPIRequestFactory
2-
from openapi_core.contrib.falcon.responses import FalconOpenAPIResponseFactory
1+
from openapi_core.contrib.falcon.requests import FalconOpenAPIRequest
2+
from openapi_core.contrib.falcon.responses import FalconOpenAPIResponse
33

4-
__all__ = ["FalconOpenAPIRequestFactory", "FalconOpenAPIResponseFactory"]
4+
__all__ = [
5+
"FalconOpenAPIRequest",
6+
"FalconOpenAPIResponse",
7+
]

openapi_core/contrib/falcon/middlewares.py

+14-14
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,37 @@
11
"""OpenAPI core contrib falcon middlewares module"""
22

33
from openapi_core.contrib.falcon.handlers import FalconOpenAPIErrorsHandler
4-
from openapi_core.contrib.falcon.requests import FalconOpenAPIRequestFactory
5-
from openapi_core.contrib.falcon.responses import FalconOpenAPIResponseFactory
4+
from openapi_core.contrib.falcon.requests import FalconOpenAPIRequest
5+
from openapi_core.contrib.falcon.responses import FalconOpenAPIResponse
66
from openapi_core.validation.processors import OpenAPIProcessor
77
from openapi_core.validation.request.validators import RequestValidator
88
from openapi_core.validation.response.validators import ResponseValidator
99

1010

1111
class FalconOpenAPIMiddleware:
1212

13-
request_factory = FalconOpenAPIRequestFactory()
14-
response_factory = FalconOpenAPIResponseFactory()
13+
request_class = FalconOpenAPIRequest
14+
response_class = FalconOpenAPIResponse
1515
errors_handler = FalconOpenAPIErrorsHandler()
1616

1717
def __init__(
1818
self,
1919
validation_processor,
20-
request_factory=None,
21-
response_factory=None,
20+
request_class=None,
21+
response_class=None,
2222
errors_handler=None,
2323
):
2424
self.validation_processor = validation_processor
25-
self.request_factory = request_factory or self.request_factory
26-
self.response_factory = response_factory or self.response_factory
25+
self.request_class = request_class or self.request_class
26+
self.response_class = response_class or self.response_class
2727
self.errors_handler = errors_handler or self.errors_handler
2828

2929
@classmethod
3030
def from_spec(
3131
cls,
3232
spec,
33-
request_factory=None,
34-
response_factory=None,
33+
request_class=None,
34+
response_class=None,
3535
errors_handler=None,
3636
):
3737
request_validator = RequestValidator(spec)
@@ -41,8 +41,8 @@ def from_spec(
4141
)
4242
return cls(
4343
validation_processor,
44-
request_factory=request_factory,
45-
response_factory=response_factory,
44+
request_class=request_class,
45+
response_class=response_class,
4646
errors_handler=errors_handler,
4747
)
4848

@@ -70,10 +70,10 @@ def _handle_response_errors(self, req, resp, response_result):
7070
return self.errors_handler.handle(req, resp, response_result.errors)
7171

7272
def _get_openapi_request(self, request):
73-
return self.request_factory.create(request)
73+
return self.request_class(request)
7474

7575
def _get_openapi_response(self, response):
76-
return self.response_factory.create(response)
76+
return self.response_class(response)
7777

7878
def _process_openapi_request(self, openapi_request):
7979
return self.validation_processor.process_request(openapi_request)

openapi_core/contrib/falcon/requests.py

+31-29
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,45 @@
44
from werkzeug.datastructures import Headers
55
from werkzeug.datastructures import ImmutableMultiDict
66

7-
from openapi_core.validation.request.datatypes import OpenAPIRequest
87
from openapi_core.validation.request.datatypes import RequestParameters
98

109

11-
class FalconOpenAPIRequestFactory:
12-
def __init__(self, default_when_empty=None):
10+
class FalconOpenAPIRequest:
11+
def __init__(self, request, default_when_empty=None):
12+
self.request = request
1313
if default_when_empty is None:
1414
default_when_empty = {}
1515
self.default_when_empty = default_when_empty
1616

17-
def create(self, request):
18-
"""
19-
Create OpenAPIRequest from falcon Request and route params.
20-
"""
21-
method = request.method.lower()
17+
# Path gets deduced by path finder against spec
18+
self.parameters = RequestParameters(
19+
query=ImmutableMultiDict(list(self.request.params.items())),
20+
header=Headers(self.request.headers),
21+
cookie=self.request.cookies,
22+
)
2223

23-
media = request.get_media(default_when_empty=self.default_when_empty)
24-
# Support falcon-jsonify.
25-
body = dumps(getattr(request, "json", media))
26-
mimetype = request.options.default_media_type
27-
if request.content_type:
28-
mimetype = request.content_type.partition(";")[0]
24+
@property
25+
def host_url(self):
26+
return self.request.prefix
2927

30-
query = ImmutableMultiDict(list(request.params.items()))
31-
header = Headers(request.headers)
28+
@property
29+
def path(self):
30+
return self.request.path
3231

33-
# Path gets deduced by path finder against spec
34-
parameters = RequestParameters(
35-
query=query,
36-
header=header,
37-
cookie=request.cookies,
38-
)
39-
url_pattern = request.prefix + request.path
40-
return OpenAPIRequest(
41-
full_url_pattern=url_pattern,
42-
method=method,
43-
parameters=parameters,
44-
body=body,
45-
mimetype=mimetype,
32+
@property
33+
def method(self):
34+
return self.request.method.lower()
35+
36+
@property
37+
def body(self):
38+
media = self.request.get_media(
39+
default_when_empty=self.default_when_empty
4640
)
41+
# Support falcon-jsonify.
42+
return dumps(getattr(self.request, "json", media))
43+
44+
@property
45+
def mimetype(self):
46+
if self.request.content_type:
47+
return self.request.content_type.partition(";")[0]
48+
return self.request.options.default_media_type

0 commit comments

Comments
 (0)