Skip to content

Commit b68206f

Browse files
committed
fix: customize the handler methods
1 parent 70a0589 commit b68206f

File tree

3 files changed

+238
-26
lines changed

3 files changed

+238
-26
lines changed

aws_lambda_powertools/event_handler/api_gateway.py

Lines changed: 168 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
# API GW/ALB decode non-safe URI chars; we must support them too
5252
_UNSAFE_URI = r"%<> \[\]{}|^"
5353
_NAMED_GROUP_BOUNDARY_PATTERN = rf"(?P\1[{_SAFE_URI}{_UNSAFE_URI}\\w]+)"
54+
_DEFAULT_OPENAPI_RESPONSE_DESCRIPTION = "Successful Response"
5455
_ROUTE_REGEX = "^{}$"
5556

5657
if TYPE_CHECKING:
@@ -203,9 +204,13 @@ def __init__(
203204
cors: bool,
204205
compress: bool,
205206
cache_control: Optional[str],
206-
middlewares: Optional[List[Callable[..., Response]]],
207+
summary: Optional[str],
207208
description: Optional[str],
209+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]],
210+
response_description: Optional[str],
208211
tags: Optional[List["Tag"]],
212+
operation_id: Optional[str],
213+
middlewares: Optional[List[Callable[..., Response]]],
209214
):
210215
"""
211216
@@ -214,10 +219,10 @@ def __init__(
214219
215220
method: str
216221
The HTTP method, example "GET"
217-
rule: Pattern
218-
The route rule, example "/my/path"
219222
path: str
220223
The path of the route
224+
rule: Pattern
225+
The route rule, example "/my/path"
221226
func: Callable
222227
The route handler function
223228
cors: bool
@@ -226,12 +231,20 @@ def __init__(
226231
Whether or not to enable gzip compression for this route
227232
cache_control: Optional[str]
228233
The cache control header value, example "max-age=3600"
229-
middlewares: Optional[List[Callable[..., Response]]]
230-
The list of route middlewares to be called in order.
234+
summary: Optional[str]
235+
The OpenAPI summary for this route
231236
description: Optional[str]
232237
The OpenAPI description for this route
238+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]]
239+
The OpenAPI responses for this route
240+
response_description: Optional[str]
241+
The OpenAPI response description for this route
233242
tags: Optional[List[Tag]]
234243
The list of OpenAPI tags to be used for this route
244+
operation_id: Optional[str]
245+
The OpenAPI operationId for this route
246+
middlewares: Optional[List[Callable[..., Response]]]
247+
The list of route middlewares to be called in order.
235248
"""
236249
self.method = method.upper()
237250
self.path = path
@@ -241,10 +254,13 @@ def __init__(
241254
self.cors = cors
242255
self.compress = compress
243256
self.cache_control = cache_control
244-
self.middlewares = middlewares or []
257+
self.summary = summary
245258
self.description = description
259+
self.responses = responses
260+
self.response_description = response_description
246261
self.tags = tags or []
247-
self.operation_id = self.method.title() + self.func.__name__.title()
262+
self.middlewares = middlewares or []
263+
self.operation_id = operation_id or (self.method.title() + self.func.__name__.title())
248264

249265
# _middleware_stack_built is used to ensure the middleware stack is only built once.
250266
self._middleware_stack_built = False
@@ -372,7 +388,7 @@ def _get_openapi_path(
372388

373389
responses = operation.setdefault("responses", {})
374390
success_response = responses.setdefault("200", {})
375-
success_response["description"] = "Success"
391+
success_response["description"] = self.response_description or _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION
376392
success_response["content"] = {"application/json": {"schema": {}}}
377393
json_response = success_response["content"].setdefault("application/json", {})
378394

@@ -391,7 +407,7 @@ def _get_openapi_path(
391407
return path, definitions
392408

393409
def _openapi_operation_summary(self) -> str:
394-
return f"{self.method.upper()} {self.path}"
410+
return self.summary or f"{self.method.upper()} {self.path}"
395411

396412
def _openapi_operation_metadata(self, operation_ids: Set[str]) -> Dict[str, Any]:
397413
operation: Dict[str, Any] = {}
@@ -598,8 +614,12 @@ def route(
598614
cors: Optional[bool] = None,
599615
compress: bool = False,
600616
cache_control: Optional[str] = None,
617+
summary: Optional[str] = None,
601618
description: Optional[str] = None,
619+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
620+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
602621
tags: Optional[List["Tag"]] = None,
622+
operation_id: Optional[str] = None,
603623
middlewares: Optional[List[Callable[..., Any]]] = None,
604624
):
605625
raise NotImplementedError()
@@ -651,9 +671,13 @@ def get(
651671
cors: Optional[bool] = None,
652672
compress: bool = False,
653673
cache_control: Optional[str] = None,
654-
middlewares: Optional[List[Callable[..., Any]]] = None,
674+
summary: Optional[str] = None,
655675
description: Optional[str] = None,
676+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
677+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
656678
tags: Optional[List["Tag"]] = None,
679+
operation_id: Optional[str] = None,
680+
middlewares: Optional[List[Callable[..., Any]]] = None,
657681
):
658682
"""Get route decorator with GET `method`
659683
@@ -677,17 +701,34 @@ def lambda_handler(event, context):
677701
return app.resolve(event, context)
678702
```
679703
"""
680-
return self.route(rule, "GET", cors, compress, cache_control, description, tags, middlewares)
704+
return self.route(
705+
rule,
706+
"GET",
707+
cors,
708+
compress,
709+
cache_control,
710+
summary,
711+
description,
712+
responses,
713+
response_description,
714+
tags,
715+
operation_id,
716+
middlewares,
717+
)
681718

682719
def post(
683720
self,
684721
rule: str,
685722
cors: Optional[bool] = None,
686723
compress: bool = False,
687724
cache_control: Optional[str] = None,
688-
middlewares: Optional[List[Callable[..., Any]]] = None,
725+
summary: Optional[str] = None,
689726
description: Optional[str] = None,
727+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
728+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
690729
tags: Optional[List["Tag"]] = None,
730+
operation_id: Optional[str] = None,
731+
middlewares: Optional[List[Callable[..., Any]]] = None,
691732
):
692733
"""Post route decorator with POST `method`
693734
@@ -712,17 +753,34 @@ def lambda_handler(event, context):
712753
return app.resolve(event, context)
713754
```
714755
"""
715-
return self.route(rule, "POST", cors, compress, cache_control, description, tags, middlewares)
756+
return self.route(
757+
rule,
758+
"POST",
759+
cors,
760+
compress,
761+
cache_control,
762+
summary,
763+
description,
764+
responses,
765+
response_description,
766+
tags,
767+
operation_id,
768+
middlewares,
769+
)
716770

717771
def put(
718772
self,
719773
rule: str,
720774
cors: Optional[bool] = None,
721775
compress: bool = False,
722776
cache_control: Optional[str] = None,
723-
middlewares: Optional[List[Callable[..., Any]]] = None,
777+
summary: Optional[str] = None,
724778
description: Optional[str] = None,
779+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
780+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
725781
tags: Optional[List["Tag"]] = None,
782+
operation_id: Optional[str] = None,
783+
middlewares: Optional[List[Callable[..., Any]]] = None,
726784
):
727785
"""Put route decorator with PUT `method`
728786
@@ -747,17 +805,34 @@ def lambda_handler(event, context):
747805
return app.resolve(event, context)
748806
```
749807
"""
750-
return self.route(rule, "PUT", cors, compress, cache_control, description, tags, middlewares)
808+
return self.route(
809+
rule,
810+
"PUT",
811+
cors,
812+
compress,
813+
cache_control,
814+
summary,
815+
description,
816+
responses,
817+
response_description,
818+
tags,
819+
operation_id,
820+
middlewares,
821+
)
751822

752823
def delete(
753824
self,
754825
rule: str,
755826
cors: Optional[bool] = None,
756827
compress: bool = False,
757828
cache_control: Optional[str] = None,
758-
middlewares: Optional[List[Callable[..., Any]]] = None,
829+
summary: Optional[str] = None,
759830
description: Optional[str] = None,
831+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
832+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
760833
tags: Optional[List["Tag"]] = None,
834+
operation_id: Optional[str] = None,
835+
middlewares: Optional[List[Callable[..., Any]]] = None,
761836
):
762837
"""Delete route decorator with DELETE `method`
763838
@@ -781,17 +856,34 @@ def lambda_handler(event, context):
781856
return app.resolve(event, context)
782857
```
783858
"""
784-
return self.route(rule, "DELETE", cors, compress, cache_control, description, tags, middlewares)
859+
return self.route(
860+
rule,
861+
"DELETE",
862+
cors,
863+
compress,
864+
cache_control,
865+
summary,
866+
description,
867+
responses,
868+
response_description,
869+
tags,
870+
operation_id,
871+
middlewares,
872+
)
785873

786874
def patch(
787875
self,
788876
rule: str,
789877
cors: Optional[bool] = None,
790878
compress: bool = False,
791879
cache_control: Optional[str] = None,
792-
middlewares: Optional[List[Callable]] = None,
880+
summary: Optional[str] = None,
793881
description: Optional[str] = None,
882+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
883+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
794884
tags: Optional[List["Tag"]] = None,
885+
operation_id: Optional[str] = None,
886+
middlewares: Optional[List[Callable]] = None,
795887
):
796888
"""Patch route decorator with PATCH `method`
797889
@@ -818,7 +910,20 @@ def lambda_handler(event, context):
818910
return app.resolve(event, context)
819911
```
820912
"""
821-
return self.route(rule, "PATCH", cors, compress, cache_control, description, tags, middlewares)
913+
return self.route(
914+
rule,
915+
"PATCH",
916+
cors,
917+
compress,
918+
cache_control,
919+
summary,
920+
description,
921+
responses,
922+
response_description,
923+
tags,
924+
operation_id,
925+
middlewares,
926+
)
822927

823928
def _push_processed_stack_frame(self, frame: str):
824929
"""
@@ -1204,8 +1309,12 @@ def route(
12041309
cors: Optional[bool] = None,
12051310
compress: bool = False,
12061311
cache_control: Optional[str] = None,
1312+
summary: Optional[str] = None,
12071313
description: Optional[str] = None,
1314+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
1315+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
12081316
tags: Optional[List["Tag"]] = None,
1317+
operation_id: Optional[str] = None,
12091318
middlewares: Optional[List[Callable[..., Any]]] = None,
12101319
):
12111320
"""Route decorator includes parameter `method`"""
@@ -1225,9 +1334,13 @@ def register_resolver(func: Callable):
12251334
cors_enabled,
12261335
compress,
12271336
cache_control,
1228-
middlewares,
1337+
summary,
12291338
description,
1339+
responses,
1340+
response_description,
12301341
tags,
1342+
operation_id,
1343+
middlewares,
12311344
)
12321345

12331346
# The more specific route wins.
@@ -1628,15 +1741,31 @@ def route(
16281741
cors: Optional[bool] = None,
16291742
compress: bool = False,
16301743
cache_control: Optional[str] = None,
1744+
summary: Optional[str] = None,
16311745
description: Optional[str] = None,
1746+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
1747+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
16321748
tags: Optional[List["Tag"]] = None,
1749+
operation_id: Optional[str] = None,
16331750
middlewares: Optional[List[Callable[..., Any]]] = None,
16341751
):
16351752
def register_route(func: Callable):
16361753
# Convert methods to tuple. It needs to be hashable as its part of the self._routes dict key
16371754
methods = (method,) if isinstance(method, str) else tuple(method)
16381755

1639-
route_key = (rule, methods, cors, compress, cache_control, description, tags)
1756+
route_key = (
1757+
rule,
1758+
methods,
1759+
cors,
1760+
compress,
1761+
cache_control,
1762+
summary,
1763+
description,
1764+
responses,
1765+
response_description,
1766+
tags,
1767+
operation_id,
1768+
)
16401769

16411770
# Collate Middleware for routes
16421771
if middlewares is not None:
@@ -1676,12 +1805,29 @@ def route(
16761805
cors: Optional[bool] = None,
16771806
compress: bool = False,
16781807
cache_control: Optional[str] = None,
1808+
summary: Optional[str] = None,
16791809
description: Optional[str] = None,
1810+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
1811+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
16801812
tags: Optional[List["Tag"]] = None,
1813+
operation_id: Optional[str] = None,
16811814
middlewares: Optional[List[Callable[..., Any]]] = None,
16821815
):
16831816
# NOTE: see #1552 for more context.
1684-
return super().route(rule.rstrip("/"), method, cors, compress, cache_control, description, tags, middlewares)
1817+
return super().route(
1818+
rule.rstrip("/"),
1819+
method,
1820+
cors,
1821+
compress,
1822+
cache_control,
1823+
summary,
1824+
description,
1825+
responses,
1826+
response_description,
1827+
tags,
1828+
operation_id,
1829+
middlewares,
1830+
)
16851831

16861832
# Override _compile_regex to exclude trailing slashes for route resolution
16871833
@staticmethod

aws_lambda_powertools/event_handler/openapi/params.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ def _get_field_info_and_type_annotation(annotation, value, is_path_param: bool)
351351
if annotation is not inspect.Signature.empty:
352352
# If the annotation is an Annotated type, we need to extract the type annotation and the FieldInfo
353353
if get_origin(annotation) is Annotated:
354-
type_annotation = _get_field_info_annotated_type(annotation, value, is_path_param)
354+
field_info, type_annotation = _get_field_info_annotated_type(annotation, value, is_path_param)
355355
# If the annotation is not an Annotated type, we use it as the type annotation
356356
else:
357357
type_annotation = annotation

0 commit comments

Comments
 (0)