Skip to content

Commit 3838a42

Browse files
feat(event_handler): adding status_code OpenAPI field
1 parent 12aeb50 commit 3838a42

8 files changed

Lines changed: 213 additions & 7 deletions

File tree

aws_lambda_powertools/event_handler/api_gateway.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@ def __init__(
387387
deprecated: bool = False,
388388
enable_validation: bool | None = None,
389389
custom_response_validation_http_code: HTTPStatus | None = None,
390+
status_code: int = 200,
390391
middlewares: list[Callable[..., Response]] | None = None,
391392
):
392393
"""
@@ -432,6 +433,9 @@ def __init__(
432433
Enable or disable validation for this specific route. If None, inherits from resolver setting.
433434
custom_response_validation_http_code: int | HTTPStatus | None, optional
434435
Whether to have custom http status code for this route if response validation fails
436+
status_code: int
437+
The default HTTP status code for successful responses. Used in both the OpenAPI schema
438+
and the actual response when the handler returns a dict. Defaults to 200.
435439
middlewares: list[Callable[..., Response]] | None
436440
The list of route middlewares to be called in order.
437441
"""
@@ -471,6 +475,7 @@ def __init__(
471475
self._body_field: ModelField | None = None
472476

473477
self.custom_response_validation_http_code = custom_response_validation_http_code
478+
self.status_code = status_code
474479

475480
# Caches the name of any Request-typed parameter in the handler.
476481
# Avoids re-scanning the signature on every invocation.
@@ -652,6 +657,7 @@ def _get_openapi_path(
652657
response_description=self.response_description,
653658
body_field=self.body_field,
654659
custom_response_validation_http_code=self.custom_response_validation_http_code,
660+
status_code=self.status_code,
655661
dependant=dependant,
656662
operation_ids=operation_ids,
657663
model_name_map=model_name_map,
@@ -808,6 +814,7 @@ def route(
808814
deprecated: bool = False,
809815
enable_validation: bool | None = None,
810816
custom_response_validation_http_code: int | HTTPStatus | None = None,
817+
status_code: int = 200,
811818
middlewares: list[Callable[..., Any]] | None = None,
812819
) -> Callable[[AnyCallableT], AnyCallableT]:
813820
raise NotImplementedError()
@@ -871,6 +878,7 @@ def get(
871878
deprecated: bool = False,
872879
enable_validation: bool | None = None,
873880
custom_response_validation_http_code: int | HTTPStatus | None = None,
881+
status_code: int = 200,
874882
middlewares: list[Callable[..., Any]] | None = None,
875883
) -> Callable[[AnyCallableT], AnyCallableT]:
876884
"""Get route decorator with GET `method`
@@ -913,6 +921,7 @@ def lambda_handler(event, context):
913921
deprecated,
914922
enable_validation,
915923
custom_response_validation_http_code,
924+
status_code,
916925
middlewares,
917926
)
918927

@@ -934,6 +943,7 @@ def post(
934943
deprecated: bool = False,
935944
enable_validation: bool | None = None,
936945
custom_response_validation_http_code: int | HTTPStatus | None = None,
946+
status_code: int = 200,
937947
middlewares: list[Callable[..., Any]] | None = None,
938948
) -> Callable[[AnyCallableT], AnyCallableT]:
939949
"""Post route decorator with POST `method`
@@ -977,6 +987,7 @@ def lambda_handler(event, context):
977987
deprecated,
978988
enable_validation,
979989
custom_response_validation_http_code,
990+
status_code,
980991
middlewares,
981992
)
982993

@@ -998,6 +1009,7 @@ def put(
9981009
deprecated: bool = False,
9991010
enable_validation: bool | None = None,
10001011
custom_response_validation_http_code: int | HTTPStatus | None = None,
1012+
status_code: int = 200,
10011013
middlewares: list[Callable[..., Any]] | None = None,
10021014
) -> Callable[[AnyCallableT], AnyCallableT]:
10031015
"""Put route decorator with PUT `method`
@@ -1041,6 +1053,7 @@ def lambda_handler(event, context):
10411053
deprecated,
10421054
enable_validation,
10431055
custom_response_validation_http_code,
1056+
status_code,
10441057
middlewares,
10451058
)
10461059

@@ -1062,6 +1075,7 @@ def delete(
10621075
deprecated: bool = False,
10631076
enable_validation: bool | None = None,
10641077
custom_response_validation_http_code: int | HTTPStatus | None = None,
1078+
status_code: int = 200,
10651079
middlewares: list[Callable[..., Any]] | None = None,
10661080
) -> Callable[[AnyCallableT], AnyCallableT]:
10671081
"""Delete route decorator with DELETE `method`
@@ -1104,6 +1118,7 @@ def lambda_handler(event, context):
11041118
deprecated,
11051119
enable_validation,
11061120
custom_response_validation_http_code,
1121+
status_code,
11071122
middlewares,
11081123
)
11091124

@@ -1125,6 +1140,7 @@ def patch(
11251140
deprecated: bool = False,
11261141
enable_validation: bool | None = None,
11271142
custom_response_validation_http_code: int | HTTPStatus | None = None,
1143+
status_code: int = 200,
11281144
middlewares: list[Callable] | None = None,
11291145
) -> Callable[[AnyCallableT], AnyCallableT]:
11301146
"""Patch route decorator with PATCH `method`
@@ -1170,6 +1186,7 @@ def lambda_handler(event, context):
11701186
deprecated,
11711187
enable_validation,
11721188
custom_response_validation_http_code,
1189+
status_code,
11731190
middlewares,
11741191
)
11751192

@@ -1191,6 +1208,7 @@ def head(
11911208
deprecated: bool = False,
11921209
enable_validation: bool | None = None,
11931210
custom_response_validation_http_code: int | HTTPStatus | None = None,
1211+
status_code: int = 200,
11941212
middlewares: list[Callable] | None = None,
11951213
) -> Callable[[AnyCallableT], AnyCallableT]:
11961214
"""Head route decorator with HEAD `method`
@@ -1235,6 +1253,7 @@ def lambda_handler(event, context):
12351253
deprecated,
12361254
enable_validation,
12371255
custom_response_validation_http_code,
1256+
status_code,
12381257
middlewares,
12391258
)
12401259

@@ -2329,6 +2348,7 @@ def route(
23292348
deprecated: bool = False,
23302349
enable_validation: bool | None = None,
23312350
custom_response_validation_http_code: int | HTTPStatus | None = None,
2351+
status_code: int = 200,
23322352
middlewares: list[Callable[..., Any]] | None = None,
23332353
) -> Callable[[AnyCallableT], AnyCallableT]:
23342354
"""Route decorator includes parameter `method`"""
@@ -2364,6 +2384,7 @@ def register_resolver(func: AnyCallableT) -> AnyCallableT:
23642384
deprecated,
23652385
enable_validation,
23662386
custom_response_validation_http_code,
2387+
status_code,
23672388
middlewares,
23682389
)
23692390

@@ -2751,12 +2772,15 @@ def _to_response(self, result: dict | tuple | Response | BedrockResponse) -> Res
27512772
- tuple[dict, int]: Same dict handling as above but with the option of including a status code
27522773
- Response: returned as is, and allows for more flexibility
27532774
"""
2754-
status_code = HTTPStatus.OK
27552775
if isinstance(result, (Response, BedrockResponse)):
27562776
return result
27572777
elif isinstance(result, tuple) and len(result) == 2:
27582778
# Unpack result dict and status code from tuple
27592779
result, status_code = result
2780+
else:
2781+
# Use the route's status_code if available, otherwise default to 200
2782+
route: Route | None = self.context.get("_route")
2783+
status_code = route.status_code if route else HTTPStatus.OK
27602784

27612785
logger.debug("Simple response detected, serializing return before constructing final response")
27622786
return Response(
@@ -2875,6 +2899,7 @@ def route(
28752899
deprecated: bool = False,
28762900
enable_validation: bool | None = None,
28772901
custom_response_validation_http_code: int | HTTPStatus | None = None,
2902+
status_code: int = 200,
28782903
middlewares: list[Callable[..., Any]] | None = None,
28792904
) -> Callable[[AnyCallableT], AnyCallableT]:
28802905
def register_route(func: AnyCallableT) -> AnyCallableT:
@@ -2903,6 +2928,7 @@ def register_route(func: AnyCallableT) -> AnyCallableT:
29032928
deprecated,
29042929
enable_validation,
29052930
custom_response_validation_http_code,
2931+
status_code,
29062932
)
29072933

29082934
# Collate Middleware for routes
@@ -2972,6 +2998,7 @@ def route(
29722998
deprecated: bool = False,
29732999
enable_validation: bool | None = None,
29743000
custom_response_validation_http_code: int | HTTPStatus | None = None,
3001+
status_code: int = 200,
29753002
middlewares: list[Callable[..., Any]] | None = None,
29763003
) -> Callable[[AnyCallableT], AnyCallableT]:
29773004
# NOTE: see #1552 for more context.
@@ -2993,6 +3020,7 @@ def route(
29933020
deprecated,
29943021
enable_validation,
29953022
custom_response_validation_http_code,
3023+
status_code,
29963024
middlewares,
29973025
)
29983026

aws_lambda_powertools/event_handler/bedrock_agent.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ def get( # type: ignore[override]
129129
deprecated: bool = False,
130130
enable_validation: bool | None = None,
131131
custom_response_validation_http_code: int | HTTPStatus | None = None,
132+
status_code: int = 200,
132133
middlewares: list[Callable[..., Any]] | None = None,
133134
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
134135
security = None
@@ -150,6 +151,7 @@ def get( # type: ignore[override]
150151
deprecated,
151152
enable_validation,
152153
custom_response_validation_http_code,
154+
status_code,
153155
middlewares,
154156
)
155157

@@ -172,6 +174,7 @@ def post( # type: ignore[override]
172174
deprecated: bool = False,
173175
enable_validation: bool | None = None,
174176
custom_response_validation_http_code: int | HTTPStatus | None = None,
177+
status_code: int = 200,
175178
middlewares: list[Callable[..., Any]] | None = None,
176179
):
177180
security = None
@@ -193,6 +196,7 @@ def post( # type: ignore[override]
193196
deprecated,
194197
enable_validation,
195198
custom_response_validation_http_code,
199+
status_code,
196200
middlewares,
197201
)
198202

@@ -215,6 +219,7 @@ def put( # type: ignore[override]
215219
deprecated: bool = False,
216220
enable_validation: bool | None = None,
217221
custom_response_validation_http_code: int | HTTPStatus | None = None,
222+
status_code: int = 200,
218223
middlewares: list[Callable[..., Any]] | None = None,
219224
):
220225
security = None
@@ -236,6 +241,7 @@ def put( # type: ignore[override]
236241
deprecated,
237242
enable_validation,
238243
custom_response_validation_http_code,
244+
status_code,
239245
middlewares,
240246
)
241247

@@ -258,6 +264,7 @@ def patch( # type: ignore[override]
258264
deprecated: bool = False,
259265
enable_validation: bool | None = None,
260266
custom_response_validation_http_code: int | HTTPStatus | None = None,
267+
status_code: int = 200,
261268
middlewares: list[Callable] | None = None,
262269
):
263270
security = None
@@ -279,6 +286,7 @@ def patch( # type: ignore[override]
279286
deprecated,
280287
enable_validation,
281288
custom_response_validation_http_code,
289+
status_code,
282290
middlewares,
283291
)
284292

@@ -301,6 +309,7 @@ def delete( # type: ignore[override]
301309
deprecated: bool = False,
302310
enable_validation: bool | None = None,
303311
custom_response_validation_http_code: int | HTTPStatus | None = None,
312+
status_code: int = 200,
304313
middlewares: list[Callable[..., Any]] | None = None,
305314
):
306315
security = None
@@ -322,6 +331,7 @@ def delete( # type: ignore[override]
322331
deprecated,
323332
enable_validation,
324333
custom_response_validation_http_code,
334+
status_code,
325335
middlewares,
326336
)
327337

aws_lambda_powertools/event_handler/openapi/schema_generator.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def generate_openapi_path(
5454
response_description: str | None,
5555
body_field: ModelField | None,
5656
custom_response_validation_http_code: HTTPStatus | None,
57+
status_code: int = 200,
5758
dependant: Dependant,
5859
operation_ids: set[str],
5960
model_name_map: dict[TypeModelOrEnum, str],
@@ -108,6 +109,7 @@ def generate_openapi_path(
108109
responses=responses,
109110
response_description=response_description,
110111
custom_response_validation_http_code=custom_response_validation_http_code,
112+
status_code=status_code,
111113
dependant=dependant,
112114
model_name_map=model_name_map,
113115
field_mapping=field_mapping,
@@ -220,6 +222,7 @@ def _build_responses(
220222
responses: dict[int, OpenAPIResponse] | None,
221223
response_description: str | None,
222224
custom_response_validation_http_code: HTTPStatus | None,
225+
status_code: int = 200,
223226
dependant: Dependant,
224227
model_name_map: dict[TypeModelOrEnum, str],
225228
field_mapping: dict[tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue],
@@ -237,9 +240,9 @@ def _build_responses(
237240
)
238241

239242
if responses:
240-
for status_code in list(responses):
241-
operation_responses[status_code] = _build_custom_response(
242-
response=copy.deepcopy(responses[status_code]),
243+
for resp_code in list(responses):
244+
operation_responses[resp_code] = _build_custom_response(
245+
response=copy.deepcopy(responses[resp_code]),
243246
dependant=dependant,
244247
model_name_map=model_name_map,
245248
field_mapping=field_mapping,
@@ -251,7 +254,7 @@ def _build_responses(
251254
field_mapping=field_mapping,
252255
)
253256

254-
operation_responses[200] = {
257+
operation_responses[status_code] = {
255258
"description": response_description or DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
256259
"content": {DEFAULT_CONTENT_TYPE: response_schema},
257260
}

docs/core/event_handler/_openapi_customization_operations.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ Here's a breakdown of various customizable fields:
1313
| `tags` | `List[str]` | Tags are a way to categorize and group endpoints within the API documentation. They can help organize the operations by resources or other heuristic. |
1414
| `operation_id` | `str` | A unique identifier for the operation, which can be used for referencing this operation in documentation or code. This ID must be unique across all operations described in the API. |
1515
| `include_in_schema` | `bool` | A boolean value that determines whether or not this operation should be included in the OpenAPI schema. Setting it to `False` can hide the endpoint from generated documentation and schema exports, which might be useful for private or experimental endpoints. |
16-
| `deprecated` | `bool` | A boolean value that determines whether or not this operation should be marked as deprecated in the OpenAPI schema. |
16+
| `deprecated` | `bool` | A boolean value that determines whether or not this operation should be marked as deprecated in the OpenAPI schema. |
17+
| `status_code` | `int` | The default HTTP status code for successful responses. Defaults to `200`. This value is used both in the OpenAPI schema and as the actual response status code when the handler returns a dictionary or plain value (not a `Response` object or tuple). |

docs/core/event_handler/openapi.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ To implement these customizations, include extra parameters when defining your r
7373

7474
=== "customizing_api_operations.py"
7575

76-
```python hl_lines="11-20"
76+
```python hl_lines="11-20 29-36"
7777
--8<-- "examples/event_handler_rest/src/customizing_api_operations.py"
7878
```
7979

examples/event_handler_rest/src/customizing_api_operations.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,17 @@ def get_todo_title(todo_id: int) -> str:
2626
return todo.json()["title"]
2727

2828

29+
@app.post(
30+
"/todos",
31+
summary="Creates a new todo item",
32+
description="Creates a new todo item and returns it",
33+
response_description="The created todo object",
34+
status_code=201,
35+
tags=["Todos"],
36+
)
37+
def create_todo(title: str) -> dict:
38+
return {"id": 1, "title": title}
39+
40+
2941
def lambda_handler(event: dict, context: LambdaContext) -> dict:
3042
return app.resolve(event, context)

0 commit comments

Comments
 (0)