From c9d69681ece1baeee088baf449a9273b921c941d Mon Sep 17 00:00:00 2001 From: gruebel Date: Sun, 6 Oct 2019 16:49:02 +0200 Subject: [PATCH] Add evaluation of ConditionExpression to DynamoDB2 delete_item --- moto/dynamodb2/models.py | 17 ++++++++++++++--- moto/dynamodb2/responses.py | 18 ++++++++++++++++-- tests/test_dynamodb2/test_dynamodb.py | 17 +++++++++++++++++ 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/moto/dynamodb2/models.py b/moto/dynamodb2/models.py index c06014488..b2ff7ffc3 100644 --- a/moto/dynamodb2/models.py +++ b/moto/dynamodb2/models.py @@ -1092,12 +1092,23 @@ class DynamoDBBackend(BaseBackend): item.update_with_attribute_updates(attribute_updates) return item - def delete_item(self, table_name, keys): + def delete_item(self, table_name, key, expression_attribute_names=None, expression_attribute_values=None, + condition_expression=None): table = self.get_table(table_name) if not table: return None - hash_key, range_key = self.get_keys_value(table, keys) - return table.delete_item(hash_key, range_key) + + hash_value, range_value = self.get_keys_value(table, key) + item = table.get_item(hash_value, range_value) + + condition_op = get_filter_expression( + condition_expression, + expression_attribute_names, + expression_attribute_values) + if not condition_op.expr(item): + raise ValueError('The conditional request failed') + + return table.delete_item(hash_value, range_value) def update_ttl(self, table_name, ttl_spec): table = self.tables.get(table_name) diff --git a/moto/dynamodb2/responses.py b/moto/dynamodb2/responses.py index d07beefd6..9e66a3342 100644 --- a/moto/dynamodb2/responses.py +++ b/moto/dynamodb2/responses.py @@ -579,7 +579,7 @@ class DynamoHandler(BaseResponse): def delete_item(self): name = self.body['TableName'] - keys = self.body['Key'] + key = self.body['Key'] return_values = self.body.get('ReturnValues', 'NONE') if return_values not in ('ALL_OLD', 'NONE'): er = 'com.amazonaws.dynamodb.v20111205#ValidationException' @@ -590,7 +590,21 @@ class DynamoHandler(BaseResponse): er = 'com.amazonaws.dynamodb.v20120810#ConditionalCheckFailedException' return self.error(er, 'A condition specified in the operation could not be evaluated.') - item = self.dynamodb_backend.delete_item(name, keys) + # Attempt to parse simple ConditionExpressions into an Expected + # expression + condition_expression = self.body.get('ConditionExpression') + expression_attribute_names = self.body.get('ExpressionAttributeNames', {}) + expression_attribute_values = self.body.get('ExpressionAttributeValues', {}) + + try: + item = self.dynamodb_backend.delete_item( + name, key, expression_attribute_names, expression_attribute_values, + condition_expression + ) + except ValueError: + er = 'com.amazonaws.dynamodb.v20111205#ConditionalCheckFailedException' + return self.error(er, 'A condition specified in the operation could not be evaluated.') + if item and return_values == 'ALL_OLD': item_dict = item.to_json() else: diff --git a/tests/test_dynamodb2/test_dynamodb.py b/tests/test_dynamodb2/test_dynamodb.py index b0952f101..c4cf05c59 100644 --- a/tests/test_dynamodb2/test_dynamodb.py +++ b/tests/test_dynamodb2/test_dynamodb.py @@ -1995,6 +1995,23 @@ def test_condition_expressions(): }, ) + with assert_raises(client.exceptions.ConditionalCheckFailedException): + client.delete_item( + TableName = 'test1', + Key = { + 'client': {'S': 'client1'}, + 'app': {'S': 'app1'}, + }, + ConditionExpression = 'attribute_not_exists(#existing)', + ExpressionAttributeValues = { + ':match': {'S': 'match'} + }, + ExpressionAttributeNames = { + '#existing': 'existing', + '#match': 'match', + }, + ) + @mock_dynamodb2 def test_condition_expression__attr_doesnt_exist():