Skip to content

feat(apigateway): add exception_handler support #898

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 16, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat(event-handler): Add exception_handler support
  • Loading branch information
Michael Brewer committed Dec 15, 2021
commit f78c0d6bb696bd8cacd5f52166fd405ae05e9fcb
21 changes: 19 additions & 2 deletions aws_lambda_powertools/event_handler/api_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from enum import Enum
from functools import partial
from http import HTTPStatus
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union

from aws_lambda_powertools.event_handler import content_types
from aws_lambda_powertools.event_handler.exceptions import ServiceError
Expand Down Expand Up @@ -435,6 +435,7 @@ def __init__(
self._proxy_type = proxy_type
self._routes: List[Route] = []
self._route_keys: List[str] = []
self._exception_handlers: Dict[Type, Callable] = {}
self._cors = cors
self._cors_enabled: bool = cors is not None
self._cors_methods: Set[str] = {"OPTIONS"}
Expand Down Expand Up @@ -618,7 +619,11 @@ def _call_route(self, route: Route, args: Dict[str, str]) -> ResponseBuilder:
),
route,
)
except Exception:
except Exception as exc:
handler = self._lookup_exception_handler(exc)
if handler:
return ResponseBuilder(handler(exc))

if self._debug:
# If the user has turned on debug mode,
# we'll let the original exception propagate so
Expand Down Expand Up @@ -676,6 +681,18 @@ def include_router(self, router: "Router", prefix: Optional[str] = None) -> None

self.route(*route)(func)

def exception_handler(self, exception):
def register_exception_handler(func: Callable):
self._exception_handlers[exception] = func

return register_exception_handler

def _lookup_exception_handler(self, exc: Exception) -> Optional[Callable]:
for cls in type(exc).__mro__:
if cls in self._exception_handlers:
return self._exception_handlers[cls]
return None


class Router(BaseRouter):
"""Router helper class to allow splitting ApiGatewayResolver into multiple files"""
Expand Down
29 changes: 28 additions & 1 deletion tests/functional/event_handler/test_api_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def patch_func():
def handler(event, context):
return app.resolve(event, context)

# Also check check the route configurations
# Also check the route configurations
routes = app._routes
assert len(routes) == 5
for route in routes:
Expand Down Expand Up @@ -1076,3 +1076,30 @@ def foo():

assert result["statusCode"] == 200
assert result["headers"]["Content-Type"] == content_types.APPLICATION_JSON


def test_exception_handler():
# GIVEN a resolver with an exception handler defined for ValueError
app = ApiGatewayResolver(proxy_type=ProxyEventType.APIGatewayProxyEvent)

@app.exception_handler(ValueError)
def handle_value_error(ex: ValueError):
print(f"request path is '{app.current_event.path}'")
return Response(
status_code=418,
content_type=content_types.TEXT_HTML,
body=str(ex),
)

@app.get("/my/path")
def get_lambda() -> Response:
raise ValueError("Foo!")

# WHEN calling the event handler
# AND a ValueError is raised
result = app(LOAD_GW_EVENT, {})

# THEN call the exception_handler
assert result["statusCode"] == 418
assert result["headers"]["Content-Type"] == content_types.TEXT_HTML
assert result["body"] == "Foo!"