Skip to content

Commit

Permalink
Add error handlers for AsyncApp (#1640)
Browse files Browse the repository at this point in the history
This PR adds all the needed error handling for the AsyncApp.

Only 5 errors remaining.
  • Loading branch information
RobbeSneyders authored Feb 16, 2023
1 parent 1ee9c25 commit 03d8067
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 20 deletions.
1 change: 0 additions & 1 deletion connexion/apps/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ def add_error_handler(
:param function: Callable that will handle exception.
"""

@abc.abstractmethod
def test_client(self, **kwargs):
"""Creates a test client for this application."""
return TestClient(self, **kwargs)
Expand Down
2 changes: 1 addition & 1 deletion connexion/apps/asynchronous.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,4 @@ def add_url_rule(
def add_error_handler(
self, code_or_exception: t.Union[int, t.Type[Exception]], function: t.Callable
) -> None:
"""TODO: implement"""
self.middleware.add_error_handler(code_or_exception, function)
2 changes: 1 addition & 1 deletion connexion/middleware/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
operation = api.operations[operation_id]
except KeyError as e:
if operation_id is None:
logger.debug("Skipping validation check for operation without id.")
logger.debug("Skipping operation without id.")
await self.app(scope, receive, send)
return
else:
Expand Down
43 changes: 32 additions & 11 deletions connexion/middleware/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import json

import werkzeug.exceptions
from starlette.exceptions import ExceptionMiddleware as StarletteExceptionMiddleware
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.requests import Request as StarletteRequest
from starlette.responses import Response
from starlette.types import Receive, Scope, Send

from connexion.exceptions import ProblemException, problem

Expand All @@ -15,11 +17,9 @@ class ExceptionMiddleware(StarletteExceptionMiddleware):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.add_exception_handler(ProblemException, self.problem_handler)
self.add_exception_handler(Exception, self.common_error_handler)

def problem_handler(self, _, exception: ProblemException):
"""
:type exception: Exception
"""
def problem_handler(self, _request: StarletteRequest, exception: ProblemException):
connexion_response = problem(
status=exception.status,
title=exception.title,
Expand All @@ -37,12 +37,10 @@ def problem_handler(self, _, exception: ProblemException):
headers=connexion_response.headers,
)

def http_exception(self, request: Request, exc: HTTPException) -> Response:
try:
headers = exc.headers # type: ignore
except AttributeError:
# Starlette < 0.19
headers = {}
def http_exception(
self, _request: StarletteRequest, exc: HTTPException
) -> Response:
headers = exc.headers

connexion_response = problem(
title=exc.detail, detail=exc.detail, status=exc.status_code, headers=headers
Expand All @@ -54,3 +52,26 @@ def http_exception(self, request: Request, exc: HTTPException) -> Response:
media_type=connexion_response.mimetype,
headers=connexion_response.headers,
)

def common_error_handler(
self, _request: StarletteRequest, exc: HTTPException
) -> Response:
exception = werkzeug.exceptions.InternalServerError()

response = problem(
title=exception.name,
detail=exception.description,
status=exception.code,
)

return Response(
content=json.dumps(response.body),
status_code=response.status_code,
media_type=response.mimetype,
headers=response.headers,
)

async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
# Needs to be set so starlette router throws exceptions instead of returning error responses
scope["app"] = self
await super().__call__(scope, receive, send)
7 changes: 7 additions & 0 deletions connexion/middleware/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,13 @@ def add_api(
# Api registered on the inner application.
return api

def add_error_handler(
self, code_or_exception: t.Union[int, t.Type[Exception]], function: t.Callable
) -> None:
for app in self.apps:
if isinstance(app, ExceptionMiddleware):
app.add_exception_handler(code_or_exception, function)

def run(self, import_string: str = None, **kwargs):
"""Run the application using uvicorn.
Expand Down
2 changes: 0 additions & 2 deletions connexion/middleware/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,4 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:

_scope.set(scope.copy()) # type: ignore

# Needs to be set so starlette router throws exceptions instead of returning error responses
scope["app"] = self
await self.router(scope, receive, send)
4 changes: 0 additions & 4 deletions tests/api/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ def test_errors(problem_app):
error404 = greeting404.json()
assert error404["type"] == "about:blank"
assert error404["title"] == "Not Found"
assert (
error404["detail"] == "The requested URL was not found on the server. "
"If you entered the URL manually please check your spelling and try again."
)
assert error404["status"] == 404
assert "instance" not in error404

Expand Down

0 comments on commit 03d8067

Please sign in to comment.