Skip to content

Commit c61dbe8

Browse files
committed
fix: parse single list items in form data
1 parent da72ca2 commit c61dbe8

File tree

2 files changed

+22
-5
lines changed

2 files changed

+22
-5
lines changed

aws_lambda_powertools/event_handler/middlewares/openapi_validation.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
_normalize_errors,
1616
_regenerate_error_with_loc,
1717
get_missing_field_error,
18+
is_sequence_field,
1819
)
1920
from aws_lambda_powertools.event_handler.openapi.dependant import is_scalar_field
2021
from aws_lambda_powertools.event_handler.openapi.encoders import jsonable_encoder
@@ -150,11 +151,10 @@ def _parse_form_data(self, app: EventHandlerInstance) -> dict[str, Any]:
150151
"""Parse URL-encoded form data from the request body."""
151152
try:
152153
body = app.current_event.decoded_body or ""
153-
# parse_qs returns dict[str, list[str]], but we want dict[str, str] for single values
154+
# NOTE: Keep values as lists; we'll normalize per-field later based on the expected type.
155+
# This avoids breaking List[...] fields when only a single value is provided.
154156
parsed = parse_qs(body, keep_blank_values=True)
155-
156-
result: dict[str, Any] = {key: values[0] if len(values) == 1 else values for key, values in parsed.items()}
157-
return result
157+
return parsed
158158

159159
except Exception as e: # pragma: no cover
160160
raise RequestValidationError( # pragma: no cover
@@ -380,6 +380,10 @@ def _request_body_to_args(
380380
errors.append(get_missing_field_error(loc))
381381
continue
382382

383+
# Normalize lists for non-sequence fields
384+
if isinstance(value, list) and not is_sequence_field(field):
385+
value = value[0] if value else None
386+
383387
# Determine if the field is required
384388
if value is None:
385389
if field.required:

tests/functional/event_handler/_pydantic/test_openapi_validation_middleware.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1481,20 +1481,33 @@ def handler_custom_route_response_validation_error() -> Model:
14811481

14821482
def test_parse_form_data_url_encoded(gw_event):
14831483
"""Test _parse_form_data method with URL-encoded form data"""
1484-
1484+
# GIVEN an APIGatewayRestResolver with validation enabled
14851485
app = APIGatewayRestResolver(enable_validation=True)
14861486

14871487
@app.post("/form")
14881488
def post_form(name: Annotated[str, Form()], tags: Annotated[List[str], Form()]):
14891489
return {"name": name, "tags": tags}
14901490

1491+
# WHEN sending a POST request with URL-encoded form data
14911492
gw_event["httpMethod"] = "POST"
14921493
gw_event["path"] = "/form"
14931494
gw_event["headers"]["content-type"] = "application/x-www-form-urlencoded"
14941495
gw_event["body"] = "name=test&tags=tag1&tags=tag2"
14951496

14961497
result = app(gw_event, {})
1498+
1499+
# THEN it should parse the form data correctly
1500+
assert result["statusCode"] == 200
1501+
assert result["body"] == '{"name":"test","tags":["tag1","tag2"]}'
1502+
1503+
# WHEN sending a POST request with a single value for a list field
1504+
gw_event["body"] = "name=test&tags=tag1"
1505+
1506+
result = app(gw_event, {})
1507+
1508+
# THEN it should parse the form data correctly
14971509
assert result["statusCode"] == 200
1510+
assert result["body"] == '{"name":"test","tags":["tag1"]}'
14981511

14991512

15001513
def test_parse_form_data_wrong_value(gw_event):

0 commit comments

Comments
 (0)