Skip to content

Commit e0af0df

Browse files
authored
Merge pull request #625 from python-openapi/feature/unmarshalling-processor-enhancement
Unmarshalling processor enhancement
2 parents d7d1fac + 22ace6d commit e0af0df

File tree

11 files changed

+286
-167
lines changed

11 files changed

+286
-167
lines changed

openapi_core/contrib/django/handlers.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""OpenAPI core contrib django handlers module"""
22
from typing import Any
3+
from typing import Callable
34
from typing import Dict
45
from typing import Iterable
56
from typing import Optional
@@ -14,6 +15,7 @@
1415
from openapi_core.templating.paths.exceptions import PathNotFound
1516
from openapi_core.templating.paths.exceptions import ServerNotFound
1617
from openapi_core.templating.security.exceptions import SecurityNotFound
18+
from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult
1719

1820

1921
class DjangoOpenAPIErrorsHandler:
@@ -25,18 +27,15 @@ class DjangoOpenAPIErrorsHandler:
2527
MediaTypeNotFound: 415,
2628
}
2729

28-
@classmethod
29-
def handle(
30-
cls,
30+
def __call__(
31+
self,
3132
errors: Iterable[Exception],
32-
req: HttpRequest,
33-
resp: Optional[HttpResponse] = None,
3433
) -> JsonResponse:
35-
data_errors = [cls.format_openapi_error(err) for err in errors]
34+
data_errors = [self.format_openapi_error(err) for err in errors]
3635
data = {
3736
"errors": data_errors,
3837
}
39-
data_error_max = max(data_errors, key=cls.get_error_status)
38+
data_error_max = max(data_errors, key=self.get_error_status)
4039
return JsonResponse(data, status=data_error_max["status"])
4140

4241
@classmethod
@@ -52,3 +51,15 @@ def format_openapi_error(cls, error: BaseException) -> Dict[str, Any]:
5251
@classmethod
5352
def get_error_status(cls, error: Dict[str, Any]) -> str:
5453
return str(error["status"])
54+
55+
56+
class DjangoOpenAPIValidRequestHandler:
57+
def __init__(self, req: HttpRequest, view: Callable[[Any], HttpResponse]):
58+
self.req = req
59+
self.view = view
60+
61+
def __call__(
62+
self, request_unmarshal_result: RequestUnmarshalResult
63+
) -> HttpResponse:
64+
self.req.openapi = request_unmarshal_result
65+
return self.view(self.req)

openapi_core/contrib/django/middlewares.py

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,24 @@
33

44
from django.conf import settings
55
from django.core.exceptions import ImproperlyConfigured
6-
from django.http import JsonResponse
76
from django.http.request import HttpRequest
87
from django.http.response import HttpResponse
98

109
from openapi_core.contrib.django.handlers import DjangoOpenAPIErrorsHandler
10+
from openapi_core.contrib.django.handlers import (
11+
DjangoOpenAPIValidRequestHandler,
12+
)
1113
from openapi_core.contrib.django.requests import DjangoOpenAPIRequest
1214
from openapi_core.contrib.django.responses import DjangoOpenAPIResponse
1315
from openapi_core.unmarshalling.processors import UnmarshallingProcessor
14-
from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult
15-
from openapi_core.unmarshalling.response.datatypes import (
16-
ResponseUnmarshalResult,
17-
)
1816

1917

20-
class DjangoOpenAPIMiddleware:
18+
class DjangoOpenAPIMiddleware(
19+
UnmarshallingProcessor[HttpRequest, HttpResponse]
20+
):
2121
request_cls = DjangoOpenAPIRequest
2222
response_cls = DjangoOpenAPIResponse
23+
valid_request_handler_cls = DjangoOpenAPIValidRequestHandler
2324
errors_handler = DjangoOpenAPIErrorsHandler()
2425

2526
def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
@@ -31,40 +32,17 @@ def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
3132
if hasattr(settings, "OPENAPI_RESPONSE_CLS"):
3233
self.response_cls = settings.OPENAPI_RESPONSE_CLS
3334

34-
self.processor = UnmarshallingProcessor(settings.OPENAPI_SPEC)
35+
super().__init__(settings.OPENAPI_SPEC)
3536

3637
def __call__(self, request: HttpRequest) -> HttpResponse:
37-
openapi_request = self._get_openapi_request(request)
38-
req_result = self.processor.process_request(openapi_request)
39-
if req_result.errors:
40-
response = self._handle_request_errors(req_result, request)
41-
else:
42-
request.openapi = req_result
43-
response = self.get_response(request)
44-
45-
if self.response_cls is None:
46-
return response
47-
openapi_response = self._get_openapi_response(response)
48-
resp_result = self.processor.process_response(
49-
openapi_request, openapi_response
38+
valid_request_handler = self.valid_request_handler_cls(
39+
request, self.get_response
40+
)
41+
response = self.handle_request(
42+
request, valid_request_handler, self.errors_handler
5043
)
51-
if resp_result.errors:
52-
return self._handle_response_errors(resp_result, request, response)
53-
54-
return response
55-
56-
def _handle_request_errors(
57-
self, request_result: RequestUnmarshalResult, req: HttpRequest
58-
) -> JsonResponse:
59-
return self.errors_handler.handle(request_result.errors, req, None)
6044

61-
def _handle_response_errors(
62-
self,
63-
response_result: ResponseUnmarshalResult,
64-
req: HttpRequest,
65-
resp: HttpResponse,
66-
) -> JsonResponse:
67-
return self.errors_handler.handle(response_result.errors, req, resp)
45+
return self.handle_response(request, response, self.errors_handler)
6846

6947
def _get_openapi_request(
7048
self, request: HttpRequest
@@ -76,3 +54,6 @@ def _get_openapi_response(
7654
) -> DjangoOpenAPIResponse:
7755
assert self.response_cls is not None
7856
return self.response_cls(response)
57+
58+
def _validate_response(self) -> bool:
59+
return self.response_cls is not None

openapi_core/contrib/falcon/handlers.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from openapi_core.templating.paths.exceptions import PathNotFound
1616
from openapi_core.templating.paths.exceptions import ServerNotFound
1717
from openapi_core.templating.security.exceptions import SecurityNotFound
18+
from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult
1819

1920

2021
class FalconOpenAPIErrorsHandler:
@@ -26,24 +27,26 @@ class FalconOpenAPIErrorsHandler:
2627
MediaTypeNotFound: 415,
2728
}
2829

29-
@classmethod
30-
def handle(
31-
cls, req: Request, resp: Response, errors: Iterable[Exception]
32-
) -> None:
33-
data_errors = [cls.format_openapi_error(err) for err in errors]
30+
def __init__(self, req: Request, resp: Response):
31+
self.req = req
32+
self.resp = resp
33+
34+
def __call__(self, errors: Iterable[Exception]) -> Response:
35+
data_errors = [self.format_openapi_error(err) for err in errors]
3436
data = {
3537
"errors": data_errors,
3638
}
3739
data_str = dumps(data)
38-
data_error_max = max(data_errors, key=cls.get_error_status)
39-
resp.content_type = MEDIA_JSON
40-
resp.status = getattr(
40+
data_error_max = max(data_errors, key=self.get_error_status)
41+
self.resp.content_type = MEDIA_JSON
42+
self.resp.status = getattr(
4143
status_codes,
4244
f"HTTP_{data_error_max['status']}",
4345
status_codes.HTTP_400,
4446
)
45-
resp.text = data_str
46-
resp.complete = True
47+
self.resp.text = data_str
48+
self.resp.complete = True
49+
return self.resp
4750

4851
@classmethod
4952
def format_openapi_error(cls, error: BaseException) -> Dict[str, Any]:
@@ -58,3 +61,15 @@ def format_openapi_error(cls, error: BaseException) -> Dict[str, Any]:
5861
@classmethod
5962
def get_error_status(cls, error: Dict[str, Any]) -> int:
6063
return int(error["status"])
64+
65+
66+
class FalconOpenAPIValidRequestHandler:
67+
def __init__(self, req: Request, resp: Response):
68+
self.req = req
69+
self.resp = resp
70+
71+
def __call__(
72+
self, request_unmarshal_result: RequestUnmarshalResult
73+
) -> Response:
74+
self.req.context.openapi = request_unmarshal_result
75+
return self.resp

openapi_core/contrib/falcon/middlewares.py

Lines changed: 26 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,24 @@
77
from falcon.response import Response
88

99
from openapi_core.contrib.falcon.handlers import FalconOpenAPIErrorsHandler
10+
from openapi_core.contrib.falcon.handlers import (
11+
FalconOpenAPIValidRequestHandler,
12+
)
1013
from openapi_core.contrib.falcon.requests import FalconOpenAPIRequest
1114
from openapi_core.contrib.falcon.responses import FalconOpenAPIResponse
1215
from openapi_core.spec import Spec
1316
from openapi_core.unmarshalling.processors import UnmarshallingProcessor
14-
from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult
1517
from openapi_core.unmarshalling.request.types import RequestUnmarshallerType
16-
from openapi_core.unmarshalling.response.datatypes import (
17-
ResponseUnmarshalResult,
18-
)
1918
from openapi_core.unmarshalling.response.types import ResponseUnmarshallerType
2019

2120

22-
class FalconOpenAPIMiddleware(UnmarshallingProcessor):
21+
class FalconOpenAPIMiddleware(UnmarshallingProcessor[Request, Response]):
2322
request_cls = FalconOpenAPIRequest
2423
response_cls = FalconOpenAPIResponse
25-
errors_handler = FalconOpenAPIErrorsHandler()
24+
valid_request_handler_cls = FalconOpenAPIValidRequestHandler
25+
errors_handler_cls: Type[
26+
FalconOpenAPIErrorsHandler
27+
] = FalconOpenAPIErrorsHandler
2628

2729
def __init__(
2830
self,
@@ -31,7 +33,9 @@ def __init__(
3133
response_unmarshaller_cls: Optional[ResponseUnmarshallerType] = None,
3234
request_cls: Type[FalconOpenAPIRequest] = FalconOpenAPIRequest,
3335
response_cls: Type[FalconOpenAPIResponse] = FalconOpenAPIResponse,
34-
errors_handler: Optional[FalconOpenAPIErrorsHandler] = None,
36+
errors_handler_cls: Type[
37+
FalconOpenAPIErrorsHandler
38+
] = FalconOpenAPIErrorsHandler,
3539
**unmarshaller_kwargs: Any,
3640
):
3741
super().__init__(
@@ -42,7 +46,7 @@ def __init__(
4246
)
4347
self.request_cls = request_cls or self.request_cls
4448
self.response_cls = response_cls or self.response_cls
45-
self.errors_handler = errors_handler or self.errors_handler
49+
self.errors_handler_cls = errors_handler_cls or self.errors_handler_cls
4650

4751
@classmethod
4852
def from_spec(
@@ -52,7 +56,9 @@ def from_spec(
5256
response_unmarshaller_cls: Optional[ResponseUnmarshallerType] = None,
5357
request_cls: Type[FalconOpenAPIRequest] = FalconOpenAPIRequest,
5458
response_cls: Type[FalconOpenAPIResponse] = FalconOpenAPIResponse,
55-
errors_handler: Optional[FalconOpenAPIErrorsHandler] = None,
59+
errors_handler_cls: Type[
60+
FalconOpenAPIErrorsHandler
61+
] = FalconOpenAPIErrorsHandler,
5662
**unmarshaller_kwargs: Any,
5763
) -> "FalconOpenAPIMiddleware":
5864
return cls(
@@ -61,46 +67,20 @@ def from_spec(
6167
response_unmarshaller_cls=response_unmarshaller_cls,
6268
request_cls=request_cls,
6369
response_cls=response_cls,
64-
errors_handler=errors_handler,
70+
errors_handler_cls=errors_handler_cls,
6571
**unmarshaller_kwargs,
6672
)
6773

68-
def process_request(self, req: Request, resp: Response) -> None: # type: ignore
69-
openapi_req = self._get_openapi_request(req)
70-
req.context.openapi = super().process_request(openapi_req)
71-
if req.context.openapi.errors:
72-
return self._handle_request_errors(req, resp, req.context.openapi)
74+
def process_request(self, req: Request, resp: Response) -> None:
75+
valid_handler = self.valid_request_handler_cls(req, resp)
76+
errors_handler = self.errors_handler_cls(req, resp)
77+
self.handle_request(req, valid_handler, errors_handler)
7378

74-
def process_response( # type: ignore
79+
def process_response(
7580
self, req: Request, resp: Response, resource: Any, req_succeeded: bool
7681
) -> None:
77-
if self.response_cls is None:
78-
return resp
79-
openapi_req = self._get_openapi_request(req)
80-
openapi_resp = self._get_openapi_response(resp)
81-
resp.context.openapi = super().process_response(
82-
openapi_req, openapi_resp
83-
)
84-
if resp.context.openapi.errors:
85-
return self._handle_response_errors(
86-
req, resp, resp.context.openapi
87-
)
88-
89-
def _handle_request_errors(
90-
self,
91-
req: Request,
92-
resp: Response,
93-
request_result: RequestUnmarshalResult,
94-
) -> None:
95-
return self.errors_handler.handle(req, resp, request_result.errors)
96-
97-
def _handle_response_errors(
98-
self,
99-
req: Request,
100-
resp: Response,
101-
response_result: ResponseUnmarshalResult,
102-
) -> None:
103-
return self.errors_handler.handle(req, resp, response_result.errors)
82+
errors_handler = self.errors_handler_cls(req, resp)
83+
self.handle_response(req, resp, errors_handler)
10484

10585
def _get_openapi_request(self, request: Request) -> FalconOpenAPIRequest:
10686
return self.request_cls(request)
@@ -110,3 +90,6 @@ def _get_openapi_response(
11090
) -> FalconOpenAPIResponse:
11191
assert self.response_cls is not None
11292
return self.response_cls(response)
93+
94+
def _validate_response(self) -> bool:
95+
return self.response_cls is not None

0 commit comments

Comments
 (0)