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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user