Skip to content

Commit ebb10ea

Browse files
fix(event_handler): allow responses and metadata when using Router (#3514)
Co-authored-by: Heitor Lessa <[email protected]>
1 parent 5dc225b commit ebb10ea

File tree

3 files changed

+52
-4
lines changed

3 files changed

+52
-4
lines changed

aws_lambda_powertools/event_handler/api_gateway.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
validation_error_definition,
4141
validation_error_response_definition,
4242
)
43+
from aws_lambda_powertools.event_handler.util import _FrozenDict
4344
from aws_lambda_powertools.shared.cookies import Cookie
4445
from aws_lambda_powertools.shared.functions import powertools_dev_is_set
4546
from aws_lambda_powertools.shared.json_encoder import Encoder
@@ -2130,8 +2131,10 @@ def route(
21302131
middlewares: Optional[List[Callable[..., Any]]] = None,
21312132
):
21322133
def register_route(func: Callable):
2133-
# Convert methods to tuple. It needs to be hashable as its part of the self._routes dict key
2134+
# All dict keys needs to be hashable. So we'll need to do some conversions:
21342135
methods = (method,) if isinstance(method, str) else tuple(method)
2136+
frozen_responses = _FrozenDict(responses) if responses else None
2137+
frozen_tags = frozenset(tags) if tags else None
21352138

21362139
route_key = (
21372140
rule,
@@ -2141,9 +2144,9 @@ def register_route(func: Callable):
21412144
cache_control,
21422145
summary,
21432146
description,
2144-
responses,
2147+
frozen_responses,
21452148
response_description,
2146-
tags,
2149+
frozen_tags,
21472150
operation_id,
21482151
include_in_schema,
21492152
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class _FrozenDict(dict):
2+
"""
3+
A dictionary that can be used as a key in another dictionary.
4+
5+
This is needed because the default dict implementation is not hashable.
6+
The only usage for this right now is to store dicts as part of the Router key.
7+
The implementation only takes into consideration the keys of the dictionary.
8+
9+
MAINTENANCE: this is a temporary solution until we refactor the route key into a class.
10+
"""
11+
12+
def __hash__(self):
13+
return hash(frozenset(self.keys()))

tests/functional/event_handler/test_openapi_params.py

+33-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from pydantic import BaseModel
66

7-
from aws_lambda_powertools.event_handler.api_gateway import APIGatewayRestResolver, Response
7+
from aws_lambda_powertools.event_handler.api_gateway import APIGatewayRestResolver, Response, Router
88
from aws_lambda_powertools.event_handler.openapi.models import (
99
Example,
1010
Parameter,
@@ -392,6 +392,38 @@ def secret():
392392
assert len(schema.paths.keys()) == 0
393393

394394

395+
def test_openapi_with_router_response():
396+
router = Router()
397+
398+
@router.put("/example-resource", responses={200: {"description": "Custom response"}})
399+
def handler():
400+
pass
401+
402+
app = APIGatewayRestResolver(enable_validation=True)
403+
app.include_router(router)
404+
405+
schema = app.get_openapi_schema()
406+
put = schema.paths["/example-resource"].put
407+
assert 200 in put.responses.keys()
408+
assert put.responses[200].description == "Custom response"
409+
410+
411+
def test_openapi_with_router_tags():
412+
router = Router()
413+
414+
@router.put("/example-resource", tags=["Example"])
415+
def handler():
416+
pass
417+
418+
app = APIGatewayRestResolver(enable_validation=True)
419+
app.include_router(router)
420+
421+
schema = app.get_openapi_schema()
422+
tags = schema.paths["/example-resource"].put.tags
423+
assert len(tags) == 1
424+
assert tags[0].name == "Example"
425+
426+
395427
def test_create_header():
396428
header = _Header(convert_underscores=True)
397429
assert header.convert_underscores is True

0 commit comments

Comments
 (0)