Skip to content

Commit 0312c11

Browse files
committed
fix: customize the handler methods
1 parent 0f0e386 commit 0312c11

File tree

3 files changed

+238
-26
lines changed

3 files changed

+238
-26
lines changed

aws_lambda_powertools/event_handler/api_gateway.py

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

5556
if TYPE_CHECKING:
@@ -201,9 +202,13 @@ def __init__(
201202
cors: bool,
202203
compress: bool,
203204
cache_control: Optional[str],
204-
middlewares: Optional[List[Callable[..., Response]]],
205+
summary: Optional[str],
205206
description: Optional[str],
207+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]],
208+
response_description: Optional[str],
206209
tags: Optional[List["Tag"]],
210+
operation_id: Optional[str],
211+
middlewares: Optional[List[Callable[..., Response]]],
207212
):
208213
"""
209214
@@ -212,10 +217,10 @@ def __init__(
212217
213218
method: str
214219
The HTTP method, example "GET"
215-
rule: Pattern
216-
The route rule, example "/my/path"
217220
path: str
218221
The path of the route
222+
rule: Pattern
223+
The route rule, example "/my/path"
219224
func: Callable
220225
The route handler function
221226
cors: bool
@@ -224,12 +229,20 @@ def __init__(
224229
Whether or not to enable gzip compression for this route
225230
cache_control: Optional[str]
226231
The cache control header value, example "max-age=3600"
227-
middlewares: Optional[List[Callable[..., Response]]]
228-
The list of route middlewares to be called in order.
232+
summary: Optional[str]
233+
The OpenAPI summary for this route
229234
description: Optional[str]
230235
The OpenAPI description for this route
236+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]]
237+
The OpenAPI responses for this route
238+
response_description: Optional[str]
239+
The OpenAPI response description for this route
231240
tags: Optional[List[Tag]]
232241
The list of OpenAPI tags to be used for this route
242+
operation_id: Optional[str]
243+
The OpenAPI operationId for this route
244+
middlewares: Optional[List[Callable[..., Response]]]
245+
The list of route middlewares to be called in order.
233246
"""
234247
self.method = method.upper()
235248
self.path = path
@@ -239,10 +252,13 @@ def __init__(
239252
self.cors = cors
240253
self.compress = compress
241254
self.cache_control = cache_control
242-
self.middlewares = middlewares or []
255+
self.summary = summary
243256
self.description = description
257+
self.responses = responses
258+
self.response_description = response_description
244259
self.tags = tags or []
245-
self.operation_id = self.method.title() + self.func.__name__.title()
260+
self.middlewares = middlewares or []
261+
self.operation_id = operation_id or (self.method.title() + self.func.__name__.title())
246262

247263
# _middleware_stack_built is used to ensure the middleware stack is only built once.
248264
self._middleware_stack_built = False
@@ -370,7 +386,7 @@ def _get_openapi_path(
370386

371387
responses = operation.setdefault("responses", {})
372388
success_response = responses.setdefault("200", {})
373-
success_response["description"] = "Success"
389+
success_response["description"] = self.response_description or _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION
374390
success_response["content"] = {"application/json": {"schema": {}}}
375391
json_response = success_response["content"].setdefault("application/json", {})
376392

@@ -389,7 +405,7 @@ def _get_openapi_path(
389405
return path, definitions
390406

391407
def _openapi_operation_summary(self) -> str:
392-
return f"{self.method.upper()} {self.path}"
408+
return self.summary or f"{self.method.upper()} {self.path}"
393409

394410
def _openapi_operation_metadata(self, operation_ids: Set[str]) -> Dict[str, Any]:
395411
operation: Dict[str, Any] = {}
@@ -596,8 +612,12 @@ def route(
596612
cors: Optional[bool] = None,
597613
compress: bool = False,
598614
cache_control: Optional[str] = None,
615+
summary: Optional[str] = None,
599616
description: Optional[str] = None,
617+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
618+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
600619
tags: Optional[List["Tag"]] = None,
620+
operation_id: Optional[str] = None,
601621
middlewares: Optional[List[Callable[..., Any]]] = None,
602622
):
603623
raise NotImplementedError()
@@ -649,9 +669,13 @@ def get(
649669
cors: Optional[bool] = None,
650670
compress: bool = False,
651671
cache_control: Optional[str] = None,
652-
middlewares: Optional[List[Callable[..., Any]]] = None,
672+
summary: Optional[str] = None,
653673
description: Optional[str] = None,
674+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
675+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
654676
tags: Optional[List["Tag"]] = None,
677+
operation_id: Optional[str] = None,
678+
middlewares: Optional[List[Callable[..., Any]]] = None,
655679
):
656680
"""Get route decorator with GET `method`
657681
@@ -675,17 +699,34 @@ def lambda_handler(event, context):
675699
return app.resolve(event, context)
676700
```
677701
"""
678-
return self.route(rule, "GET", cors, compress, cache_control, description, tags, middlewares)
702+
return self.route(
703+
rule,
704+
"GET",
705+
cors,
706+
compress,
707+
cache_control,
708+
summary,
709+
description,
710+
responses,
711+
response_description,
712+
tags,
713+
operation_id,
714+
middlewares,
715+
)
679716

680717
def post(
681718
self,
682719
rule: str,
683720
cors: Optional[bool] = None,
684721
compress: bool = False,
685722
cache_control: Optional[str] = None,
686-
middlewares: Optional[List[Callable[..., Any]]] = None,
723+
summary: Optional[str] = None,
687724
description: Optional[str] = None,
725+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
726+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
688727
tags: Optional[List["Tag"]] = None,
728+
operation_id: Optional[str] = None,
729+
middlewares: Optional[List[Callable[..., Any]]] = None,
689730
):
690731
"""Post route decorator with POST `method`
691732
@@ -710,17 +751,34 @@ def lambda_handler(event, context):
710751
return app.resolve(event, context)
711752
```
712753
"""
713-
return self.route(rule, "POST", cors, compress, cache_control, description, tags, middlewares)
754+
return self.route(
755+
rule,
756+
"POST",
757+
cors,
758+
compress,
759+
cache_control,
760+
summary,
761+
description,
762+
responses,
763+
response_description,
764+
tags,
765+
operation_id,
766+
middlewares,
767+
)
714768

715769
def put(
716770
self,
717771
rule: str,
718772
cors: Optional[bool] = None,
719773
compress: bool = False,
720774
cache_control: Optional[str] = None,
721-
middlewares: Optional[List[Callable[..., Any]]] = None,
775+
summary: Optional[str] = None,
722776
description: Optional[str] = None,
777+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
778+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
723779
tags: Optional[List["Tag"]] = None,
780+
operation_id: Optional[str] = None,
781+
middlewares: Optional[List[Callable[..., Any]]] = None,
724782
):
725783
"""Put route decorator with PUT `method`
726784
@@ -745,17 +803,34 @@ def lambda_handler(event, context):
745803
return app.resolve(event, context)
746804
```
747805
"""
748-
return self.route(rule, "PUT", cors, compress, cache_control, description, tags, middlewares)
806+
return self.route(
807+
rule,
808+
"PUT",
809+
cors,
810+
compress,
811+
cache_control,
812+
summary,
813+
description,
814+
responses,
815+
response_description,
816+
tags,
817+
operation_id,
818+
middlewares,
819+
)
749820

750821
def delete(
751822
self,
752823
rule: str,
753824
cors: Optional[bool] = None,
754825
compress: bool = False,
755826
cache_control: Optional[str] = None,
756-
middlewares: Optional[List[Callable[..., Any]]] = None,
827+
summary: Optional[str] = None,
757828
description: Optional[str] = None,
829+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
830+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
758831
tags: Optional[List["Tag"]] = None,
832+
operation_id: Optional[str] = None,
833+
middlewares: Optional[List[Callable[..., Any]]] = None,
759834
):
760835
"""Delete route decorator with DELETE `method`
761836
@@ -779,17 +854,34 @@ def lambda_handler(event, context):
779854
return app.resolve(event, context)
780855
```
781856
"""
782-
return self.route(rule, "DELETE", cors, compress, cache_control, description, tags, middlewares)
857+
return self.route(
858+
rule,
859+
"DELETE",
860+
cors,
861+
compress,
862+
cache_control,
863+
summary,
864+
description,
865+
responses,
866+
response_description,
867+
tags,
868+
operation_id,
869+
middlewares,
870+
)
783871

784872
def patch(
785873
self,
786874
rule: str,
787875
cors: Optional[bool] = None,
788876
compress: bool = False,
789877
cache_control: Optional[str] = None,
790-
middlewares: Optional[List[Callable]] = None,
878+
summary: Optional[str] = None,
791879
description: Optional[str] = None,
880+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
881+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
792882
tags: Optional[List["Tag"]] = None,
883+
operation_id: Optional[str] = None,
884+
middlewares: Optional[List[Callable]] = None,
793885
):
794886
"""Patch route decorator with PATCH `method`
795887
@@ -816,7 +908,20 @@ def lambda_handler(event, context):
816908
return app.resolve(event, context)
817909
```
818910
"""
819-
return self.route(rule, "PATCH", cors, compress, cache_control, description, tags, middlewares)
911+
return self.route(
912+
rule,
913+
"PATCH",
914+
cors,
915+
compress,
916+
cache_control,
917+
summary,
918+
description,
919+
responses,
920+
response_description,
921+
tags,
922+
operation_id,
923+
middlewares,
924+
)
820925

821926
def _push_processed_stack_frame(self, frame: str):
822927
"""
@@ -1202,8 +1307,12 @@ def route(
12021307
cors: Optional[bool] = None,
12031308
compress: bool = False,
12041309
cache_control: Optional[str] = None,
1310+
summary: Optional[str] = None,
12051311
description: Optional[str] = None,
1312+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
1313+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
12061314
tags: Optional[List["Tag"]] = None,
1315+
operation_id: Optional[str] = None,
12071316
middlewares: Optional[List[Callable[..., Any]]] = None,
12081317
):
12091318
"""Route decorator includes parameter `method`"""
@@ -1223,9 +1332,13 @@ def register_resolver(func: Callable):
12231332
cors_enabled,
12241333
compress,
12251334
cache_control,
1226-
middlewares,
1335+
summary,
12271336
description,
1337+
responses,
1338+
response_description,
12281339
tags,
1340+
operation_id,
1341+
middlewares,
12291342
)
12301343

12311344
# The more specific route wins.
@@ -1623,15 +1736,31 @@ def route(
16231736
cors: Optional[bool] = None,
16241737
compress: bool = False,
16251738
cache_control: Optional[str] = None,
1739+
summary: Optional[str] = None,
16261740
description: Optional[str] = None,
1741+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
1742+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
16271743
tags: Optional[List["Tag"]] = None,
1744+
operation_id: Optional[str] = None,
16281745
middlewares: Optional[List[Callable[..., Any]]] = None,
16291746
):
16301747
def register_route(func: Callable):
16311748
# Convert methods to tuple. It needs to be hashable as its part of the self._routes dict key
16321749
methods = (method,) if isinstance(method, str) else tuple(method)
16331750

1634-
route_key = (rule, methods, cors, compress, cache_control, description, tags)
1751+
route_key = (
1752+
rule,
1753+
methods,
1754+
cors,
1755+
compress,
1756+
cache_control,
1757+
summary,
1758+
description,
1759+
responses,
1760+
response_description,
1761+
tags,
1762+
operation_id,
1763+
)
16351764

16361765
# Collate Middleware for routes
16371766
if middlewares is not None:
@@ -1671,12 +1800,29 @@ def route(
16711800
cors: Optional[bool] = None,
16721801
compress: bool = False,
16731802
cache_control: Optional[str] = None,
1803+
summary: Optional[str] = None,
16741804
description: Optional[str] = None,
1805+
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
1806+
response_description: Optional[str] = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
16751807
tags: Optional[List["Tag"]] = None,
1808+
operation_id: Optional[str] = None,
16761809
middlewares: Optional[List[Callable[..., Any]]] = None,
16771810
):
16781811
# NOTE: see #1552 for more context.
1679-
return super().route(rule.rstrip("/"), method, cors, compress, cache_control, description, tags, middlewares)
1812+
return super().route(
1813+
rule.rstrip("/"),
1814+
method,
1815+
cors,
1816+
compress,
1817+
cache_control,
1818+
summary,
1819+
description,
1820+
responses,
1821+
response_description,
1822+
tags,
1823+
operation_id,
1824+
middlewares,
1825+
)
16801826

16811827
# Override _compile_regex to exclude trailing slashes for route resolution
16821828
@staticmethod

aws_lambda_powertools/event_handler/openapi/params.py

+1-1
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)