From e3c859868c8cb0c8f08a67c2f33fb8eddffdabad Mon Sep 17 00:00:00 2001 From: Alan Jaffe Date: Tue, 7 Jul 2015 15:07:32 -0400 Subject: [PATCH] Adds the ConditionalCheckFailedException to put_item MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the Item‘s original data is inconsistent with what's in DynamoDB, the request should fail (unless overwrite is set to True). http://boto.readthedocs.org/en/latest/ref/dynamodb2.html#boto.dynamodb2.table.Table.put_item --- moto/dynamodb2/models.py | 35 ++++++++++++++++++++++++++++++++--- moto/dynamodb2/responses.py | 12 +++++++++++- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/moto/dynamodb2/models.py b/moto/dynamodb2/models.py index 56a8fb4c0..b538deb88 100644 --- a/moto/dynamodb2/models.py +++ b/moto/dynamodb2/models.py @@ -191,7 +191,7 @@ class Table(object): keys.append(key['AttributeName']) return keys - def put_item(self, item_attrs): + def put_item(self, item_attrs, expected = None, overwrite = False): hash_value = DynamoType(item_attrs.get(self.hash_key_attr)) if self.has_range_key: range_value = DynamoType(item_attrs.get(self.range_key_attr)) @@ -200,6 +200,35 @@ class Table(object): item = Item(hash_value, self.hash_key_type, range_value, self.range_key_type, item_attrs) + if not overwrite: + if expected is None: + expected = {} + lookup_range_value = range_value + else: + expected_range_value = expected.get(self.range_key_attr, {}).get("Value") + if(expected_range_value is None): + lookup_range_value = range_value + else: + lookup_range_value = DynamoType(expected_range_value) + + current = self.get_item(hash_value, lookup_range_value) + + if current is None: + current_attr = {} + elif hasattr(current,'attrs'): + current_attr = current.attrs + else: + current_attr = current + + for key, val in expected.iteritems(): + if 'Exists' in val and val['Exists'] == False: + if key in current_attr: + raise ValueError("The conditional request failed") + elif key not in current_attr: + raise ValueError("The conditional request failed") + elif DynamoType(val['Value']).value != current_attr[key].value: + raise ValueError("The conditional request failed") + if range_value: self.items[hash_value][range_value] = item else: @@ -317,11 +346,11 @@ class DynamoDBBackend(BaseBackend): table.throughput = throughput return table - def put_item(self, table_name, item_attrs): + def put_item(self, table_name, item_attrs, expected = None, overwrite = False): table = self.tables.get(table_name) if not table: return None - return table.put_item(item_attrs) + return table.put_item(item_attrs, expected, overwrite) def get_table_keys_name(self, table_name, keys): """ diff --git a/moto/dynamodb2/responses.py b/moto/dynamodb2/responses.py index 8cee08ebe..5713910d9 100644 --- a/moto/dynamodb2/responses.py +++ b/moto/dynamodb2/responses.py @@ -134,7 +134,17 @@ class DynamoHandler(BaseResponse): def put_item(self): name = self.body['TableName'] item = self.body['Item'] - result = dynamodb_backend2.put_item(name, item) + overwrite = 'Expected' not in self.body + if not overwrite: + expected = self.body['Expected'] + else: + expected = None + + try: + result = dynamodb_backend2.put_item(name, item, expected, overwrite) + except Exception: + er = 'com.amazonaws.dynamodb.v20111205#ConditionalCheckFailedException' + return self.error(er) if result: item_dict = result.to_json()