Skip to content

Commit 880315e

Browse files
committed
Unmarshalling processor enhancement
1 parent d60be8c commit 880315e

File tree

11 files changed

+305
-172
lines changed

11 files changed

+305
-172
lines changed

Diff for: openapi_core/contrib/django/handlers.py

+27-6
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,15 @@ 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+
def __init__(self, req: HttpRequest, view: Callable[[Any], HttpResponse]):
68+
self.req = req
69+
self.view = view
70+
71+
def __call__(
72+
self, request_unmarshal_result: RequestUnmarshalResult
73+
) -> HttpResponse:
74+
self.req.openapi = request_unmarshal_result
75+
return self.view(self.req)

Diff for: openapi_core/contrib/django/middlewares.py

+14-34
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_class = DjangoOpenAPIRequest
2222
response_class = DjangoOpenAPIResponse
23+
valid_request_handler_cls = DjangoOpenAPIValidRequestHandler
2324
errors_handler = DjangoOpenAPIErrorsHandler()
2425

2526
def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
@@ -28,38 +29,17 @@ def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
2829
if not hasattr(settings, "OPENAPI_SPEC"):
2930
raise ImproperlyConfigured("OPENAPI_SPEC not defined in settings")
3031

31-
self.processor = UnmarshallingProcessor(settings.OPENAPI_SPEC)
32+
super().__init__(settings.OPENAPI_SPEC)
3233

3334
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)
41-
42-
openapi_response = self._get_openapi_response(response)
43-
resp_result = self.processor.process_response(
44-
openapi_request, openapi_response
35+
valid_request_handler = self.valid_request_handler_cls(
36+
request, self.get_response
37+
)
38+
response = self.handle_request(
39+
request, valid_request_handler, self.errors_handler
4540
)
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)
5541

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)
42+
return self.handle_response(request, response, self.errors_handler)
6343

6444
def _get_openapi_request(
6545
self, request: HttpRequest

Diff for: openapi_core/contrib/falcon/handlers.py

+32-10
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,33 @@ 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(
53+
cls, req: Request, resp: Response, errors: Iterable[Exception]
54+
) -> Response:
55+
instance = cls(req, resp)
56+
return instance(errors)
4757

4858
@classmethod
4959
def format_openapi_error(cls, error: BaseException) -> Dict[str, Any]:
@@ -58,3 +68,15 @@ def format_openapi_error(cls, error: BaseException) -> Dict[str, Any]:
5868
@classmethod
5969
def get_error_status(cls, error: Dict[str, Any]) -> int:
6070
return int(error["status"])
71+
72+
73+
class FalconOpenAPIValidRequestHandler:
74+
def __init__(self, req: Request, resp: Response):
75+
self.req = req
76+
self.resp = resp
77+
78+
def __call__(
79+
self, request_unmarshal_result: RequestUnmarshalResult
80+
) -> Response:
81+
self.req.context.openapi = request_unmarshal_result
82+
return self.resp

Diff for: openapi_core/contrib/falcon/middlewares.py

+35-53
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,35 @@
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):
23-
request_class = FalconOpenAPIRequest
24-
response_class = FalconOpenAPIResponse
25-
errors_handler = FalconOpenAPIErrorsHandler()
21+
class FalconOpenAPIMiddleware(UnmarshallingProcessor[Request, Response]):
22+
request_cls = FalconOpenAPIRequest
23+
response_cls = FalconOpenAPIResponse
24+
valid_request_handler_cls = FalconOpenAPIValidRequestHandler
25+
errors_handler_cls: Type[
26+
FalconOpenAPIErrorsHandler
27+
] = FalconOpenAPIErrorsHandler
2628

2729
def __init__(
2830
self,
2931
spec: Spec,
3032
request_unmarshaller_cls: Optional[RequestUnmarshallerType] = None,
3133
response_unmarshaller_cls: Optional[ResponseUnmarshallerType] = None,
32-
request_class: Type[FalconOpenAPIRequest] = FalconOpenAPIRequest,
33-
response_class: Type[FalconOpenAPIResponse] = FalconOpenAPIResponse,
34-
errors_handler: Optional[FalconOpenAPIErrorsHandler] = None,
34+
request_cls: Type[FalconOpenAPIRequest] = FalconOpenAPIRequest,
35+
response_cls: Type[FalconOpenAPIResponse] = FalconOpenAPIResponse,
36+
errors_handler_cls: Type[
37+
FalconOpenAPIErrorsHandler
38+
] = FalconOpenAPIErrorsHandler,
3539
**unmarshaller_kwargs: Any,
3640
):
3741
super().__init__(
@@ -40,70 +44,48 @@ def __init__(
4044
response_unmarshaller_cls=response_unmarshaller_cls,
4145
**unmarshaller_kwargs,
4246
)
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
47+
self.request_cls = request_cls or self.request_cls
48+
self.response_cls = response_cls or self.response_cls
49+
self.errors_handler_cls = errors_handler_cls or self.errors_handler_cls
4650

4751
@classmethod
4852
def from_spec(
4953
cls,
5054
spec: Spec,
5155
request_unmarshaller_cls: Optional[RequestUnmarshallerType] = None,
5256
response_unmarshaller_cls: Optional[ResponseUnmarshallerType] = None,
53-
request_class: Type[FalconOpenAPIRequest] = FalconOpenAPIRequest,
54-
response_class: Type[FalconOpenAPIResponse] = FalconOpenAPIResponse,
55-
errors_handler: Optional[FalconOpenAPIErrorsHandler] = None,
57+
request_cls: Type[FalconOpenAPIRequest] = FalconOpenAPIRequest,
58+
response_cls: Type[FalconOpenAPIResponse] = FalconOpenAPIResponse,
59+
errors_handler_cls: Type[
60+
FalconOpenAPIErrorsHandler
61+
] = FalconOpenAPIErrorsHandler,
5662
**unmarshaller_kwargs: Any,
5763
) -> "FalconOpenAPIMiddleware":
5864
return cls(
5965
spec,
6066
request_unmarshaller_cls=request_unmarshaller_cls,
6167
response_unmarshaller_cls=response_unmarshaller_cls,
62-
request_class=request_class,
63-
response_class=response_class,
64-
errors_handler=errors_handler,
68+
request_cls=request_cls,
69+
response_cls=response_cls,
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-
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)
82+
errors_handler = self.errors_handler_cls(req, resp)
83+
self.handle_response(req, resp, errors_handler)
10284

10385
def _get_openapi_request(self, request: Request) -> FalconOpenAPIRequest:
104-
return self.request_class(request)
86+
return self.request_cls(request)
10587

10688
def _get_openapi_response(
10789
self, response: Response
10890
) -> FalconOpenAPIResponse:
109-
return self.response_class(response)
91+
return self.response_cls(response)

0 commit comments

Comments
 (0)