Skip to content

Commit 4f7bebb

Browse files
theoutsider24Matthew Coylerubenfonseca
authored
feat(event_handlers) Add support for returning dict, status-code tuples from resolvers (#1853)
Co-authored-by: Matthew Coyle <[email protected]> Co-authored-by: Rúben Fonseca <[email protected]>
1 parent 9ccc86e commit 4f7bebb

File tree

4 files changed

+70
-4
lines changed

4 files changed

+70
-4
lines changed

aws_lambda_powertools/event_handler/api_gateway.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -728,21 +728,26 @@ def _call_exception_handler(self, exp: Exception, route: Route) -> Optional[Resp
728728

729729
return None
730730

731-
def _to_response(self, result: Union[Dict, Response]) -> Response:
731+
def _to_response(self, result: Union[Dict, Tuple, Response]) -> Response:
732732
"""Convert the route's result to a Response
733733
734-
2 main result types are supported:
734+
3 main result types are supported:
735735
736736
- Dict[str, Any]: Rest api response with just the Dict to json stringify and content-type is set to
737737
application/json
738+
- Tuple[dict, int]: Same dict handling as above but with the option of including a status code
738739
- Response: returned as is, and allows for more flexibility
739740
"""
741+
status_code = HTTPStatus.OK
740742
if isinstance(result, Response):
741743
return result
744+
elif isinstance(result, tuple) and len(result) == 2:
745+
# Unpack result dict and status code from tuple
746+
result, status_code = result
742747

743748
logger.debug("Simple response detected, serializing return before constructing final response")
744749
return Response(
745-
status_code=200,
750+
status_code=status_code,
746751
content_type=content_types.APPLICATION_JSON,
747752
body=self._json_dump(result),
748753
)

docs/core/event_handler/api_gateway.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,12 @@ A resolver will handle request resolution, including [one or more routers](#spli
4545
For resolvers, we provide: `APIGatewayRestResolver`, `APIGatewayHttpResolver`, `ALBResolver`, and `LambdaFunctionUrlResolver`. From here on, we will default to `APIGatewayRestResolver` across examples.
4646

4747
???+ info "Auto-serialization"
48-
We serialize `Dict` responses as JSON, trim whitespace for compact responses, and set content-type to `application/json`.
48+
We serialize `Dict` responses as JSON, trim whitespace for compact responses, set content-type to `application/json`, and
49+
return a 200 OK HTTP status. You can optionally set a different HTTP status code as the second argument of the tuple:
50+
51+
```python hl_lines="15 16"
52+
--8<-- "examples/event_handler_rest/src/getting_started_return_tuple.py"
53+
```
4954

5055
#### API Gateway REST API
5156

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import requests
2+
from requests import Response
3+
4+
from aws_lambda_powertools.event_handler import ALBResolver
5+
from aws_lambda_powertools.utilities.typing import LambdaContext
6+
7+
app = ALBResolver()
8+
9+
10+
@app.post("/todo")
11+
def create_todo():
12+
data: dict = app.current_event.json_body
13+
todo: Response = requests.post("https://jsonplaceholder.typicode.com/todos", data=data)
14+
15+
# Returns the created todo object, with a HTTP 201 Created status
16+
return {"todo": todo.json()}, 201
17+
18+
19+
def lambda_handler(event: dict, context: LambdaContext) -> dict:
20+
return app.resolve(event, context)

tests/functional/event_handler/test_api_gateway.py

+36
Original file line numberDiff line numberDiff line change
@@ -1579,3 +1579,39 @@ def get_lambda() -> Response:
15791579
# AND set the current_event type as APIGatewayProxyEvent
15801580
assert result["statusCode"] == 200
15811581
assert result2["statusCode"] == 200
1582+
1583+
1584+
def test_dict_response():
1585+
# GIVEN a dict is returned
1586+
app = ApiGatewayResolver()
1587+
1588+
@app.get("/lambda")
1589+
def get_message():
1590+
return {"message": "success"}
1591+
1592+
# WHEN calling handler
1593+
response = app({"httpMethod": "GET", "path": "/lambda"}, None)
1594+
1595+
# THEN the body is correctly formatted, the status code is 200 and the content type is json
1596+
assert response["statusCode"] == 200
1597+
assert response["multiValueHeaders"]["Content-Type"] == [content_types.APPLICATION_JSON]
1598+
response_body = json.loads(response["body"])
1599+
assert response_body["message"] == "success"
1600+
1601+
1602+
def test_dict_response_with_status_code():
1603+
# GIVEN a dict is returned with a status code
1604+
app = ApiGatewayResolver()
1605+
1606+
@app.get("/lambda")
1607+
def get_message():
1608+
return {"message": "success"}, 201
1609+
1610+
# WHEN calling handler
1611+
response = app({"httpMethod": "GET", "path": "/lambda"}, None)
1612+
1613+
# THEN the body is correctly formatted, the status code is 201 and the content type is json
1614+
assert response["statusCode"] == 201
1615+
assert response["multiValueHeaders"]["Content-Type"] == [content_types.APPLICATION_JSON]
1616+
response_body = json.loads(response["body"])
1617+
assert response_body["message"] == "success"

0 commit comments

Comments
 (0)