Skip to content

Commit

Permalink
DynamoDB: update_item() now validates empty string sets (#8031)
Browse files Browse the repository at this point in the history
  • Loading branch information
bblommers authored Aug 23, 2024
1 parent 9d7a102 commit ed926f0
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 27 deletions.
32 changes: 15 additions & 17 deletions moto/dynamodb/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,23 +122,24 @@ def validate_put_has_empty_keys(
raise MockValidationException(msg.format(empty_key))


def put_has_empty_attrs(field_updates: Dict[str, Any], table: Table) -> bool:
def validate_put_has_empty_attrs(field_updates: Dict[str, Any], table: Table) -> None:
# Example invalid attribute: [{'M': {'SS': {'NS': []}}}]
def _validate_attr(attr: Dict[str, Any]) -> bool:
if "NS" in attr and attr["NS"] == []:
return True
def _validate_attr(attr: Dict[str, Any]) -> None:
for set_type, error in [("NS", "number"), ("SS", "string")]:
if set_type in attr and attr[set_type] == []:
raise MockValidationException(
f"One or more parameter values were invalid: An {error} set may not be empty"
)

else:
return any(
[_validate_attr(val) for val in attr.values() if isinstance(val, dict)]
)
for val in attr.values():
if isinstance(val, dict):
_validate_attr(val)

if table:
key_names = table.attribute_keys
attrs_to_check = [
val for attr, val in field_updates.items() if attr not in key_names
]
return any([_validate_attr(attr) for attr in attrs_to_check])
return False
for attr, val in field_updates.items():
if attr not in table.attribute_keys:
_validate_attr(val)


def validate_put_has_gsi_keys_set_to_none(item: Dict[str, Any], table: Table) -> None:
Expand Down Expand Up @@ -475,10 +476,7 @@ def put_item(self) -> str:

table = self.dynamodb_backend.get_table(name)
validate_put_has_empty_keys(item, table)
if put_has_empty_attrs(item, table):
raise MockValidationException(
"One or more parameter values were invalid: An number set may not be empty"
)
validate_put_has_empty_attrs(item, table)
validate_put_has_gsi_keys_set_to_none(item, table)

overwrite = "Expected" not in self.body
Expand Down
27 changes: 17 additions & 10 deletions tests/test_dynamodb/exceptions/test_dynamodb_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,26 +694,33 @@ def test_put_item_wrong_datatype():
assert err["Message"] == "NUMBER_VALUE cannot be converted to String"


@mock_aws
def test_put_item_empty_set():
@dynamodb_aws_verified()
@pytest.mark.aws_verified
def test_put_item_empty_set(table_name=None):
client = boto3.client("dynamodb", region_name="us-east-1")
dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
client.create_table(
TableName="test-table",
KeySchema=[{"AttributeName": "Key", "KeyType": "HASH"}],
AttributeDefinitions=[{"AttributeName": "Key", "AttributeType": "S"}],
BillingMode="PAY_PER_REQUEST",
)
table = dynamodb.Table("test-table")

table = dynamodb.Table(table_name)
with pytest.raises(ClientError) as exc:
table.put_item(Item={"Key": "some-irrelevant_key", "attr2": {"SS": set([])}})
table.put_item(Item={"pk": "some-irrelevant_key", "attr2": {"SS": set([])}})
err = exc.value.response["Error"]
assert err["Code"] == "ValidationException"
assert (
err["Message"]
== "One or more parameter values were invalid: An number set may not be empty"
)

with pytest.raises(ClientError) as exc:
client.put_item(
TableName=table_name, Item={"pk": {"S": "foo"}, "stringSet": {"SS": []}}
)
err = exc.value.response["Error"]
assert err["Code"] == "ValidationException"
assert (
err["Message"]
== "One or more parameter values were invalid: An string set may not be empty"
)


@mock_aws
def test_put_item_returns_old_item():
Expand Down

0 comments on commit ed926f0

Please sign in to comment.