powertools-lambda-python: Bug: APIGatewayHttpResolver with validation enabled returns HTTP 422 when using single value query parameters
Expected Behaviour
When using Amazon Api Gateway REST API to invoke an AWS Lambda using proxy integration, calling API with a single query parameter should return HTTP 200 with Lambda Powertools validation enabled.
This is also how it is documented here: https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/api_gateway/#validating-query-strings
Current Behaviour
Calling API with GET /my-path?productType=Category1
returns HTTP 422:
{
"statusCode": 422,
"detail": [{
"loc": ["query", "productType"],
"type": "type_error.str"
}]
}
Code snippet
Bug can be reproduced with this code inside a Lambda function:
app = api_gateway.APIGatewayRestResolver(
enable_validation=True
)
@app.get("/my-path")
def my_handler(
product_type: Annotated[str | None, Query(alias="productType")] = None,
) -> api_gateway.Response[api_model.GetAvailableProductsResponse]:
...
Possible Solution
Can be fixed by changing query param type to list:
@app.get("/my-path")
def my_handler(
product_type: Annotated[list[str] | None, Query(alias="productType")] = None,
) -> api_gateway.Response[api_model.GetAvailableProductsResponse]:
"""Lists available products"""
...
Steps to Reproduce
In AWS:
- Create a REST API in the Amazon Api Gateway Service.
- Create a Lambda function with Lambda Powertools configured as in the code examples.
- Configure the API Gateway with proxy integration to the Lambda function.
- Invoke the API with a query parameter.
Locally:
- Create unit tests for a Lambda function as described here: https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/api_gateway/#testing-your-code
- Run the test using REST API proxy integration payload. This payload always contains 2 attributes for the query string parameters, regardless if it’s single value or multi value in REST API proxy integration:
{ ... "queryStringParameters": { "productType": "Category1" }, "multiValueQueryStringParameters": { "productType": ["Category1"] } }
The issue is this line of code: https://github.com/aws-powertools/powertools-lambda-python/blob/develop/aws_lambda_powertools/utilities/data_classes/api_gateway_proxy_event.py#L123
Lambda Powertools should additionally check dictionary values in multiValueQueryStringParameters
if they contain only one value, and validate accordingly to be of type str
instead of list[str]
.
Powertools for AWS Lambda (Python) version
2.33.0
AWS Lambda function runtime
3.12
Packaging format used
PyPi
Debugging logs
No response
About this issue
- Original URL
- State: closed
- Created 5 months ago
- Reactions: 3
- Comments: 15 (11 by maintainers)
Coming up in this week’s release! Just merged Ruben’s PR
Got it, was able to reproduce the problem! Fix incoming
No that helps, let me investigate today and I’ll get back to you today!
The issue isn’t related to the alias but the fact that the
openapi_validation.py
handles all query strings with_normalize_multi_query_string_with_param
and takes the first value of the item.With
"queryStringParameters": { "productType": "Category1" }
it’s normalize to:"productType": "C"
With
"multiValueQueryStringParameters": { "productType": ["Category1"] }
it’s normalize correctly to:"productType": "Category1"
I was able to find this as I was trying to validate the min length of a query string parameter (
def get_hello(fname: Annotated[Optional[str], Query(min_length=4)] = None) -> HelloWorld:
andfname
was always too short… as it was always truncated to 1 character.The line truncating the value is here
@DariusKunce Confirmed! We’ll add this into the next sprint to fix