diff --git a/openapi_core/contrib/django/handlers.py b/openapi_core/contrib/django/handlers.py index d82e3e31..2209bc8c 100644 --- a/openapi_core/contrib/django/handlers.py +++ b/openapi_core/contrib/django/handlers.py @@ -1,5 +1,6 @@ """OpenAPI core contrib django handlers module""" from typing import Any +from typing import Callable from typing import Dict from typing import Iterable from typing import Optional @@ -14,6 +15,7 @@ from openapi_core.templating.paths.exceptions import PathNotFound from openapi_core.templating.paths.exceptions import ServerNotFound from openapi_core.templating.security.exceptions import SecurityNotFound +from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult class DjangoOpenAPIErrorsHandler: @@ -25,18 +27,15 @@ class DjangoOpenAPIErrorsHandler: MediaTypeNotFound: 415, } - @classmethod - def handle( - cls, + def __call__( + self, errors: Iterable[Exception], - req: HttpRequest, - resp: Optional[HttpResponse] = None, ) -> JsonResponse: - data_errors = [cls.format_openapi_error(err) for err in errors] + data_errors = [self.format_openapi_error(err) for err in errors] data = { "errors": data_errors, } - data_error_max = max(data_errors, key=cls.get_error_status) + data_error_max = max(data_errors, key=self.get_error_status) return JsonResponse(data, status=data_error_max["status"]) @classmethod @@ -52,3 +51,15 @@ def format_openapi_error(cls, error: BaseException) -> Dict[str, Any]: @classmethod def get_error_status(cls, error: Dict[str, Any]) -> str: return str(error["status"]) + + +class DjangoOpenAPIValidRequestHandler: + def __init__(self, req: HttpRequest, view: Callable[[Any], HttpResponse]): + self.req = req + self.view = view + + def __call__( + self, request_unmarshal_result: RequestUnmarshalResult + ) -> HttpResponse: + self.req.openapi = request_unmarshal_result + return self.view(self.req) diff --git a/openapi_core/contrib/django/middlewares.py b/openapi_core/contrib/django/middlewares.py index 6998b9be..db87751f 100644 --- a/openapi_core/contrib/django/middlewares.py +++ b/openapi_core/contrib/django/middlewares.py @@ -3,23 +3,24 @@ from django.conf import settings from django.core.exceptions import ImproperlyConfigured -from django.http import JsonResponse from django.http.request import HttpRequest from django.http.response import HttpResponse from openapi_core.contrib.django.handlers import DjangoOpenAPIErrorsHandler +from openapi_core.contrib.django.handlers import ( + DjangoOpenAPIValidRequestHandler, +) from openapi_core.contrib.django.requests import DjangoOpenAPIRequest from openapi_core.contrib.django.responses import DjangoOpenAPIResponse from openapi_core.unmarshalling.processors import UnmarshallingProcessor -from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult -from openapi_core.unmarshalling.response.datatypes import ( - ResponseUnmarshalResult, -) -class DjangoOpenAPIMiddleware: +class DjangoOpenAPIMiddleware( + UnmarshallingProcessor[HttpRequest, HttpResponse] +): request_cls = DjangoOpenAPIRequest response_cls = DjangoOpenAPIResponse + valid_request_handler_cls = DjangoOpenAPIValidRequestHandler errors_handler = DjangoOpenAPIErrorsHandler() def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]): @@ -31,40 +32,17 @@ def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]): if hasattr(settings, "OPENAPI_RESPONSE_CLS"): self.response_cls = settings.OPENAPI_RESPONSE_CLS - self.processor = UnmarshallingProcessor(settings.OPENAPI_SPEC) + super().__init__(settings.OPENAPI_SPEC) def __call__(self, request: HttpRequest) -> HttpResponse: - openapi_request = self._get_openapi_request(request) - req_result = self.processor.process_request(openapi_request) - if req_result.errors: - response = self._handle_request_errors(req_result, request) - else: - request.openapi = req_result - response = self.get_response(request) - - if self.response_cls is None: - return response - openapi_response = self._get_openapi_response(response) - resp_result = self.processor.process_response( - openapi_request, openapi_response + valid_request_handler = self.valid_request_handler_cls( + request, self.get_response + ) + response = self.handle_request( + request, valid_request_handler, self.errors_handler ) - if resp_result.errors: - return self._handle_response_errors(resp_result, request, response) - - return response - - def _handle_request_errors( - self, request_result: RequestUnmarshalResult, req: HttpRequest - ) -> JsonResponse: - return self.errors_handler.handle(request_result.errors, req, None) - def _handle_response_errors( - self, - response_result: ResponseUnmarshalResult, - req: HttpRequest, - resp: HttpResponse, - ) -> JsonResponse: - return self.errors_handler.handle(response_result.errors, req, resp) + return self.handle_response(request, response, self.errors_handler) def _get_openapi_request( self, request: HttpRequest @@ -76,3 +54,6 @@ def _get_openapi_response( ) -> DjangoOpenAPIResponse: assert self.response_cls is not None return self.response_cls(response) + + def _validate_response(self) -> bool: + return self.response_cls is not None diff --git a/openapi_core/contrib/falcon/handlers.py b/openapi_core/contrib/falcon/handlers.py index 857b6b8b..ccbbe657 100644 --- a/openapi_core/contrib/falcon/handlers.py +++ b/openapi_core/contrib/falcon/handlers.py @@ -15,6 +15,7 @@ from openapi_core.templating.paths.exceptions import PathNotFound from openapi_core.templating.paths.exceptions import ServerNotFound from openapi_core.templating.security.exceptions import SecurityNotFound +from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult class FalconOpenAPIErrorsHandler: @@ -26,24 +27,26 @@ class FalconOpenAPIErrorsHandler: MediaTypeNotFound: 415, } - @classmethod - def handle( - cls, req: Request, resp: Response, errors: Iterable[Exception] - ) -> None: - data_errors = [cls.format_openapi_error(err) for err in errors] + def __init__(self, req: Request, resp: Response): + self.req = req + self.resp = resp + + def __call__(self, errors: Iterable[Exception]) -> Response: + data_errors = [self.format_openapi_error(err) for err in errors] data = { "errors": data_errors, } data_str = dumps(data) - data_error_max = max(data_errors, key=cls.get_error_status) - resp.content_type = MEDIA_JSON - resp.status = getattr( + data_error_max = max(data_errors, key=self.get_error_status) + self.resp.content_type = MEDIA_JSON + self.resp.status = getattr( status_codes, f"HTTP_{data_error_max['status']}", status_codes.HTTP_400, ) - resp.text = data_str - resp.complete = True + self.resp.text = data_str + self.resp.complete = True + return self.resp @classmethod def format_openapi_error(cls, error: BaseException) -> Dict[str, Any]: @@ -58,3 +61,15 @@ def format_openapi_error(cls, error: BaseException) -> Dict[str, Any]: @classmethod def get_error_status(cls, error: Dict[str, Any]) -> int: return int(error["status"]) + + +class FalconOpenAPIValidRequestHandler: + def __init__(self, req: Request, resp: Response): + self.req = req + self.resp = resp + + def __call__( + self, request_unmarshal_result: RequestUnmarshalResult + ) -> Response: + self.req.context.openapi = request_unmarshal_result + return self.resp diff --git a/openapi_core/contrib/falcon/middlewares.py b/openapi_core/contrib/falcon/middlewares.py index f30c7f59..7a7d1533 100644 --- a/openapi_core/contrib/falcon/middlewares.py +++ b/openapi_core/contrib/falcon/middlewares.py @@ -7,22 +7,24 @@ from falcon.response import Response from openapi_core.contrib.falcon.handlers import FalconOpenAPIErrorsHandler +from openapi_core.contrib.falcon.handlers import ( + FalconOpenAPIValidRequestHandler, +) from openapi_core.contrib.falcon.requests import FalconOpenAPIRequest from openapi_core.contrib.falcon.responses import FalconOpenAPIResponse from openapi_core.spec import Spec from openapi_core.unmarshalling.processors import UnmarshallingProcessor -from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult from openapi_core.unmarshalling.request.types import RequestUnmarshallerType -from openapi_core.unmarshalling.response.datatypes import ( - ResponseUnmarshalResult, -) from openapi_core.unmarshalling.response.types import ResponseUnmarshallerType -class FalconOpenAPIMiddleware(UnmarshallingProcessor): +class FalconOpenAPIMiddleware(UnmarshallingProcessor[Request, Response]): request_cls = FalconOpenAPIRequest response_cls = FalconOpenAPIResponse - errors_handler = FalconOpenAPIErrorsHandler() + valid_request_handler_cls = FalconOpenAPIValidRequestHandler + errors_handler_cls: Type[ + FalconOpenAPIErrorsHandler + ] = FalconOpenAPIErrorsHandler def __init__( self, @@ -31,7 +33,9 @@ def __init__( response_unmarshaller_cls: Optional[ResponseUnmarshallerType] = None, request_cls: Type[FalconOpenAPIRequest] = FalconOpenAPIRequest, response_cls: Type[FalconOpenAPIResponse] = FalconOpenAPIResponse, - errors_handler: Optional[FalconOpenAPIErrorsHandler] = None, + errors_handler_cls: Type[ + FalconOpenAPIErrorsHandler + ] = FalconOpenAPIErrorsHandler, **unmarshaller_kwargs: Any, ): super().__init__( @@ -42,7 +46,7 @@ def __init__( ) self.request_cls = request_cls or self.request_cls self.response_cls = response_cls or self.response_cls - self.errors_handler = errors_handler or self.errors_handler + self.errors_handler_cls = errors_handler_cls or self.errors_handler_cls @classmethod def from_spec( @@ -52,7 +56,9 @@ def from_spec( response_unmarshaller_cls: Optional[ResponseUnmarshallerType] = None, request_cls: Type[FalconOpenAPIRequest] = FalconOpenAPIRequest, response_cls: Type[FalconOpenAPIResponse] = FalconOpenAPIResponse, - errors_handler: Optional[FalconOpenAPIErrorsHandler] = None, + errors_handler_cls: Type[ + FalconOpenAPIErrorsHandler + ] = FalconOpenAPIErrorsHandler, **unmarshaller_kwargs: Any, ) -> "FalconOpenAPIMiddleware": return cls( @@ -61,46 +67,20 @@ def from_spec( response_unmarshaller_cls=response_unmarshaller_cls, request_cls=request_cls, response_cls=response_cls, - errors_handler=errors_handler, + errors_handler_cls=errors_handler_cls, **unmarshaller_kwargs, ) - def process_request(self, req: Request, resp: Response) -> None: # type: ignore - openapi_req = self._get_openapi_request(req) - req.context.openapi = super().process_request(openapi_req) - if req.context.openapi.errors: - return self._handle_request_errors(req, resp, req.context.openapi) + def process_request(self, req: Request, resp: Response) -> None: + valid_handler = self.valid_request_handler_cls(req, resp) + errors_handler = self.errors_handler_cls(req, resp) + self.handle_request(req, valid_handler, errors_handler) - def process_response( # type: ignore + def process_response( self, req: Request, resp: Response, resource: Any, req_succeeded: bool ) -> None: - if self.response_cls is None: - return resp - openapi_req = self._get_openapi_request(req) - openapi_resp = self._get_openapi_response(resp) - resp.context.openapi = super().process_response( - openapi_req, openapi_resp - ) - if resp.context.openapi.errors: - return self._handle_response_errors( - req, resp, resp.context.openapi - ) - - def _handle_request_errors( - self, - req: Request, - resp: Response, - request_result: RequestUnmarshalResult, - ) -> None: - return self.errors_handler.handle(req, resp, request_result.errors) - - def _handle_response_errors( - self, - req: Request, - resp: Response, - response_result: ResponseUnmarshalResult, - ) -> None: - return self.errors_handler.handle(req, resp, response_result.errors) + errors_handler = self.errors_handler_cls(req, resp) + self.handle_response(req, resp, errors_handler) def _get_openapi_request(self, request: Request) -> FalconOpenAPIRequest: return self.request_cls(request) @@ -110,3 +90,6 @@ def _get_openapi_response( ) -> FalconOpenAPIResponse: assert self.response_cls is not None return self.response_cls(response) + + def _validate_response(self) -> bool: + return self.response_cls is not None diff --git a/openapi_core/contrib/flask/decorators.py b/openapi_core/contrib/flask/decorators.py index 7c71ad24..3756f058 100644 --- a/openapi_core/contrib/flask/decorators.py +++ b/openapi_core/contrib/flask/decorators.py @@ -6,25 +6,26 @@ from typing import Type from flask.globals import request -from flask.helpers import make_response from flask.wrappers import Request from flask.wrappers import Response from openapi_core.contrib.flask.handlers import FlaskOpenAPIErrorsHandler +from openapi_core.contrib.flask.handlers import FlaskOpenAPIValidRequestHandler from openapi_core.contrib.flask.providers import FlaskRequestProvider from openapi_core.contrib.flask.requests import FlaskOpenAPIRequest from openapi_core.contrib.flask.responses import FlaskOpenAPIResponse from openapi_core.spec import Spec from openapi_core.unmarshalling.processors import UnmarshallingProcessor -from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult from openapi_core.unmarshalling.request.types import RequestUnmarshallerType -from openapi_core.unmarshalling.response.datatypes import ( - ResponseUnmarshalResult, -) from openapi_core.unmarshalling.response.types import ResponseUnmarshallerType -class FlaskOpenAPIViewDecorator(UnmarshallingProcessor): +class FlaskOpenAPIViewDecorator(UnmarshallingProcessor[Request, Response]): + valid_request_handler_cls = FlaskOpenAPIValidRequestHandler + errors_handler_cls: Type[ + FlaskOpenAPIErrorsHandler + ] = FlaskOpenAPIErrorsHandler + def __init__( self, spec: Spec, @@ -35,7 +36,7 @@ def __init__( Type[FlaskOpenAPIResponse] ] = FlaskOpenAPIResponse, request_provider: Type[FlaskRequestProvider] = FlaskRequestProvider, - openapi_errors_handler: Type[ + errors_handler_cls: Type[ FlaskOpenAPIErrorsHandler ] = FlaskOpenAPIErrorsHandler, **unmarshaller_kwargs: Any, @@ -49,53 +50,23 @@ def __init__( self.request_cls = request_cls self.response_cls = response_cls self.request_provider = request_provider - self.openapi_errors_handler = openapi_errors_handler + self.errors_handler_cls = errors_handler_cls def __call__(self, view: Callable[..., Any]) -> Callable[..., Any]: @wraps(view) def decorated(*args: Any, **kwargs: Any) -> Response: request = self._get_request() - openapi_request = self._get_openapi_request(request) - request_result = self.process_request(openapi_request) - if request_result.errors: - return self._handle_request_errors(request_result) - response = self._handle_request_view( - request_result, view, *args, **kwargs + valid_request_handler = self.valid_request_handler_cls( + request, view, *args, **kwargs ) - if self.response_cls is None: - return response - openapi_response = self._get_openapi_response(response) - response_result = self.process_response( - openapi_request, openapi_response + errors_handler = self.errors_handler_cls() + response = self.handle_request( + request, valid_request_handler, errors_handler ) - if response_result.errors: - return self._handle_response_errors(response_result) - return response + return self.handle_response(request, response, errors_handler) return decorated - def _handle_request_view( - self, - request_result: RequestUnmarshalResult, - view: Callable[[Any], Response], - *args: Any, - **kwargs: Any, - ) -> Response: - request = self._get_request() - request.openapi = request_result # type: ignore - rv = view(*args, **kwargs) - return make_response(rv) - - def _handle_request_errors( - self, request_result: RequestUnmarshalResult - ) -> Response: - return self.openapi_errors_handler.handle(request_result.errors) - - def _handle_response_errors( - self, response_result: ResponseUnmarshalResult - ) -> Response: - return self.openapi_errors_handler.handle(response_result.errors) - def _get_request(self) -> Request: return request @@ -108,6 +79,9 @@ def _get_openapi_response( assert self.response_cls is not None return self.response_cls(response) + def _validate_response(self) -> bool: + return self.response_cls is not None + @classmethod def from_spec( cls, @@ -117,7 +91,7 @@ def from_spec( request_cls: Type[FlaskOpenAPIRequest] = FlaskOpenAPIRequest, response_cls: Type[FlaskOpenAPIResponse] = FlaskOpenAPIResponse, request_provider: Type[FlaskRequestProvider] = FlaskRequestProvider, - openapi_errors_handler: Type[ + errors_handler_cls: Type[ FlaskOpenAPIErrorsHandler ] = FlaskOpenAPIErrorsHandler, **unmarshaller_kwargs: Any, @@ -129,6 +103,6 @@ def from_spec( request_cls=request_cls, response_cls=response_cls, request_provider=request_provider, - openapi_errors_handler=openapi_errors_handler, + errors_handler_cls=errors_handler_cls, **unmarshaller_kwargs, ) diff --git a/openapi_core/contrib/flask/handlers.py b/openapi_core/contrib/flask/handlers.py index 3e50ad76..e1a20fc4 100644 --- a/openapi_core/contrib/flask/handlers.py +++ b/openapi_core/contrib/flask/handlers.py @@ -1,11 +1,14 @@ """OpenAPI core contrib flask handlers module""" from typing import Any +from typing import Callable from typing import Dict from typing import Iterable from typing import Type from flask.globals import current_app +from flask.helpers import make_response from flask.json import dumps +from flask.wrappers import Request from flask.wrappers import Response from openapi_core.templating.media_types.exceptions import MediaTypeNotFound @@ -13,6 +16,7 @@ from openapi_core.templating.paths.exceptions import PathNotFound from openapi_core.templating.paths.exceptions import ServerNotFound from openapi_core.templating.security.exceptions import SecurityNotFound +from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult class FlaskOpenAPIErrorsHandler: @@ -24,13 +28,12 @@ class FlaskOpenAPIErrorsHandler: MediaTypeNotFound: 415, } - @classmethod - def handle(cls, errors: Iterable[BaseException]) -> Response: - data_errors = [cls.format_openapi_error(err) for err in errors] + def __call__(self, errors: Iterable[Exception]) -> Response: + data_errors = [self.format_openapi_error(err) for err in errors] data = { "errors": data_errors, } - data_error_max = max(data_errors, key=cls.get_error_status) + data_error_max = max(data_errors, key=self.get_error_status) status = data_error_max["status"] return current_app.response_class( dumps(data), status=status, mimetype="application/json" @@ -49,3 +52,24 @@ def format_openapi_error(cls, error: BaseException) -> Dict[str, Any]: @classmethod def get_error_status(cls, error: Dict[str, Any]) -> int: return int(error["status"]) + + +class FlaskOpenAPIValidRequestHandler: + def __init__( + self, + req: Request, + view: Callable[[Any], Response], + *view_args: Any, + **view_kwargs: Any, + ): + self.req = req + self.view = view + self.view_args = view_args + self.view_kwargs = view_kwargs + + def __call__( + self, request_unmarshal_result: RequestUnmarshalResult + ) -> Response: + self.req.openapi = request_unmarshal_result # type: ignore + rv = self.view(*self.view_args, **self.view_kwargs) + return make_response(rv) diff --git a/openapi_core/contrib/flask/views.py b/openapi_core/contrib/flask/views.py index 71e1afe7..39bef43c 100644 --- a/openapi_core/contrib/flask/views.py +++ b/openapi_core/contrib/flask/views.py @@ -15,13 +15,14 @@ class FlaskOpenAPIView(MethodView): def __init__(self, spec: Spec, **unmarshaller_kwargs: Any): super().__init__() - self.spec = spec self.decorator = FlaskOpenAPIViewDecorator( - self.spec, - openapi_errors_handler=self.openapi_errors_handler, + spec, + errors_handler_cls=self.openapi_errors_handler, **unmarshaller_kwargs, ) def dispatch_request(self, *args: Any, **kwargs: Any) -> Any: - return self.decorator(super().dispatch_request)(*args, **kwargs) + response = self.decorator(super().dispatch_request)(*args, **kwargs) + + return response diff --git a/openapi_core/typing.py b/openapi_core/typing.py new file mode 100644 index 00000000..78b66b24 --- /dev/null +++ b/openapi_core/typing.py @@ -0,0 +1,13 @@ +from typing import Callable +from typing import Iterable +from typing import TypeVar + +from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult + +#: The type of request within an integration. +RequestType = TypeVar("RequestType") +#: The type of response within an integration. +ResponseType = TypeVar("ResponseType") + +ErrorsHandlerCallable = Callable[[Iterable[Exception]], ResponseType] +ValidRequestHandlerCallable = Callable[[RequestUnmarshalResult], ResponseType] diff --git a/openapi_core/unmarshalling/processors.py b/openapi_core/unmarshalling/processors.py index 5a1458c1..fcec7c26 100644 --- a/openapi_core/unmarshalling/processors.py +++ b/openapi_core/unmarshalling/processors.py @@ -1,21 +1,27 @@ """OpenAPI core unmarshalling processors module""" from typing import Any +from typing import Generic from typing import Optional -from typing import Type from openapi_core.protocols import Request from openapi_core.protocols import Response from openapi_core.shortcuts import get_classes from openapi_core.spec import Spec -from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult +from openapi_core.typing import ErrorsHandlerCallable +from openapi_core.typing import RequestType +from openapi_core.typing import ResponseType +from openapi_core.typing import ValidRequestHandlerCallable +from openapi_core.unmarshalling.request.processors import ( + RequestUnmarshallingProcessor, +) from openapi_core.unmarshalling.request.types import RequestUnmarshallerType -from openapi_core.unmarshalling.response.datatypes import ( - ResponseUnmarshalResult, +from openapi_core.unmarshalling.response.processors import ( + ResponseUnmarshallingProcessor, ) from openapi_core.unmarshalling.response.types import ResponseUnmarshallerType -class UnmarshallingProcessor: +class UnmarshallingProcessor(Generic[RequestType, ResponseType]): def __init__( self, spec: Spec, @@ -23,27 +29,63 @@ def __init__( response_unmarshaller_cls: Optional[ResponseUnmarshallerType] = None, **unmarshaller_kwargs: Any, ): - self.spec = spec if ( request_unmarshaller_cls is None or response_unmarshaller_cls is None ): - classes = get_classes(self.spec) + classes = get_classes(spec) if request_unmarshaller_cls is None: request_unmarshaller_cls = classes.request_unmarshaller_cls if response_unmarshaller_cls is None: response_unmarshaller_cls = classes.response_unmarshaller_cls - self.request_unmarshaller = request_unmarshaller_cls( - self.spec, **unmarshaller_kwargs + + self.request_processor = RequestUnmarshallingProcessor( + spec, + request_unmarshaller_cls, + **unmarshaller_kwargs, ) - self.response_unmarshaller = response_unmarshaller_cls( - self.spec, **unmarshaller_kwargs + self.response_processor = ResponseUnmarshallingProcessor( + spec, + response_unmarshaller_cls, + **unmarshaller_kwargs, ) - def process_request(self, request: Request) -> RequestUnmarshalResult: - return self.request_unmarshaller.unmarshal(request) + def _get_openapi_request(self, request: RequestType) -> Request: + raise NotImplementedError + + def _get_openapi_response(self, response: ResponseType) -> Response: + raise NotImplementedError - def process_response( - self, request: Request, response: Response - ) -> ResponseUnmarshalResult: - return self.response_unmarshaller.unmarshal(request, response) + def _validate_response(self) -> bool: + raise NotImplementedError + + def handle_request( + self, + request: RequestType, + valid_handler: ValidRequestHandlerCallable[ResponseType], + errors_handler: ErrorsHandlerCallable[ResponseType], + ) -> ResponseType: + openapi_request = self._get_openapi_request(request) + request_unmarshal_result = self.request_processor.process( + openapi_request + ) + if request_unmarshal_result.errors: + return errors_handler(request_unmarshal_result.errors) + return valid_handler(request_unmarshal_result) + + def handle_response( + self, + request: RequestType, + response: ResponseType, + errors_handler: ErrorsHandlerCallable[ResponseType], + ) -> ResponseType: + if not self._validate_response(): + return response + openapi_request = self._get_openapi_request(request) + openapi_response = self._get_openapi_response(response) + response_unmarshal_result = self.response_processor.process( + openapi_request, openapi_response + ) + if response_unmarshal_result.errors: + return errors_handler(response_unmarshal_result.errors) + return response diff --git a/openapi_core/unmarshalling/request/processors.py b/openapi_core/unmarshalling/request/processors.py new file mode 100644 index 00000000..1719605e --- /dev/null +++ b/openapi_core/unmarshalling/request/processors.py @@ -0,0 +1,33 @@ +from typing import Any +from typing import Optional + +from openapi_core.protocols import Request +from openapi_core.spec import Spec +from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult +from openapi_core.unmarshalling.request.protocols import RequestUnmarshaller +from openapi_core.unmarshalling.request.types import RequestUnmarshallerType + + +class RequestUnmarshallingProcessor: + def __init__( + self, + spec: Spec, + request_unmarshaller_cls: RequestUnmarshallerType, + **unmarshaller_kwargs: Any + ) -> None: + self.spec = spec + self.request_unmarshaller_cls = request_unmarshaller_cls + self.unmarshaller_kwargs = unmarshaller_kwargs + + self._request_unmarshaller_cached: Optional[RequestUnmarshaller] = None + + @property + def request_unmarshaller(self) -> RequestUnmarshaller: + if self._request_unmarshaller_cached is None: + self._request_unmarshaller_cached = self.request_unmarshaller_cls( + self.spec, **self.unmarshaller_kwargs + ) + return self._request_unmarshaller_cached + + def process(self, request: Request) -> RequestUnmarshalResult: + return self.request_unmarshaller.unmarshal(request) diff --git a/openapi_core/unmarshalling/response/processors.py b/openapi_core/unmarshalling/response/processors.py new file mode 100644 index 00000000..517af232 --- /dev/null +++ b/openapi_core/unmarshalling/response/processors.py @@ -0,0 +1,42 @@ +from typing import Any +from typing import Optional + +from openapi_core.protocols import Request +from openapi_core.protocols import Response +from openapi_core.spec import Spec +from openapi_core.unmarshalling.response.datatypes import ( + ResponseUnmarshalResult, +) +from openapi_core.unmarshalling.response.protocols import ResponseUnmarshaller +from openapi_core.unmarshalling.response.types import ResponseUnmarshallerType + + +class ResponseUnmarshallingProcessor: + def __init__( + self, + spec: Spec, + response_unmarshaller_cls: ResponseUnmarshallerType, + **unmarshaller_kwargs: Any + ) -> None: + self.spec = spec + self.response_unmarshaller_cls = response_unmarshaller_cls + self.unmarshaller_kwargs = unmarshaller_kwargs + + self._response_unmarshaller_cached: Optional[ + ResponseUnmarshaller + ] = None + + @property + def response_unmarshaller(self) -> ResponseUnmarshaller: + if self._response_unmarshaller_cached is None: + self._response_unmarshaller_cached = ( + self.response_unmarshaller_cls( + self.spec, **self.unmarshaller_kwargs + ) + ) + return self._response_unmarshaller_cached + + def process( + self, request: Request, response: Response + ) -> ResponseUnmarshalResult: + return self.response_unmarshaller.unmarshal(request, response)