diff --git a/aws_lambda_powertools/event_handler/router.py b/aws_lambda_powertools/event_handler/router.py new file mode 100644 index 00000000000..85f2bbbef82 --- /dev/null +++ b/aws_lambda_powertools/event_handler/router.py @@ -0,0 +1,31 @@ +from aws_lambda_powertools.event_handler.api_gateway import Router +from aws_lambda_powertools.utilities.data_classes import ( + ALBEvent, + APIGatewayProxyEvent, + APIGatewayProxyEventV2, + LambdaFunctionUrlEvent, +) + + +class APIGatewayRouter(Router): + """Specialized Router class that exposes current_event as an APIGatewayProxyEvent""" + + current_event: APIGatewayProxyEvent + + +class APIGatewayHttpRouter(Router): + """Specialized Router class that exposes current_event as an APIGatewayProxyEventV2""" + + current_event: APIGatewayProxyEventV2 + + +class LambdaFunctionUrlRouter(Router): + """Specialized Router class that exposes current_event as a LambdaFunctionUrlEvent""" + + current_event: LambdaFunctionUrlEvent + + +class ALBRouter(Router): + """Specialized Router class that exposes current_event as an ALBEvent""" + + current_event: ALBEvent diff --git a/docs/core/event_handler/api_gateway.md b/docs/core/event_handler/api_gateway.md index ca092e30c04..cee848c24c3 100644 --- a/docs/core/event_handler/api_gateway.md +++ b/docs/core/event_handler/api_gateway.md @@ -459,6 +459,21 @@ When necessary, you can set a prefix when including a router object. This means --8<-- "examples/event_handler_rest/src/split_route_prefix_module.py" ``` +#### Specialized router types + +You can use specialized router classes according to the type of event that you are resolving. This way you'll get type hints from your IDE as you access the `current_event` property. + +| Router | Resolver | `current_event` type | +|-------------------------|---------------------------|------------------------| +| APIGatewayRouter | APIGatewayRestResolver | APIGatewayProxyEvent | +| APIGatewayHttpRouter | APIGatewayHttpResolver | APIGatewayProxyEventV2 | +| ALBRouter | ALBResolver | ALBEvent | +| LambdaFunctionUrlRouter | LambdaFunctionUrlResolver | LambdaFunctionUrlEvent | + +```python hl_lines="1 5 9" +--8<-- "examples/event_handler_rest/src/split_route_specialized_router.py" +``` + #### Sharing contextual data You can use `append_context` when you want to share data between your App and Router instances. Any data you share will be available via the `context` dictionary available in your App or Router context. diff --git a/examples/event_handler_rest/src/split_route_specialized_router.py b/examples/event_handler_rest/src/split_route_specialized_router.py new file mode 100644 index 00000000000..f9002be4209 --- /dev/null +++ b/examples/event_handler_rest/src/split_route_specialized_router.py @@ -0,0 +1,20 @@ +from aws_lambda_powertools.event_handler import APIGatewayRestResolver +from aws_lambda_powertools.event_handler.router import APIGatewayRouter + +app = APIGatewayRestResolver() +router = APIGatewayRouter() + + +@router.get("/me") +def get_self(): + # router.current_event is a APIGatewayProxyEvent + account_id = router.current_event.request_context.account_id + + return {"account_id": account_id} + + +app.include_router(router) + + +def lambda_handler(event, context): + return app.resolve(event, context) diff --git a/tests/functional/event_handler/test_router.py b/tests/functional/event_handler/test_router.py new file mode 100644 index 00000000000..d96f5035114 --- /dev/null +++ b/tests/functional/event_handler/test_router.py @@ -0,0 +1,76 @@ +from aws_lambda_powertools.event_handler import ( + ALBResolver, + APIGatewayHttpResolver, + APIGatewayRestResolver, + LambdaFunctionUrlResolver, + Response, +) +from aws_lambda_powertools.event_handler.router import ( + ALBRouter, + APIGatewayHttpRouter, + APIGatewayRouter, + LambdaFunctionUrlRouter, +) +from aws_lambda_powertools.utilities.data_classes import ( + ALBEvent, + APIGatewayProxyEvent, + APIGatewayProxyEventV2, + LambdaFunctionUrlEvent, +) +from tests.functional.utils import load_event + + +def test_alb_router_event_type(): + app = ALBResolver() + router = ALBRouter() + + @router.route(rule="/lambda", method=["GET"]) + def foo(): + assert type(router.current_event) is ALBEvent + return Response(status_code=200, body="routed") + + app.include_router(router) + result = app(load_event("albEvent.json"), {}) + assert result["body"] == "routed" + + +def test_apigateway_router_event_type(): + app = APIGatewayRestResolver() + router = APIGatewayRouter() + + @router.route(rule="/my/path", method=["GET"]) + def foo(): + assert type(router.current_event) is APIGatewayProxyEvent + return Response(status_code=200, body="routed") + + app.include_router(router) + result = app(load_event("apiGatewayProxyEvent.json"), {}) + assert result["body"] == "routed" + + +def test_apigatewayhttp_router_event_type(): + app = APIGatewayHttpResolver() + router = APIGatewayHttpRouter() + + @router.route(rule="/my/path", method=["POST"]) + def foo(): + assert type(router.current_event) is APIGatewayProxyEventV2 + return Response(status_code=200, body="routed") + + app.include_router(router) + result = app(load_event("apiGatewayProxyV2Event.json"), {}) + assert result["body"] == "routed" + + +def test_lambda_function_url_router_event_type(): + app = LambdaFunctionUrlResolver() + router = LambdaFunctionUrlRouter() + + @router.route(rule="/", method=["GET"]) + def foo(): + assert type(router.current_event) is LambdaFunctionUrlEvent + return Response(status_code=200, body="routed") + + app.include_router(router) + result = app(load_event("lambdaFunctionUrlEvent.json"), {}) + assert result["body"] == "routed"