DynamoDB: transact_write_items() should raise ValidationException when putting and deleting the same item (#7378)
This commit is contained in:
parent
cfbc275e02
commit
7c26f9f831
@ -579,7 +579,11 @@ class DynamoDBBackend(BaseBackend):
|
||||
item = item["Put"]
|
||||
attrs = item["Item"]
|
||||
table_name = item["TableName"]
|
||||
check_unicity(table_name, item)
|
||||
table = self.get_table(table_name)
|
||||
key = {table.hash_key_attr: attrs[table.hash_key_attr]}
|
||||
if table.range_key_attr is not None:
|
||||
key[table.range_key_attr] = attrs[table.range_key_attr]
|
||||
check_unicity(table_name, key)
|
||||
condition_expression = item.get("ConditionExpression", None)
|
||||
expression_attribute_names = item.get(
|
||||
"ExpressionAttributeNames", None
|
||||
|
@ -46,6 +46,55 @@ def test_multiple_transactions_on_same_item():
|
||||
)
|
||||
|
||||
|
||||
@mock_aws
|
||||
def test_transact_write_items__put_and_delete_on_same_item():
|
||||
schema = {
|
||||
"KeySchema": [
|
||||
{"AttributeName": "pk", "KeyType": "HASH"},
|
||||
{"AttributeName": "sk", "KeyType": "RANGE"},
|
||||
],
|
||||
"AttributeDefinitions": [
|
||||
{"AttributeName": "pk", "AttributeType": "S"},
|
||||
{"AttributeName": "sk", "AttributeType": "S"},
|
||||
],
|
||||
}
|
||||
dynamodb = boto3.client("dynamodb", region_name="us-east-1")
|
||||
dynamodb.create_table(
|
||||
TableName="test-table", BillingMode="PAY_PER_REQUEST", **schema
|
||||
)
|
||||
|
||||
with pytest.raises(ClientError) as exc:
|
||||
dynamodb.transact_write_items(
|
||||
TransactItems=[
|
||||
{
|
||||
"Put": {
|
||||
"TableName": "test-table",
|
||||
"Item": {
|
||||
"pk": {"S": "test-pk"},
|
||||
"sk": {"S": "test-sk"},
|
||||
"field": {"S": "test-field"},
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
"Delete": {
|
||||
"TableName": "test-table",
|
||||
"Key": {
|
||||
"pk": {"S": "test-pk"},
|
||||
"sk": {"S": "test-sk"},
|
||||
},
|
||||
}
|
||||
},
|
||||
]
|
||||
)
|
||||
err = exc.value.response["Error"]
|
||||
assert err["Code"] == "ValidationException"
|
||||
assert (
|
||||
err["Message"]
|
||||
== "Transaction request cannot include multiple operations on one item"
|
||||
)
|
||||
|
||||
|
||||
@mock_aws
|
||||
def test_transact_write_items__too_many_transactions():
|
||||
schema = {
|
||||
|
@ -3856,24 +3856,27 @@ def test_transact_write_items_conditioncheck_passes():
|
||||
dynamodb.create_table(
|
||||
TableName="test-table", BillingMode="PAY_PER_REQUEST", **table_schema
|
||||
)
|
||||
# Insert an item without email address
|
||||
dynamodb.put_item(TableName="test-table", Item={"id": {"S": "foo"}})
|
||||
# Put an email address, after verifying it doesn't exist yet
|
||||
# Insert an item with email address
|
||||
dynamodb.put_item(
|
||||
TableName="test-table",
|
||||
Item={"id": {"S": "foo"}, "email_address": {"S": "foo@moto.com"}},
|
||||
)
|
||||
# Put a new item, after verifying the exising item also has email address
|
||||
dynamodb.transact_write_items(
|
||||
TransactItems=[
|
||||
{
|
||||
"ConditionCheck": {
|
||||
"Key": {"id": {"S": "foo"}},
|
||||
"TableName": "test-table",
|
||||
"ConditionExpression": "attribute_not_exists(#e)",
|
||||
"ConditionExpression": "attribute_exists(#e)",
|
||||
"ExpressionAttributeNames": {"#e": "email_address"},
|
||||
}
|
||||
},
|
||||
{
|
||||
"Put": {
|
||||
"Item": {
|
||||
"id": {"S": "foo"},
|
||||
"email_address": {"S": "test@moto.com"},
|
||||
"id": {"S": "bar"},
|
||||
"email_address": {"S": "bar@moto.com"},
|
||||
},
|
||||
"TableName": "test-table",
|
||||
}
|
||||
@ -3882,8 +3885,9 @@ def test_transact_write_items_conditioncheck_passes():
|
||||
)
|
||||
# Assert all are present
|
||||
items = dynamodb.scan(TableName="test-table")["Items"]
|
||||
assert len(items) == 1
|
||||
assert items[0] == {"email_address": {"S": "test@moto.com"}, "id": {"S": "foo"}}
|
||||
assert len(items) == 2
|
||||
assert items[0] == {"email_address": {"S": "foo@moto.com"}, "id": {"S": "foo"}}
|
||||
assert items[1] == {"email_address": {"S": "bar@moto.com"}, "id": {"S": "bar"}}
|
||||
|
||||
|
||||
@mock_aws
|
||||
@ -3896,12 +3900,12 @@ def test_transact_write_items_conditioncheck_fails():
|
||||
dynamodb.create_table(
|
||||
TableName="test-table", BillingMode="PAY_PER_REQUEST", **table_schema
|
||||
)
|
||||
# Insert an item with email address
|
||||
# Insert an item without email address
|
||||
dynamodb.put_item(
|
||||
TableName="test-table",
|
||||
Item={"id": {"S": "foo"}, "email_address": {"S": "test@moto.com"}},
|
||||
Item={"id": {"S": "foo"}},
|
||||
)
|
||||
# Try to put an email address, but verify whether it exists
|
||||
# Try putting a new item, after verifying the exising item also has email address
|
||||
# ConditionCheck should fail
|
||||
with pytest.raises(ClientError) as ex:
|
||||
dynamodb.transact_write_items(
|
||||
@ -3910,15 +3914,15 @@ def test_transact_write_items_conditioncheck_fails():
|
||||
"ConditionCheck": {
|
||||
"Key": {"id": {"S": "foo"}},
|
||||
"TableName": "test-table",
|
||||
"ConditionExpression": "attribute_not_exists(#e)",
|
||||
"ConditionExpression": "attribute_exists(#e)",
|
||||
"ExpressionAttributeNames": {"#e": "email_address"},
|
||||
}
|
||||
},
|
||||
{
|
||||
"Put": {
|
||||
"Item": {
|
||||
"id": {"S": "foo"},
|
||||
"email_address": {"S": "update@moto.com"},
|
||||
"id": {"S": "bar"},
|
||||
"email_address": {"S": "bar@moto.com"},
|
||||
},
|
||||
"TableName": "test-table",
|
||||
}
|
||||
@ -3929,10 +3933,10 @@ def test_transact_write_items_conditioncheck_fails():
|
||||
assert ex.value.response["Error"]["Code"] == "TransactionCanceledException"
|
||||
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
|
||||
|
||||
# Assert the original email address is still present
|
||||
# Assert the original item is still present
|
||||
items = dynamodb.scan(TableName="test-table")["Items"]
|
||||
assert len(items) == 1
|
||||
assert items[0] == {"email_address": {"S": "test@moto.com"}, "id": {"S": "foo"}}
|
||||
assert items[0] == {"id": {"S": "foo"}}
|
||||
|
||||
|
||||
@mock_aws
|
||||
|
Loading…
Reference in New Issue
Block a user