Skip to content

Commit c1f90b6

Browse files
committed
Unmarshalling processor enhancement
1 parent d60be8c commit c1f90b6

File tree

12 files changed

+280
-180
lines changed

12 files changed

+280
-180
lines changed

openapi_core/contrib/django/handlers.py

Lines changed: 26 additions & 6 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,19 +27,26 @@ class DjangoOpenAPIErrorsHandler:
2527
MediaTypeNotFound: 415,
2628
}
2729

30+
def __call__(
31+
self,
32+
errors: Iterable[Exception],
33+
) -> JsonResponse:
34+
data_errors = [self.format_openapi_error(err) for err in errors]
35+
data = {
36+
"errors": data_errors,
37+
}
38+
data_error_max = max(data_errors, key=self.get_error_status)
39+
return JsonResponse(data, status=data_error_max["status"])
40+
2841
@classmethod
2942
def handle(
3043
cls,
3144
errors: Iterable[Exception],
3245
req: HttpRequest,
3346
resp: Optional[HttpResponse] = None,
3447
) -> JsonResponse:
35-
data_errors = [cls.format_openapi_error(err) for err in errors]
36-
data = {
37-
"errors": data_errors,
38-
}
39-
data_error_max = max(data_errors, key=cls.get_error_status)
40-
return JsonResponse(data, status=data_error_max["status"])
48+
instance = cls()
49+
return instance(errors)
4150

4251
@classmethod
4352
def format_openapi_error(cls, error: BaseException) -> Dict[str, Any]:
@@ -52,3 +61,14 @@ def format_openapi_error(cls, error: BaseException) -> Dict[str, Any]:
5261
@classmethod
5362
def get_error_status(cls, error: Dict[str, Any]) -> str:
5463
return str(error["status"])
64+
65+
66+
class DjangoOpenAPIValidRequestHandler:
67+
68+
def __init__(self, req: HttpRequest, view: Callable[[Any], HttpResponse]):
69+
self.req = req
70+
self.view = view
71+
72+
def __call__(self, request_unmarshal_result: RequestUnmarshalResult) -> HttpResponse:
73+
self.req.openapi = request_unmarshal_result
74+
return self.view(self.req)

openapi_core/contrib/django/middlewares.py

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,20 @@
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 DjangoOpenAPIValidRequestHandler
1111
from openapi_core.contrib.django.requests import DjangoOpenAPIRequest
1212
from openapi_core.contrib.django.responses import DjangoOpenAPIResponse
1313
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-
)
1814

1915

20-
class DjangoOpenAPIMiddleware:
16+
class DjangoOpenAPIMiddleware(UnmarshallingProcessor[HttpRequest, HttpResponse]):
2117
request_class = DjangoOpenAPIRequest
2218
response_class = DjangoOpenAPIResponse
19+
valid_request_handler_cls = DjangoOpenAPIValidRequestHandler
2320
errors_handler = DjangoOpenAPIErrorsHandler()
2421

2522
def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
@@ -28,38 +25,15 @@ def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
2825
if not hasattr(settings, "OPENAPI_SPEC"):
2926
raise ImproperlyConfigured("OPENAPI_SPEC not defined in settings")
3027

31-
self.processor = UnmarshallingProcessor(settings.OPENAPI_SPEC)
28+
super().__init__(settings.OPENAPI_SPEC)
3229

3330
def __call__(self, request: HttpRequest) -> HttpResponse:
34-
openapi_request = self._get_openapi_request(request)
35-
req_result = self.processor.process_request(openapi_request)
36-
if req_result.errors:
37-
response = self._handle_request_errors(req_result, request)
38-
else:
39-
request.openapi = req_result
40-
response = self.get_response(request)
31+
valid_request_handler = self.valid_request_handler_cls(request, self.get_response)
32+
response = self.handle_request(request, valid_request_handler, self.errors_handler)
4133

42-
openapi_response = self._get_openapi_response(response)
43-
resp_result = self.processor.process_response(
44-
openapi_request, openapi_response
34+
return self.handle_response(
35+
request, response, self.errors_handler
4536
)
46-
if resp_result.errors:
47-
return self._handle_response_errors(resp_result, request, response)
48-
49-
return response
50-
51-
def _handle_request_errors(
52-
self, request_result: RequestUnmarshalResult, req: HttpRequest
53-
) -> JsonResponse:
54-
return self.errors_handler.handle(request_result.errors, req, None)
55-
56-
def _handle_response_errors(
57-
self,
58-
response_result: ResponseUnmarshalResult,
59-
req: HttpRequest,
60-
resp: HttpResponse,
61-
) -> JsonResponse:
62-
return self.errors_handler.handle(response_result.errors, req, resp)
6337

6438
def _get_openapi_request(
6539
self, request: HttpRequest

openapi_core/contrib/falcon/handlers.py

Lines changed: 29 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,31 @@ 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
50+
51+
@classmethod
52+
def handle(cls, req: Request, resp: Response, errors: Iterable[Exception]) -> Response:
53+
instance = cls(req, resp)
54+
return instance(errors)
4755

4856
@classmethod
4957
def format_openapi_error(cls, error: BaseException) -> Dict[str, Any]:
@@ -58,3 +66,14 @@ def format_openapi_error(cls, error: BaseException) -> Dict[str, Any]:
5866
@classmethod
5967
def get_error_status(cls, error: Dict[str, Any]) -> int:
6068
return int(error["status"])
69+
70+
71+
class FalconOpenAPIValidRequestHandler:
72+
73+
def __init__(self, req: Request, resp: Response):
74+
self.req = req
75+
self.resp = resp
76+
77+
def __call__(self, request_unmarshal_result: RequestUnmarshalResult) -> Response:
78+
self.req.context.openapi = request_unmarshal_result
79+
return self.resp

openapi_core/contrib/falcon/middlewares.py

Lines changed: 31 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,33 @@
66
from falcon.request import Request
77
from falcon.response import Response
88

9-
from openapi_core.contrib.falcon.handlers import FalconOpenAPIErrorsHandler
9+
from openapi_core.contrib.falcon.handlers import (
10+
FalconOpenAPIErrorsHandler,
11+
FalconOpenAPIValidRequestHandler,
12+
)
1013
from openapi_core.contrib.falcon.requests import FalconOpenAPIRequest
1114
from openapi_core.contrib.falcon.responses import FalconOpenAPIResponse
15+
from openapi_core.protocols import ErrorsHandler
1216
from openapi_core.spec import Spec
1317
from openapi_core.unmarshalling.processors import UnmarshallingProcessor
14-
from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult
1518
from openapi_core.unmarshalling.request.types import RequestUnmarshallerType
16-
from openapi_core.unmarshalling.response.datatypes import (
17-
ResponseUnmarshalResult,
18-
)
1919
from openapi_core.unmarshalling.response.types import ResponseUnmarshallerType
2020

2121

22-
class FalconOpenAPIMiddleware(UnmarshallingProcessor):
23-
request_class = FalconOpenAPIRequest
24-
response_class = FalconOpenAPIResponse
25-
errors_handler = FalconOpenAPIErrorsHandler()
22+
class FalconOpenAPIMiddleware(UnmarshallingProcessor[Request, Response]):
23+
request_cls = FalconOpenAPIRequest
24+
response_cls = FalconOpenAPIResponse
25+
valid_request_handler_cls = FalconOpenAPIValidRequestHandler
26+
errors_handler_cls: Type[FalconOpenAPIErrorsHandler] = FalconOpenAPIErrorsHandler
2627

2728
def __init__(
2829
self,
2930
spec: Spec,
3031
request_unmarshaller_cls: Optional[RequestUnmarshallerType] = None,
3132
response_unmarshaller_cls: Optional[ResponseUnmarshallerType] = None,
32-
request_class: Type[FalconOpenAPIRequest] = FalconOpenAPIRequest,
33-
response_class: Type[FalconOpenAPIResponse] = FalconOpenAPIResponse,
34-
errors_handler: Optional[FalconOpenAPIErrorsHandler] = None,
33+
request_cls: Type[FalconOpenAPIRequest] = FalconOpenAPIRequest,
34+
response_cls: Type[FalconOpenAPIResponse] = FalconOpenAPIResponse,
35+
errors_handler_cls: Type[FalconOpenAPIErrorsHandler] = FalconOpenAPIErrorsHandler,
3536
**unmarshaller_kwargs: Any,
3637
):
3738
super().__init__(
@@ -40,70 +41,46 @@ def __init__(
4041
response_unmarshaller_cls=response_unmarshaller_cls,
4142
**unmarshaller_kwargs,
4243
)
43-
self.request_class = request_class or self.request_class
44-
self.response_class = response_class or self.response_class
45-
self.errors_handler = errors_handler or self.errors_handler
44+
self.request_cls = request_cls or self.request_cls
45+
self.response_cls = response_cls or self.response_cls
46+
self.errors_handler_cls = errors_handler_cls or self.errors_handler_cls
4647

4748
@classmethod
4849
def from_spec(
4950
cls,
5051
spec: Spec,
5152
request_unmarshaller_cls: Optional[RequestUnmarshallerType] = None,
5253
response_unmarshaller_cls: Optional[ResponseUnmarshallerType] = None,
53-
request_class: Type[FalconOpenAPIRequest] = FalconOpenAPIRequest,
54-
response_class: Type[FalconOpenAPIResponse] = FalconOpenAPIResponse,
55-
errors_handler: Optional[FalconOpenAPIErrorsHandler] = None,
54+
request_cls: Type[FalconOpenAPIRequest] = FalconOpenAPIRequest,
55+
response_cls: Type[FalconOpenAPIResponse] = FalconOpenAPIResponse,
56+
errors_handler_cls: Type[FalconOpenAPIErrorsHandler] = FalconOpenAPIErrorsHandler,
5657
**unmarshaller_kwargs: Any,
5758
) -> "FalconOpenAPIMiddleware":
5859
return cls(
5960
spec,
6061
request_unmarshaller_cls=request_unmarshaller_cls,
6162
response_unmarshaller_cls=response_unmarshaller_cls,
62-
request_class=request_class,
63-
response_class=response_class,
64-
errors_handler=errors_handler,
63+
request_cls=request_cls,
64+
response_cls=response_cls,
65+
errors_handler_cls=errors_handler_cls,
6566
**unmarshaller_kwargs,
6667
)
6768

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)
69+
def process_request(self, req: Request, resp: Response) -> None:
70+
valid_handler = self.valid_request_handler_cls(req, resp)
71+
errors_handler = self.errors_handler_cls(req, resp)
72+
self.handle_request(req, valid_handler, errors_handler)
7373

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

10380
def _get_openapi_request(self, request: Request) -> FalconOpenAPIRequest:
104-
return self.request_class(request)
81+
return self.request_cls(request)
10582

10683
def _get_openapi_response(
10784
self, response: Response
10885
) -> FalconOpenAPIResponse:
109-
return self.response_class(response)
86+
return self.response_cls(response)

0 commit comments

Comments
 (0)