8000 fix S3 parser with empty integer query string parameters by bentsku · Pull Request #9603 · localstack/localstack · GitHub
[go: up one dir, main page]

Skip to content

fix S3 parser with empty integer query string parameters #9603

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions localstack/aws/protocol/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,14 @@ def _parse_shape(
uri_params["Key"] = uri_params["Key"] + "/"
return super()._parse_shape(request, shape, node, uri_params)

@_text_content
def _parse_integer(self, _, shape, node: str, ___) -> int | None:
# S3 accepts empty query string parameters that should be integer
# to not break other cases, validate that the shape is in the querystring
if node == "" and shape.serialization.get("location") == "querystring":
return None
return int(node)


class SQSQueryRequestParser(QueryRequestParser):
def _get_serialized_name(self, shape: Shape, default_name: str, node: dict) -> str:
Expand Down
44 changes: 41 additions & 3 deletions tests/aws/services/s3/test_s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ def test_list_objects_next_marker(self, s3_bucket, snapshot, aws_client):
snapshot.match("list-objects-marker-empty", resp)

@markers.aws.validated
@pytest.mark.xfail(condition=is_v2_provider, reason="not implemented in moto")
@pytest.mark.xfail(condition=is_v2_provider(), reason="not implemented in moto")
def test_list_multiparts_next_marker(self, s3_bucket, snapshot, aws_client):
snapshot.add_transformer(snapshot.transform.s3_api())
snapshot.add_transformers_list(
Expand Down Expand Up @@ -943,7 +943,7 @@ def test_list_multiparts_next_marker(self, s3_bucket, snapshot, aws_client):
snapshot.match("list-multiparts-next-key-empty", response)

@markers.aws.validated
@pytest.mark.xfail(condition=is_v2_provider, reason="not implemented in moto")
@pytest.mark.xfail(condition=is_v2_provider(), reason="not implemented in moto")
def test_list_multiparts_with_prefix_and_delimiter(
self, s3_bucket, snapshot, aws_client, aws_http_client_factory
):
Expand Down Expand Up @@ -989,7 +989,7 @@ def test_list_multiparts_with_prefix_and_delimiter(
resp_dict["ListMultipartUploadsResult"].pop("@xmlns", None)
snapshot.match("list-multiparts-no-encoding", resp_dict)

@pytest.mark.xfail(condition=is_v2_provider, reason="not implemented in moto")
@pytest.mark.xfail(condition=is_v2_provider(), reason="not implemented in moto")
@markers.aws.validated
def test_list_parts_pagination(self, s3_bucket, snapshot, aws_client):
snapshot.add_transformer(
Expand Down Expand Up @@ -1045,6 +1045,44 @@ def test_list_parts_pagination(self, s3_bucket, snapshot, aws_client):
)
snapshot.match("list-parts-wrong-part", response)

@pytest.mark.xfail(
condition=is_v2_provider(), reason="moto does not handle empty query string parameters"
)
@markers.aws.validated
def test_list_parts_empty_part_number_marker(self, s3_bucket, snapshot, aws_client_factory):
# we need to disable validation for this test
s3_client = aws_client_factory(config=Config(parameter_validation=False)).s3
snapshot.add_transformer(
[
snapshot.transform.key_value("Bucket", reference_replacement=False),
snapshot.transform.key_value("Location"),
snapshot.transform.key_value("UploadId"),
snapshot.transform.key_value("DisplayName", reference_replacement=False),
snapshot.transform.key_value("ID", reference_replacement=False),
]
)
object_key = "test-list-part-empty-marker"
response = s3_client.create_multipart_upload(Bucket=s3_bucket, Key=object_key)
upload_id = response["UploadId"]

s3_client.upload_part(
Bucket=s3_bucket,
Key=object_key,
Body=BytesIO(b"data"),
PartNumber=1,
UploadId=upload_id,
)
# it seems S3 does not care about empty string for integer query string parameters
response = s3_client.list_parts(
Bucket=s3_bucket, UploadId=upload_id, Key=object_key, PartNumberMarker=""
)
snapshot.match("list-parts-empty-marker", response)

response = s3_client.list_parts(
Bucket=s3_bucket, UploadId=upload_id, Key=object_key, MaxParts=""
)
snapshot.match("list-parts-empty-max-parts", response)

@markers.aws.validated
def test_get_object_no_such_bucket(self, snapshot, aws_client):
snapshot.add_transformer(snapshot.transform.key_value("BucketName"))
Expand Down
65 changes: 65 additions & 0 deletions tests/aws/services/s3/test_s3.snapshot.json
54E5
Original file line number Diff line number Diff line change
Expand Up @@ -12236,5 +12236,70 @@
}
}
}
},
"tests/aws/services/s3/test_s3.py::TestS3::test_list_parts_empty_part_number_marker": {
"recorded-date": "11-11-2023, 00:20:09",
"recorded-content": {
"list-parts-empty-marker": {
"Bucket": "bucket",
"Initiator": {
"DisplayName": "display-name",
"ID": "i-d"
},
"IsTruncated": false,
"Key": "test-list-part-empty-marker",
"MaxParts": 1000,
"NextPartNumberMarker": 1,
"Owner": {
"DisplayName": "display-name",
"ID": "i-d"
},
"PartNumberMarker": 0,
"Parts": [
{
"ETag": "\"8d777f385d3dfec8815d20f7496026dc\"",
"LastModified": "datetime",
"PartNumber": 1,
"Size": 4
}
],
"StorageClass": "STANDARD",
"UploadId": "<upload-id:1>",
"ResponseMetadata": {
"HTTPHeaders": {},
"HTTPStatusCode": 200
}
},
"list-parts-empty-max-parts": {
"Bucket": "bucket",
"Initiator": {
"DisplayName": "display-name",
"ID": "i-d"
},
"IsTruncated": false,
"Key": "test-list-part-empty-marker",
"MaxParts": 1000,
"NextPartNumberMarker": 1,
"Owner": {
"DisplayName": "display-name",
"ID": "i-d"
},
"PartNumberMarker": 0,
"Parts": [
{
"ETag": "\"8d777f385d3dfec8815d20f7496026dc\"",
"LastModified": "datetime",
"PartNumber": 1,
"Size": 4
}
],
"StorageClass": "STANDARD",
"UploadId": "<upload-id:1>",
"ResponseMetadata": {
"HTTPHeaders": {},
"HTTPStatusCode": 200
}
}
}
}
}
0