Skip to content

Commit 2063c21

Browse files
fix: small changes
1 parent 5e89827 commit 2063c21

3 files changed

Lines changed: 219 additions & 74 deletions

File tree

aws_lambda_powertools/event_handler/middlewares/async_utils.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,11 @@ def run_middleware() -> None:
106106

107107
return middleware_result_holder[0]
108108

109+
109110
async def _registered_api_adapter_async(
110-
app: "ApiGatewayResolver",
111+
app: ApiGatewayResolver,
111112
next_middleware: Callable[..., Any],
112-
) -> "dict | tuple | Response | BedrockResponse":
113+
) -> dict | tuple | Response | BedrockResponse:
113114
"""
114115
Async version of _registered_api_adapter.
115116
@@ -138,13 +139,15 @@ async def _registered_api_adapter_async(
138139
if route is not None:
139140
if not route.request_param_name_checked:
140141
from aws_lambda_powertools.event_handler.api_gateway import _find_request_param_name
142+
141143
route.request_param_name = _find_request_param_name(next_middleware)
142144
route.request_param_name_checked = True
143145
if route.request_param_name:
144146
route_args = {**route_args, route.request_param_name: app.request}
145147

146148
if route.has_dependencies:
147149
from aws_lambda_powertools.event_handler.depends import build_dependency_tree, solve_dependencies
150+
148151
dep_values = solve_dependencies(
149152
dependant=build_dependency_tree(route.func),
150153
request=app.request,

aws_lambda_powertools/event_handler/test_registered_api_adapter_async.py

Lines changed: 0 additions & 72 deletions
This file was deleted.
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
from __future__ import annotations
2+
3+
import asyncio
4+
from typing import cast
5+
6+
import pytest
7+
8+
from aws_lambda_powertools.event_handler import content_types
9+
from aws_lambda_powertools.event_handler.api_gateway import (
10+
APIGatewayHttpResolver,
11+
ApiGatewayResolver,
12+
APIGatewayRestResolver,
13+
BaseRouter,
14+
ProxyEventType,
15+
Response,
16+
)
17+
from aws_lambda_powertools.event_handler.middlewares.async_utils import _registered_api_adapter_async
18+
from tests.functional.utils import load_event
19+
20+
API_REST_EVENT = load_event("apiGatewayProxyEvent.json")
21+
API_RESTV2_EVENT = load_event("apiGatewayProxyV2Event_GET.json")
22+
23+
24+
def _setup_resolver_context(app: ApiGatewayResolver, event: dict) -> None:
25+
"""Populate the resolver context the same way resolve() does, without calling the full chain."""
26+
BaseRouter.current_event = app._to_proxy_event(cast(dict, event))
27+
BaseRouter.lambda_context = {}
28+
29+
30+
@pytest.mark.parametrize(
31+
"app, event",
32+
[
33+
(ApiGatewayResolver(proxy_type=ProxyEventType.APIGatewayProxyEvent), API_REST_EVENT),
34+
(APIGatewayRestResolver(), API_REST_EVENT),
35+
(APIGatewayHttpResolver(), API_RESTV2_EVENT),
36+
],
37+
)
38+
def test_sync_handler_returns_response(app: ApiGatewayResolver, event):
39+
# GIVEN a sync route handler
40+
@app.get("/my/path")
41+
def get_lambda():
42+
return Response(200, content_types.TEXT_HTML, "sync response")
43+
44+
# WHEN resolving the event through the normal chain
45+
result = app(event, {})
46+
47+
# THEN the sync handler is called and returns correctly
48+
assert result["statusCode"] == 200
49+
assert result["body"] == "sync response"
50+
51+
52+
@pytest.mark.parametrize(
53+
"app, event",
54+
[
55+
(ApiGatewayResolver(proxy_type=ProxyEventType.APIGatewayProxyEvent), API_REST_EVENT),
56+
(APIGatewayRestResolver(), API_REST_EVENT),
57+
(APIGatewayHttpResolver(), API_RESTV2_EVENT),
58+
],
59+
)
60+
def test_async_handler_is_awaited(app: ApiGatewayResolver, event):
61+
# GIVEN an async route handler registered on the resolver
62+
@app.get("/my/path")
63+
async def get_lambda():
64+
return Response(200, content_types.TEXT_HTML, "async response")
65+
66+
# WHEN populating context and calling the async adapter directly
67+
_setup_resolver_context(app, event)
68+
app.append_context(_route_args={})
69+
70+
result = asyncio.get_event_loop().run_until_complete(
71+
_registered_api_adapter_async(app, get_lambda),
72+
)
73+
74+
# THEN the async handler is awaited and returns correctly
75+
assert result.status_code == 200
76+
assert result.body == "async response"
77+
78+
79+
@pytest.mark.parametrize(
80+
"app, event",
81+
[
82+
(ApiGatewayResolver(proxy_type=ProxyEventType.APIGatewayProxyEvent), API_REST_EVENT),
83+
(APIGatewayRestResolver(), API_REST_EVENT),
84+
(APIGatewayHttpResolver(), API_RESTV2_EVENT),
85+
],
86+
)
87+
def test_sync_handler_through_adapter(app: ApiGatewayResolver, event):
88+
# GIVEN a sync route handler
89+
@app.get("/my/path")
90+
def get_lambda():
91+
return Response(200, content_types.TEXT_HTML, "sync via adapter")
92+
93+
# WHEN calling _registered_api_adapter_async with a sync handler
94+
_setup_resolver_context(app, event)
95+
app.append_context(_route_args={})
96+
97+
result = asyncio.get_event_loop().run_until_complete(
98+
_registered_api_adapter_async(app, get_lambda),
99+
)
100+
101+
# THEN sync handler works through the async adapter without issue
102+
assert result.status_code == 200
103+
assert result.body == "sync via adapter"
104+
105+
106+
@pytest.mark.parametrize(
107+
"app, event",
108+
[
109+
(ApiGatewayResolver(proxy_type=ProxyEventType.APIGatewayProxyEvent), API_REST_EVENT),
110+
(APIGatewayRestResolver(), API_REST_EVENT),
111+
(APIGatewayHttpResolver(), API_RESTV2_EVENT),
112+
],
113+
)
114+
def test_adapter_passes_route_args_to_async_handler(app: ApiGatewayResolver, event):
115+
# GIVEN an async handler that expects route arguments
116+
async def get_lambda(name: str):
117+
return Response(200, content_types.TEXT_HTML, name)
118+
119+
# WHEN route_args are set in the context
120+
_setup_resolver_context(app, event)
121+
app.append_context(_route_args={"name": "powertools"})
122+
123+
result = asyncio.get_event_loop().run_until_complete(
124+
_registered_api_adapter_async(app, get_lambda),
125+
)
126+
127+
# THEN the route args are passed to the handler
128+
assert result.status_code == 200
129+
assert result.body == "powertools"
130+
131+
132+
@pytest.mark.parametrize(
133+
"app, event",
134+
[
135+
(ApiGatewayResolver(proxy_type=ProxyEventType.APIGatewayProxyEvent), API_REST_EVENT),
136+
(APIGatewayRestResolver(), API_REST_EVENT),
137+
(APIGatewayHttpResolver(), API_RESTV2_EVENT),
138+
],
139+
)
140+
def test_adapter_passes_route_args_to_sync_handler(app: ApiGatewayResolver, event):
141+
# GIVEN a sync handler that expects route arguments
142+
def get_lambda(name: str):
143+
return Response(200, content_types.TEXT_HTML, name)
144+
145+
# WHEN route_args are set in the context
146+
_setup_resolver_context(app, event)
147+
app.append_context(_route_args={"name": "powertools"})
148+
149+
result = asyncio.get_event_loop().run_until_complete(
150+
_registered_api_adapter_async(app, get_lambda),
151+
)
152+
153+
# THEN the route args are passed to the sync handler
154+
assert result.status_code == 200
155+
assert result.body == "powertools"
156+
157+
158+
def test_adapter_converts_dict_response_from_async_handler():
159+
# GIVEN an async handler that returns a dict (not a Response object)
160+
app = ApiGatewayResolver(proxy_type=ProxyEventType.APIGatewayProxyEvent)
161+
162+
async def get_lambda():
163+
return {"message": "hello"}
164+
165+
# WHEN calling through the async adapter
166+
_setup_resolver_context(app, API_REST_EVENT)
167+
app.append_context(_route_args={})
168+
169+
result = asyncio.get_event_loop().run_until_complete(
170+
_registered_api_adapter_async(app, get_lambda),
171+
)
172+
173+
# THEN _to_response normalizes the dict into a Response object
174+
assert result.status_code == 200
175+
assert result.body is not None
176+
177+
178+
def test_adapter_converts_tuple_response_from_async_handler():
179+
# GIVEN an async handler that returns a (dict, status_code) tuple
180+
app = ApiGatewayResolver(proxy_type=ProxyEventType.APIGatewayProxyEvent)
181+
182+
async def get_lambda():
183+
return {"created": True}, 201
184+
185+
# WHEN calling through the async adapter
186+
_setup_resolver_context(app, API_REST_EVENT)
187+
app.append_context(_route_args={})
188+
189+
result = asyncio.get_event_loop().run_until_complete(
190+
_registered_api_adapter_async(app, get_lambda),
191+
)
192+
193+
# THEN _to_response normalizes the tuple into a Response object
194+
assert result.status_code == 201
195+
196+
197+
def test_adapter_with_no_route_in_context():
198+
# GIVEN a handler and no _route in context
199+
app = ApiGatewayResolver(proxy_type=ProxyEventType.APIGatewayProxyEvent)
200+
201+
async def get_lambda():
202+
return Response(200, content_types.TEXT_HTML, "no route")
203+
204+
# WHEN _route is None in context (default)
205+
_setup_resolver_context(app, API_REST_EVENT)
206+
app.append_context(_route_args={})
207+
208+
result = asyncio.get_event_loop().run_until_complete(
209+
_registered_api_adapter_async(app, get_lambda),
210+
)
211+
212+
# THEN the adapter skips request injection and dependency resolution
213+
assert result.status_code == 200
214+
assert result.body == "no route"

0 commit comments

Comments
 (0)