Skip to content

Commit 45d25e9

Browse files
feat(event_handler): adding status_code OpenAPI field (#8130)
* feat(event_handler): adding status_code OpenAPI field * feat(event_handler): adding status_code OpenAPI field
1 parent d488548 commit 45d25e9

File tree

9 files changed

+218
-8
lines changed

9 files changed

+218
-8
lines changed

aws_lambda_powertools/event_handler/api_gateway.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
2727
DEFAULT_OPENAPI_TITLE,
2828
DEFAULT_OPENAPI_VERSION,
29+
DEFAULT_STATUS_CODE,
2930
)
3031
from aws_lambda_powertools.event_handler.openapi.exceptions import (
3132
RequestUnsupportedContentType,
@@ -283,7 +284,7 @@ class BedrockResponse(Generic[ResponseT]):
283284
def __init__(
284285
self,
285286
body: Any = None,
286-
status_code: int = 200,
287+
status_code: int = DEFAULT_STATUS_CODE,
287288
content_type: str = DEFAULT_CONTENT_TYPE,
288289
session_attributes: dict[str, Any] | None = None,
289290
prompt_session_attributes: dict[str, Any] | None = None,
@@ -387,6 +388,7 @@ def __init__(
387388
deprecated: bool = False,
388389
enable_validation: bool | None = None,
389390
custom_response_validation_http_code: HTTPStatus | None = None,
391+
status_code: int = DEFAULT_STATUS_CODE,
390392
middlewares: list[Callable[..., Response]] | None = None,
391393
):
392394
"""
@@ -432,6 +434,9 @@ def __init__(
432434
Enable or disable validation for this specific route. If None, inherits from resolver setting.
433435
custom_response_validation_http_code: int | HTTPStatus | None, optional
434436
Whether to have custom http status code for this route if response validation fails
437+
status_code: int
438+
The default HTTP status code for successful responses. Used in both the OpenAPI schema
439+
and the actual response when the handler returns a dict. Defaults to 200.
435440
middlewares: list[Callable[..., Response]] | None
436441
The list of route middlewares to be called in order.
437442
"""
@@ -471,6 +476,7 @@ def __init__(
471476
self._body_field: ModelField | None = None
472477

473478
self.custom_response_validation_http_code = custom_response_validation_http_code
479+
self.status_code = status_code
474480

475481
# Cache whether this route's handler declares Depends() parameters
476482
self._has_dependencies: bool | None = None
@@ -664,6 +670,7 @@ def _get_openapi_path(
664670
response_description=self.response_description,
665671
body_field=self.body_field,
666672
custom_response_validation_http_code=self.custom_response_validation_http_code,
673+
status_code=self.status_code,
667674
dependant=dependant,
668675
operation_ids=operation_ids,
669676
model_name_map=model_name_map,
@@ -820,6 +827,7 @@ def route(
820827
deprecated: bool = False,
821828
enable_validation: bool | None = None,
822829
custom_response_validation_http_code: int | HTTPStatus | None = None,
830+
status_code: int = DEFAULT_STATUS_CODE,
823831
middlewares: list[Callable[..., Any]] | None = None,
824832
) -> Callable[[AnyCallableT], AnyCallableT]:
825833
raise NotImplementedError()
@@ -883,6 +891,7 @@ def get(
883891
deprecated: bool = False,
884892
enable_validation: bool | None = None,
885893
custom_response_validation_http_code: int | HTTPStatus | None = None,
894+
status_code: int = DEFAULT_STATUS_CODE,
886895
middlewares: list[Callable[..., Any]] | None = None,
887896
) -> Callable[[AnyCallableT], AnyCallableT]:
888897
"""Get route decorator with GET `method`
@@ -925,6 +934,7 @@ def lambda_handler(event, context):
925934
deprecated,
926935
enable_validation,
927936
custom_response_validation_http_code,
937+
status_code,
928938
middlewares,
929939
)
930940

@@ -946,6 +956,7 @@ def post(
946956
deprecated: bool = False,
947957
enable_validation: bool | None = None,
948958
custom_response_validation_http_code: int | HTTPStatus | None = None,
959+
status_code: int = DEFAULT_STATUS_CODE,
949960
middlewares: list[Callable[..., Any]] | None = None,
950961
) -> Callable[[AnyCallableT], AnyCallableT]:
951962
"""Post route decorator with POST `method`
@@ -989,6 +1000,7 @@ def lambda_handler(event, context):
9891000
deprecated,
9901001
enable_validation,
9911002
custom_response_validation_http_code,
1003+
status_code,
9921004
middlewares,
9931005
)
9941006

@@ -1010,6 +1022,7 @@ def put(
10101022
deprecated: bool = False,
10111023
enable_validation: bool | None = None,
10121024
custom_response_validation_http_code: int | HTTPStatus | None = None,
1025+
status_code: int = DEFAULT_STATUS_CODE,
10131026
middlewares: list[Callable[..., Any]] | None = None,
10141027
) -> Callable[[AnyCallableT], AnyCallableT]:
10151028
"""Put route decorator with PUT `method`
@@ -1053,6 +1066,7 @@ def lambda_handler(event, context):
10531066
deprecated,
10541067
enable_validation,
10551068
custom_response_validation_http_code,
1069+
status_code,
10561070
middlewares,
10571071
)
10581072

@@ -1074,6 +1088,7 @@ def delete(
10741088
deprecated: bool = False,
10751089
enable_validation: bool | None = None,
10761090
custom_response_validation_http_code: int | HTTPStatus | None = None,
1091+
status_code: int = DEFAULT_STATUS_CODE,
10771092
middlewares: list[Callable[..., Any]] | None = None,
10781093
) -> Callable[[AnyCallableT], AnyCallableT]:
10791094
"""Delete route decorator with DELETE `method`
@@ -1116,6 +1131,7 @@ def lambda_handler(event, context):
11161131
deprecated,
11171132
enable_validation,
11181133
custom_response_validation_http_code,
1134+
status_code,
11191135
middlewares,
11201136
)
11211137

@@ -1137,6 +1153,7 @@ def patch(
11371153
deprecated: bool = False,
11381154
enable_validation: bool | None = None,
11391155
custom_response_validation_http_code: int | HTTPStatus | None = None,
1156+
status_code: int = DEFAULT_STATUS_CODE,
11401157
middlewares: list[Callable] | None = None,
11411158
) -> Callable[[AnyCallableT], AnyCallableT]:
11421159
"""Patch route decorator with PATCH `method`
@@ -1182,6 +1199,7 @@ def lambda_handler(event, context):
11821199
deprecated,
11831200
enable_validation,
11841201
custom_response_validation_http_code,
1202+
status_code,
11851203
middlewares,
11861204
)
11871205

@@ -1203,6 +1221,7 @@ def head(
12031221
deprecated: bool = False,
12041222
enable_validation: bool | None = None,
12051223
custom_response_validation_http_code: int | HTTPStatus | None = None,
1224+
status_code: int = DEFAULT_STATUS_CODE,
12061225
middlewares: list[Callable] | None = None,
12071226
) -> Callable[[AnyCallableT], AnyCallableT]:
12081227
"""Head route decorator with HEAD `method`
@@ -1247,6 +1266,7 @@ def lambda_handler(event, context):
12471266
deprecated,
12481267
enable_validation,
12491268
custom_response_validation_http_code,
1269+
status_code,
12501270
middlewares,
12511271
)
12521272

@@ -2357,6 +2377,7 @@ def route(
23572377
deprecated: bool = False,
23582378
enable_validation: bool | None = None,
23592379
custom_response_validation_http_code: int | HTTPStatus | None = None,
2380+
status_code: int = DEFAULT_STATUS_CODE,
23602381
middlewares: list[Callable[..., Any]] | None = None,
23612382
) -> Callable[[AnyCallableT], AnyCallableT]:
23622383
"""Route decorator includes parameter `method`"""
@@ -2392,6 +2413,7 @@ def register_resolver(func: AnyCallableT) -> AnyCallableT:
23922413
deprecated,
23932414
enable_validation,
23942415
custom_response_validation_http_code,
2416+
status_code,
23952417
middlewares,
23962418
)
23972419

@@ -2779,12 +2801,15 @@ def _to_response(self, result: dict | tuple | Response | BedrockResponse) -> Res
27792801
- tuple[dict, int]: Same dict handling as above but with the option of including a status code
27802802
- Response: returned as is, and allows for more flexibility
27812803
"""
2782-
status_code = HTTPStatus.OK
27832804
if isinstance(result, (Response, BedrockResponse)):
27842805
return result
27852806
elif isinstance(result, tuple) and len(result) == 2:
27862807
# Unpack result dict and status code from tuple
27872808
result, status_code = result
2809+
else:
2810+
# Use the route's status_code if available, otherwise default to 200
2811+
route: Route | None = self.context.get("_route")
2812+
status_code = route.status_code if route else HTTPStatus.OK
27882813

27892814
logger.debug("Simple response detected, serializing return before constructing final response")
27902815
return Response(
@@ -2903,6 +2928,7 @@ def route(
29032928
deprecated: bool = False,
29042929
enable_validation: bool | None = None,
29052930
custom_response_validation_http_code: int | HTTPStatus | None = None,
2931+
status_code: int = DEFAULT_STATUS_CODE,
29062932
middlewares: list[Callable[..., Any]] | None = None,
29072933
) -> Callable[[AnyCallableT], AnyCallableT]:
29082934
def register_route(func: AnyCallableT) -> AnyCallableT:
@@ -2931,6 +2957,7 @@ def register_route(func: AnyCallableT) -> AnyCallableT:
29312957
deprecated,
29322958
enable_validation,
29332959
custom_response_validation_http_code,
2960+
status_code,
29342961
)
29352962

29362963
# Collate Middleware for routes
@@ -3000,6 +3027,7 @@ def route(
30003027
deprecated: bool = False,
30013028
enable_validation: bool | None = None,
30023029
custom_response_validation_http_code: int | HTTPStatus | None = None,
3030+
status_code: int = DEFAULT_STATUS_CODE,
30033031
middlewares: list[Callable[..., Any]] | None = None,
30043032
) -> Callable[[AnyCallableT], AnyCallableT]:
30053033
# NOTE: see #1552 for more context.
@@ -3021,6 +3049,7 @@ def route(
30213049
deprecated,
30223050
enable_validation,
30233051
custom_response_validation_http_code,
3052+
status_code,
30243053
middlewares,
30253054
)
30263055

aws_lambda_powertools/event_handler/bedrock_agent.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
DEFAULT_API_VERSION,
1616
DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
1717
DEFAULT_OPENAPI_VERSION,
18+
DEFAULT_STATUS_CODE,
1819
)
1920

2021
if TYPE_CHECKING:
@@ -129,6 +130,7 @@ def get( # type: ignore[override]
129130
deprecated: bool = False,
130131
enable_validation: bool | None = None,
131132
custom_response_validation_http_code: int | HTTPStatus | None = None,
133+
status_code: int = DEFAULT_STATUS_CODE,
132134
middlewares: list[Callable[..., Any]] | None = None,
133135
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
134136
security = None
@@ -150,6 +152,7 @@ def get( # type: ignore[override]
150152
deprecated,
151153
enable_validation,
152154
custom_response_validation_http_code,
155+
status_code,
153156
middlewares,
154157
)
155158

@@ -172,6 +175,7 @@ def post( # type: ignore[override]
172175
deprecated: bool = False,
173176
enable_validation: bool | None = None,
174177
custom_response_validation_http_code: int | HTTPStatus | None = None,
178+
status_code: int = DEFAULT_STATUS_CODE,
175179
middlewares: list[Callable[..., Any]] | None = None,
176180
):
177181
security = None
@@ -193,6 +197,7 @@ def post( # type: ignore[override]
193197
deprecated,
194198
enable_validation,
195199
custom_response_validation_http_code,
200+
status_code,
196201
middlewares,
197202
)
198203

@@ -215,6 +220,7 @@ def put( # type: ignore[override]
215220
deprecated: bool = False,
216221
enable_validation: bool | None = None,
217222
custom_response_validation_http_code: int | HTTPStatus | None = None,
223+
status_code: int = DEFAULT_STATUS_CODE,
218224
middlewares: list[Callable[..., Any]] | None = None,
219225
):
220226
security = None
@@ -236,6 +242,7 @@ def put( # type: ignore[override]
236242
deprecated,
237243
enable_validation,
238244
custom_response_validation_http_code,
245+
status_code,
239246
middlewares,
240247
)
241248

@@ -258,6 +265,7 @@ def patch( # type: ignore[override]
258265
deprecated: bool = False,
259266
enable_validation: bool | None = None,
260267
custom_response_validation_http_code: int | HTTPStatus | None = None,
268+
status_code: int = DEFAULT_STATUS_CODE,
261269
middlewares: list[Callable] | None = None,
262270
):
263271
security = None
@@ -279,6 +287,7 @@ def patch( # type: ignore[override]
279287
deprecated,
280288
enable_validation,
281289
custom_response_validation_http_code,
290+
status_code,
282291
middlewares,
283292
)
284293

@@ -301,6 +310,7 @@ def delete( # type: ignore[override]
301310
deprecated: bool = False,
302311
enable_validation: bool | None = None,
303312
custom_response_validation_http_code: int | HTTPStatus | None = None,
313+
status_code: int = DEFAULT_STATUS_CODE,
304314
middlewares: list[Callable[..., Any]] | None = None,
305315
):
306316
security = None
@@ -322,6 +332,7 @@ def delete( # type: ignore[override]
322332
deprecated,
323333
enable_validation,
324334
custom_response_validation_http_code,
335+
status_code,
325336
middlewares,
326337
)
327338

aws_lambda_powertools/event_handler/openapi/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
DEFAULT_OPENAPI_TITLE = "Powertools for AWS Lambda (Python) API"
44
DEFAULT_CONTENT_TYPE = "application/json"
55
DEFAULT_OPENAPI_RESPONSE_DESCRIPTION = "Successful Response"
6+
DEFAULT_STATUS_CODE = 200

aws_lambda_powertools/event_handler/openapi/schema_generator.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from aws_lambda_powertools.event_handler.openapi.constants import (
3737
DEFAULT_CONTENT_TYPE,
3838
DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
39+
DEFAULT_STATUS_CODE,
3940
)
4041

4142

@@ -54,6 +55,7 @@ def generate_openapi_path(
5455
response_description: str | None,
5556
body_field: ModelField | None,
5657
custom_response_validation_http_code: HTTPStatus | None,
58+
status_code: int = DEFAULT_STATUS_CODE,
5759
dependant: Dependant,
5860
operation_ids: set[str],
5961
model_name_map: dict[TypeModelOrEnum, str],
@@ -108,6 +110,7 @@ def generate_openapi_path(
108110
responses=responses,
109111
response_description=response_description,
110112
custom_response_validation_http_code=custom_response_validation_http_code,
113+
status_code=status_code,
111114
dependant=dependant,
112115
model_name_map=model_name_map,
113116
field_mapping=field_mapping,
@@ -220,6 +223,7 @@ def _build_responses(
220223
responses: dict[int, OpenAPIResponse] | None,
221224
response_description: str | None,
222225
custom_response_validation_http_code: HTTPStatus | None,
226+
status_code: int = DEFAULT_STATUS_CODE,
223227
dependant: Dependant,
224228
model_name_map: dict[TypeModelOrEnum, str],
225229
field_mapping: dict[tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue],
@@ -237,9 +241,9 @@ def _build_responses(
237241
)
238242

239243
if responses:
240-
for status_code in list(responses):
241-
operation_responses[status_code] = _build_custom_response(
242-
response=copy.deepcopy(responses[status_code]),
244+
for resp_code in list(responses):
245+
operation_responses[resp_code] = _build_custom_response(
246+
response=copy.deepcopy(responses[resp_code]),
243247
dependant=dependant,
244248
model_name_map=model_name_map,
245249
field_mapping=field_mapping,
@@ -251,7 +255,7 @@ def _build_responses(
251255
field_mapping=field_mapping,
252256
)
253257

254-
operation_responses[200] = {
258+
operation_responses[status_code] = {
255259
"description": response_description or DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
256260
"content": {DEFAULT_CONTENT_TYPE: response_schema},
257261
}

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

0 commit comments

Comments
 (0)