Make EQ conditions work reliably in DynamoDB.

The AWS API represents a set object as a list of values. Internally
moto also represents a set as a list. This means that when we do value
comparisons, the order of the values can cause a set equality test to
fail.
This commit is contained in:
Gary Donovan 2019-01-10 21:39:12 +11:00
parent 850496f29a
commit 0b15bb13b6
2 changed files with 47 additions and 10 deletions

View File

@ -66,6 +66,8 @@ class DynamoType(object):
return int(self.value)
except ValueError:
return float(self.value)
elif self.is_set():
return set(self.value)
else:
return self.value
@ -509,15 +511,12 @@ class Table(BaseModel):
elif 'Value' in val and DynamoType(val['Value']).value != current_attr[key].value:
raise ValueError("The conditional request failed")
elif 'ComparisonOperator' in val:
comparison_func = get_comparison_func(
val['ComparisonOperator'])
dynamo_types = [
DynamoType(ele) for ele in
val.get("AttributeValueList", [])
]
for t in dynamo_types:
if not comparison_func(current_attr[key].value, t.value):
raise ValueError('The conditional request failed')
if not current_attr[key].compare(val['ComparisonOperator'], dynamo_types):
raise ValueError('The conditional request failed')
if range_value:
self.items[hash_value][range_value] = item
else:
@ -946,15 +945,12 @@ class DynamoDBBackend(BaseBackend):
elif 'Value' in val and DynamoType(val['Value']).value != item_attr[key].value:
raise ValueError("The conditional request failed")
elif 'ComparisonOperator' in val:
comparison_func = get_comparison_func(
val['ComparisonOperator'])
dynamo_types = [
DynamoType(ele) for ele in
val.get("AttributeValueList", [])
]
for t in dynamo_types:
if not comparison_func(item_attr[key].value, t.value):
raise ValueError('The conditional request failed')
if not item_attr[key].compare(val['ComparisonOperator'], dynamo_types):
raise ValueError('The conditional request failed')
# Update does not fail on new items, so create one
if item is None:

View File

@ -750,6 +750,47 @@ def test_boto3_update_item_conditions_pass_because_expect_exists_by_compare_to_n
returned_item = table.get_item(Key={'username': 'johndoe'})
assert dict(returned_item)['Item']['foo'].should.equal("baz")
@mock_dynamodb2
def test_boto3_update_settype_item_with_conditions():
class OrderedSet(set):
"""A set with predictable iteration order"""
def __init__(self, values):
super(OrderedSet, self).__init__(values)
self.__ordered_values = values
def __iter__(self):
return iter(self.__ordered_values)
table = _create_user_table()
table.put_item(Item={'username': 'johndoe'})
table.update_item(
Key={'username': 'johndoe'},
UpdateExpression='SET foo=:new_value',
ExpressionAttributeValues={
':new_value': OrderedSet(['hello', 'world']),
},
)
table.update_item(
Key={'username': 'johndoe'},
UpdateExpression='SET foo=:new_value',
ExpressionAttributeValues={
':new_value': set(['baz']),
},
Expected={
'foo': {
'ComparisonOperator': 'EQ',
'AttributeValueList': [
OrderedSet(['world', 'hello']), # Opposite order to original
],
}
},
)
returned_item = table.get_item(Key={'username': 'johndoe'})
assert dict(returned_item)['Item']['foo'].should.equal(set(['baz']))
@mock_dynamodb2
def test_boto3_put_item_conditions_pass():
table = _create_user_table()