Merge pull request #2799 from ImFlog/fix_dynamodb_updated_new
Fix UPDATED_NEW return values differences
This commit is contained in:
commit
71bf314a0f
@ -146,6 +146,9 @@ class DynamoType(object):
|
||||
def __eq__(self, other):
|
||||
return self.type == other.type and self.value == other.value
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.type != other.type or self.value != other.value
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.cast_value < other.cast_value
|
||||
|
||||
|
@ -1,9 +1,12 @@
|
||||
from __future__ import unicode_literals
|
||||
import itertools
|
||||
|
||||
import copy
|
||||
import json
|
||||
import six
|
||||
import re
|
||||
|
||||
import itertools
|
||||
import six
|
||||
|
||||
from moto.core.responses import BaseResponse
|
||||
from moto.core.utils import camelcase_to_underscores, amzn_request_id
|
||||
from .exceptions import InvalidIndexNameError, InvalidUpdateExpression, ItemSizeTooLarge
|
||||
@ -711,7 +714,8 @@ class DynamoHandler(BaseResponse):
|
||||
attribute_updates = self.body.get("AttributeUpdates")
|
||||
expression_attribute_names = self.body.get("ExpressionAttributeNames", {})
|
||||
expression_attribute_values = self.body.get("ExpressionAttributeValues", {})
|
||||
existing_item = self.dynamodb_backend.get_item(name, key)
|
||||
# We need to copy the item in order to avoid it being modified by the update_item operation
|
||||
existing_item = copy.deepcopy(self.dynamodb_backend.get_item(name, key))
|
||||
if existing_item:
|
||||
existing_attributes = existing_item.to_json()["Attributes"]
|
||||
else:
|
||||
@ -797,14 +801,39 @@ class DynamoHandler(BaseResponse):
|
||||
k: v for k, v in existing_attributes.items() if k in changed_attributes
|
||||
}
|
||||
elif return_values == "UPDATED_NEW":
|
||||
item_dict["Attributes"] = {
|
||||
k: v
|
||||
for k, v in item_dict["Attributes"].items()
|
||||
if k in changed_attributes
|
||||
}
|
||||
item_dict["Attributes"] = self._build_updated_new_attributes(
|
||||
existing_attributes, item_dict["Attributes"]
|
||||
)
|
||||
|
||||
return dynamo_json_dump(item_dict)
|
||||
|
||||
def _build_updated_new_attributes(self, original, changed):
|
||||
if type(changed) != type(original):
|
||||
return changed
|
||||
else:
|
||||
if type(changed) is dict:
|
||||
return {
|
||||
key: self._build_updated_new_attributes(
|
||||
original.get(key, None), changed[key]
|
||||
)
|
||||
for key in changed.keys()
|
||||
if changed[key] != original.get(key, None)
|
||||
}
|
||||
elif type(changed) in (set, list):
|
||||
if len(changed) != len(original):
|
||||
return changed
|
||||
else:
|
||||
return [
|
||||
self._build_updated_new_attributes(
|
||||
original[index], changed[index]
|
||||
)
|
||||
for index in range(len(changed))
|
||||
]
|
||||
elif changed != original:
|
||||
return changed
|
||||
else:
|
||||
return None
|
||||
|
||||
def describe_limits(self):
|
||||
return json.dumps(
|
||||
{
|
||||
|
@ -3431,13 +3431,18 @@ def test_update_supports_list_append():
|
||||
)
|
||||
|
||||
# Update item using list_append expression
|
||||
client.update_item(
|
||||
updated_item = client.update_item(
|
||||
TableName="TestTable",
|
||||
Key={"SHA256": {"S": "sha-of-file"}},
|
||||
UpdateExpression="SET crontab = list_append(crontab, :i)",
|
||||
ExpressionAttributeValues={":i": {"L": [{"S": "bar2"}]}},
|
||||
ReturnValues="UPDATED_NEW",
|
||||
)
|
||||
|
||||
# Verify updated item is correct
|
||||
updated_item["Attributes"].should.equal(
|
||||
{"crontab": {"L": [{"S": "bar1"}, {"S": "bar2"}]}}
|
||||
)
|
||||
# Verify item is appended to the existing list
|
||||
result = client.get_item(
|
||||
TableName="TestTable", Key={"SHA256": {"S": "sha-of-file"}}
|
||||
@ -3470,15 +3475,19 @@ def test_update_supports_nested_list_append():
|
||||
)
|
||||
|
||||
# Update item using list_append expression
|
||||
client.update_item(
|
||||
updated_item = client.update_item(
|
||||
TableName="TestTable",
|
||||
Key={"id": {"S": "nested_list_append"}},
|
||||
UpdateExpression="SET a.#b = list_append(a.#b, :i)",
|
||||
ExpressionAttributeValues={":i": {"L": [{"S": "bar2"}]}},
|
||||
ExpressionAttributeNames={"#b": "b"},
|
||||
ReturnValues="UPDATED_NEW",
|
||||
)
|
||||
|
||||
# Verify item is appended to the existing list
|
||||
# Verify updated item is correct
|
||||
updated_item["Attributes"].should.equal(
|
||||
{"a": {"M": {"b": {"L": [{"S": "bar1"}, {"S": "bar2"}]}}}}
|
||||
)
|
||||
result = client.get_item(
|
||||
TableName="TestTable", Key={"id": {"S": "nested_list_append"}}
|
||||
)["Item"]
|
||||
@ -3510,14 +3519,19 @@ def test_update_supports_multiple_levels_nested_list_append():
|
||||
)
|
||||
|
||||
# Update item using list_append expression
|
||||
client.update_item(
|
||||
updated_item = client.update_item(
|
||||
TableName="TestTable",
|
||||
Key={"id": {"S": "nested_list_append"}},
|
||||
UpdateExpression="SET a.#b.c = list_append(a.#b.#c, :i)",
|
||||
ExpressionAttributeValues={":i": {"L": [{"S": "bar2"}]}},
|
||||
ExpressionAttributeNames={"#b": "b", "#c": "c"},
|
||||
ReturnValues="UPDATED_NEW",
|
||||
)
|
||||
|
||||
# Verify updated item is correct
|
||||
updated_item["Attributes"].should.equal(
|
||||
{"a": {"M": {"b": {"M": {"c": {"L": [{"S": "bar1"}, {"S": "bar2"}]}}}}}}
|
||||
)
|
||||
# Verify item is appended to the existing list
|
||||
result = client.get_item(
|
||||
TableName="TestTable", Key={"id": {"S": "nested_list_append"}}
|
||||
@ -3551,14 +3565,19 @@ def test_update_supports_nested_list_append_onto_another_list():
|
||||
)
|
||||
|
||||
# Update item using list_append expression
|
||||
client.update_item(
|
||||
updated_item = client.update_item(
|
||||
TableName="TestTable",
|
||||
Key={"id": {"S": "list_append_another"}},
|
||||
UpdateExpression="SET a.#c = list_append(a.#b, :i)",
|
||||
ExpressionAttributeValues={":i": {"L": [{"S": "bar2"}]}},
|
||||
ExpressionAttributeNames={"#b": "b", "#c": "c"},
|
||||
ReturnValues="UPDATED_NEW",
|
||||
)
|
||||
|
||||
# Verify updated item is correct
|
||||
updated_item["Attributes"].should.equal(
|
||||
{"a": {"M": {"c": {"L": [{"S": "bar1"}, {"S": "bar2"}]}}}}
|
||||
)
|
||||
# Verify item is appended to the existing list
|
||||
result = client.get_item(
|
||||
TableName="TestTable", Key={"id": {"S": "list_append_another"}}
|
||||
@ -3601,13 +3620,18 @@ def test_update_supports_list_append_maps():
|
||||
)
|
||||
|
||||
# Update item using list_append expression
|
||||
client.update_item(
|
||||
updated_item = client.update_item(
|
||||
TableName="TestTable",
|
||||
Key={"id": {"S": "nested_list_append"}, "rid": {"S": "range_key"}},
|
||||
UpdateExpression="SET a = list_append(a, :i)",
|
||||
ExpressionAttributeValues={":i": {"L": [{"M": {"b": {"S": "bar2"}}}]}},
|
||||
ReturnValues="UPDATED_NEW",
|
||||
)
|
||||
|
||||
# Verify updated item is correct
|
||||
updated_item["Attributes"].should.equal(
|
||||
{"a": {"L": [{"M": {"b": {"S": "bar1"}}}, {"M": {"b": {"S": "bar2"}}}]}}
|
||||
)
|
||||
# Verify item is appended to the existing list
|
||||
result = client.query(
|
||||
TableName="TestTable",
|
||||
@ -3643,11 +3667,18 @@ def test_update_supports_list_append_with_nested_if_not_exists_operation():
|
||||
table = dynamo.Table(table_name)
|
||||
|
||||
table.put_item(Item={"Id": "item-id", "nest1": {"nest2": {}}})
|
||||
table.update_item(
|
||||
updated_item = table.update_item(
|
||||
Key={"Id": "item-id"},
|
||||
UpdateExpression="SET nest1.nest2.event_history = list_append(if_not_exists(nest1.nest2.event_history, :empty_list), :new_value)",
|
||||
ExpressionAttributeValues={":empty_list": [], ":new_value": ["some_value"]},
|
||||
ReturnValues="UPDATED_NEW",
|
||||
)
|
||||
|
||||
# Verify updated item is correct
|
||||
updated_item["Attributes"].should.equal(
|
||||
{"nest1": {"nest2": {"event_history": ["some_value"]}}}
|
||||
)
|
||||
|
||||
table.get_item(Key={"Id": "item-id"})["Item"].should.equal(
|
||||
{"Id": "item-id", "nest1": {"nest2": {"event_history": ["some_value"]}}}
|
||||
)
|
||||
@ -3668,11 +3699,18 @@ def test_update_supports_list_append_with_nested_if_not_exists_operation_and_pro
|
||||
table = dynamo.Table(table_name)
|
||||
|
||||
table.put_item(Item={"Id": "item-id", "event_history": ["other_value"]})
|
||||
table.update_item(
|
||||
updated_item = table.update_item(
|
||||
Key={"Id": "item-id"},
|
||||
UpdateExpression="SET event_history = list_append(if_not_exists(event_history, :empty_list), :new_value)",
|
||||
ExpressionAttributeValues={":empty_list": [], ":new_value": ["some_value"]},
|
||||
ReturnValues="UPDATED_NEW",
|
||||
)
|
||||
|
||||
# Verify updated item is correct
|
||||
updated_item["Attributes"].should.equal(
|
||||
{"event_history": ["other_value", "some_value"]}
|
||||
)
|
||||
|
||||
table.get_item(Key={"Id": "item-id"})["Item"].should.equal(
|
||||
{"Id": "item-id", "event_history": ["other_value", "some_value"]}
|
||||
)
|
||||
@ -3759,11 +3797,16 @@ def test_update_nested_item_if_original_value_is_none():
|
||||
)
|
||||
table = dynamo.Table("origin-rbu-dev")
|
||||
table.put_item(Item={"job_id": "a", "job_details": {"job_name": None}})
|
||||
table.update_item(
|
||||
updated_item = table.update_item(
|
||||
Key={"job_id": "a"},
|
||||
UpdateExpression="SET job_details.job_name = :output",
|
||||
ExpressionAttributeValues={":output": "updated"},
|
||||
ReturnValues="UPDATED_NEW",
|
||||
)
|
||||
|
||||
# Verify updated item is correct
|
||||
updated_item["Attributes"].should.equal({"job_details": {"job_name": "updated"}})
|
||||
|
||||
table.scan()["Items"][0]["job_details"]["job_name"].should.equal("updated")
|
||||
|
||||
|
||||
@ -3779,11 +3822,16 @@ def test_allow_update_to_item_with_different_type():
|
||||
table = dynamo.Table("origin-rbu-dev")
|
||||
table.put_item(Item={"job_id": "a", "job_details": {"job_name": {"nested": "yes"}}})
|
||||
table.put_item(Item={"job_id": "b", "job_details": {"job_name": {"nested": "yes"}}})
|
||||
table.update_item(
|
||||
updated_item = table.update_item(
|
||||
Key={"job_id": "a"},
|
||||
UpdateExpression="SET job_details.job_name = :output",
|
||||
ExpressionAttributeValues={":output": "updated"},
|
||||
ReturnValues="UPDATED_NEW",
|
||||
)
|
||||
|
||||
# Verify updated item is correct
|
||||
updated_item["Attributes"].should.equal({"job_details": {"job_name": "updated"}})
|
||||
|
||||
table.get_item(Key={"job_id": "a"})["Item"]["job_details"][
|
||||
"job_name"
|
||||
].should.be.equal("updated")
|
||||
|
Loading…
Reference in New Issue
Block a user