DynamoDB: fix: support top level lists for Dynamo Item to_json (#7380)
This commit is contained in:
parent
082e6c2fc0
commit
6505971ecb
@ -316,14 +316,20 @@ class Item(BaseModel):
|
||||
return sum(bytesize(key) + value.size() for key, value in self.attrs.items())
|
||||
|
||||
def to_json(self) -> Dict[str, Any]:
|
||||
attributes = {}
|
||||
attributes: Dict[str, Any] = {}
|
||||
for attribute_key, attribute in self.attrs.items():
|
||||
if isinstance(attribute.value, dict):
|
||||
attr_value = {
|
||||
attr_dict_value = {
|
||||
key: value.to_regular_json()
|
||||
for key, value in attribute.value.items()
|
||||
}
|
||||
attributes[attribute_key] = {attribute.type: attr_value}
|
||||
attributes[attribute_key] = {attribute.type: attr_dict_value}
|
||||
elif isinstance(attribute.value, list):
|
||||
attr_list_value = [
|
||||
value.to_regular_json() if isinstance(value, DynamoType) else value
|
||||
for value in attribute.value
|
||||
]
|
||||
attributes[attribute_key] = {attribute.type: attr_list_value}
|
||||
else:
|
||||
attributes[attribute_key] = {attribute.type: attribute.value}
|
||||
|
||||
|
@ -964,12 +964,11 @@ class DynamoHandler(BaseResponse):
|
||||
if len(changed) != len(original):
|
||||
return changed
|
||||
else:
|
||||
return [
|
||||
self._build_updated_new_attributes(
|
||||
original[index], changed[index]
|
||||
)
|
||||
any_element_has_changed = any(
|
||||
changed[index] != original[index]
|
||||
for index in range(len(changed))
|
||||
]
|
||||
)
|
||||
return changed if any_element_has_changed else original
|
||||
else:
|
||||
return changed
|
||||
|
||||
|
@ -3,6 +3,7 @@ from decimal import Decimal
|
||||
|
||||
import boto3
|
||||
import pytest
|
||||
from botocore.exceptions import ClientError
|
||||
|
||||
from moto import mock_aws
|
||||
|
||||
@ -421,3 +422,153 @@ def test_condition_expression_with_reserved_keyword_as_attr_name():
|
||||
|
||||
item = table.get_item(Key={"id": "key-0"})["Item"]
|
||||
assert item == {"id": "key-0", "first": {}}
|
||||
|
||||
|
||||
@mock_aws
|
||||
def test_condition_check_failure_exception_is_raised_when_values_are_returned_for_an_item_with_a_top_level_list():
|
||||
# This explicitly tests for a failure in handling JSONification of DynamoType
|
||||
# when lists are at the top level of an item.
|
||||
# This exception should not be raised:
|
||||
# TypeError: Object of type DynamoType is not JSON serializable
|
||||
|
||||
dynamodb_client = boto3.client("dynamodb", region_name="us-east-1")
|
||||
dynamodb_client.create_table(
|
||||
TableName="example_table",
|
||||
KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
|
||||
AttributeDefinitions=[
|
||||
{"AttributeName": "id", "AttributeType": "S"},
|
||||
],
|
||||
BillingMode="PAY_PER_REQUEST",
|
||||
)
|
||||
record = {
|
||||
"id": {"S": "example_id"},
|
||||
"some_list": {"L": [{"M": {"hello": {"S": "h"}}}]},
|
||||
}
|
||||
dynamodb_client.put_item(
|
||||
TableName="example_table",
|
||||
Item=record,
|
||||
)
|
||||
|
||||
with pytest.raises(ClientError) as error:
|
||||
dynamodb_client.update_item(
|
||||
TableName="example_table",
|
||||
Key={"id": {"S": "example_id"}},
|
||||
UpdateExpression="set some_list=list_append(some_list, :w)",
|
||||
ExpressionAttributeValues={
|
||||
":w": {"L": [{"M": {"world": {"S": "w"}}}]},
|
||||
":id": {"S": "incorrect id"},
|
||||
},
|
||||
ConditionExpression="id = :id",
|
||||
ReturnValuesOnConditionCheckFailure="ALL_OLD",
|
||||
)
|
||||
|
||||
assert error.type.__name__ == "ConditionalCheckFailedException"
|
||||
assert error.value.response["Error"] == {
|
||||
"Message": "The conditional request failed",
|
||||
"Code": "ConditionalCheckFailedException",
|
||||
}
|
||||
assert error.value.response["Item"] == {
|
||||
"id": {"S": "example_id"},
|
||||
"some_list": {"L": [{"M": {"hello": {"S": "h"}}}]},
|
||||
}
|
||||
|
||||
|
||||
@mock_aws
|
||||
def test_condition_check_failure_exception_is_raised_when_values_are_returned_for_an_item_with_a_top_level_string_set():
|
||||
# This explicitly tests for a failure in handling JSONification of DynamoType
|
||||
# when string sets are at the top level of an item.
|
||||
# These exception should not be raised:
|
||||
# TypeError: Object of type DynamoType is not JSON serializable
|
||||
# AttributeError: 'str' object has no attribute 'to_regular_json'
|
||||
|
||||
dynamodb_client = boto3.client("dynamodb", region_name="us-east-1")
|
||||
dynamodb_client.create_table(
|
||||
TableName="example_table",
|
||||
KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
|
||||
AttributeDefinitions=[
|
||||
{"AttributeName": "id", "AttributeType": "S"},
|
||||
],
|
||||
BillingMode="PAY_PER_REQUEST",
|
||||
)
|
||||
record = {
|
||||
"id": {"S": "example_id"},
|
||||
"some_list": {"SS": ["hello"]},
|
||||
}
|
||||
dynamodb_client.put_item(
|
||||
TableName="example_table",
|
||||
Item=record,
|
||||
)
|
||||
|
||||
with pytest.raises(ClientError) as error:
|
||||
dynamodb_client.update_item(
|
||||
TableName="example_table",
|
||||
Key={"id": {"S": "example_id"}},
|
||||
UpdateExpression="set some_list=list_append(some_list, :w)",
|
||||
ExpressionAttributeValues={
|
||||
":w": {"SS": ["world"]},
|
||||
":id": {"S": "incorrect id"},
|
||||
},
|
||||
ConditionExpression="id = :id",
|
||||
ReturnValuesOnConditionCheckFailure="ALL_OLD",
|
||||
)
|
||||
|
||||
assert error.type.__name__ == "ConditionalCheckFailedException"
|
||||
assert error.value.response["Error"] == {
|
||||
"Message": "The conditional request failed",
|
||||
"Code": "ConditionalCheckFailedException",
|
||||
}
|
||||
assert error.value.response["Item"] == {
|
||||
"id": {"S": "example_id"},
|
||||
"some_list": {"SS": ["hello"]},
|
||||
}
|
||||
|
||||
|
||||
@mock_aws
|
||||
def test_condition_check_failure_exception_is_raised_when_values_are_returned_for_an_item_with_a_list_in_a_map():
|
||||
# This explicitly tests for a failure in handling JSONification of DynamoType
|
||||
# when lists are inside a map
|
||||
|
||||
dynamodb_client = boto3.client("dynamodb", region_name="us-east-1")
|
||||
dynamodb_client.create_table(
|
||||
TableName="example_table",
|
||||
KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
|
||||
AttributeDefinitions=[
|
||||
{"AttributeName": "id", "AttributeType": "S"},
|
||||
],
|
||||
BillingMode="PAY_PER_REQUEST",
|
||||
)
|
||||
record = {
|
||||
"id": {"S": "example_id"},
|
||||
"some_list_in_a_map": {
|
||||
"M": {"some_list": {"L": [{"M": {"hello": {"S": "h"}}}]}}
|
||||
},
|
||||
}
|
||||
dynamodb_client.put_item(
|
||||
TableName="example_table",
|
||||
Item=record,
|
||||
)
|
||||
|
||||
with pytest.raises(ClientError) as error:
|
||||
dynamodb_client.update_item(
|
||||
TableName="example_table",
|
||||
Key={"id": {"S": "example_id"}},
|
||||
UpdateExpression="set some_list_in_a_map.some_list=list_append(some_list_in_a_map.some_list, :w)",
|
||||
ExpressionAttributeValues={
|
||||
":w": {"L": [{"M": {"world": {"S": "w"}}}]},
|
||||
":id": {"S": "incorrect id"},
|
||||
},
|
||||
ConditionExpression="id = :id",
|
||||
ReturnValuesOnConditionCheckFailure="ALL_OLD",
|
||||
)
|
||||
|
||||
assert error.type.__name__ == "ConditionalCheckFailedException"
|
||||
assert error.value.response["Error"] == {
|
||||
"Message": "The conditional request failed",
|
||||
"Code": "ConditionalCheckFailedException",
|
||||
}
|
||||
assert error.value.response["Item"] == {
|
||||
"id": {"S": "example_id"},
|
||||
"some_list_in_a_map": {
|
||||
"M": {"some_list": {"L": [{"M": {"hello": {"S": "h"}}}]}}
|
||||
},
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user