| 
									
										
										
										
											2022-01-25 19:26:11 +09:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2023-11-30 07:55:51 -08:00
										 |  |  | from decimal import Decimal | 
					
						
							| 
									
										
										
										
											2022-01-25 19:26:11 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 13:09:12 +00:00
										 |  |  | import boto3 | 
					
						
							|  |  |  | import pytest | 
					
						
							| 
									
										
										
										
											2024-02-23 19:55:37 +00:00
										 |  |  | from botocore.exceptions import ClientError | 
					
						
							| 
									
										
										
										
											2023-11-30 07:55:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-07 12:03:33 +00:00
										 |  |  | from moto import mock_aws | 
					
						
							| 
									
										
										
										
											2021-10-09 13:09:12 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-07 12:03:33 +00:00
										 |  |  | @mock_aws | 
					
						
							| 
									
										
										
										
											2021-10-09 13:09:12 +00:00
										 |  |  | def test_condition_expression_with_dot_in_attr_name(): | 
					
						
							|  |  |  |     dynamodb = boto3.resource("dynamodb", region_name="us-east-2") | 
					
						
							|  |  |  |     table_name = "Test" | 
					
						
							|  |  |  |     dynamodb.create_table( | 
					
						
							|  |  |  |         TableName=table_name, | 
					
						
							|  |  |  |         KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], | 
					
						
							|  |  |  |         AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}], | 
					
						
							| 
									
										
										
										
											2022-02-10 19:09:45 -01:00
										 |  |  |         BillingMode="PAY_PER_REQUEST", | 
					
						
							| 
									
										
										
										
											2021-10-09 13:09:12 +00:00
										 |  |  |     ) | 
					
						
							|  |  |  |     table = dynamodb.Table(table_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     email_like_str = "test@foo.com" | 
					
						
							|  |  |  |     record = {"id": "key-0", "first": {email_like_str: {"third": {"VALUE"}}}} | 
					
						
							|  |  |  |     table.put_item(Item=record) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     table.update_item( | 
					
						
							|  |  |  |         Key={"id": "key-0"}, | 
					
						
							|  |  |  |         UpdateExpression="REMOVE #first.#second, #other", | 
					
						
							|  |  |  |         ExpressionAttributeNames={ | 
					
						
							|  |  |  |             "#first": "first", | 
					
						
							|  |  |  |             "#second": email_like_str, | 
					
						
							|  |  |  |             "#third": "third", | 
					
						
							|  |  |  |             "#other": "other", | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         ExpressionAttributeValues={":value": "VALUE", ":one": 1}, | 
					
						
							|  |  |  |         ConditionExpression="size(#first.#second.#third) = :one AND contains(#first.#second.#third, :value)", | 
					
						
							|  |  |  |         ReturnValues="ALL_NEW", | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     item = table.get_item(Key={"id": "key-0"})["Item"] | 
					
						
							| 
									
										
										
										
											2023-07-13 10:21:47 +00:00
										 |  |  |     assert item == {"id": "key-0", "first": {}} | 
					
						
							| 
									
										
										
										
											2021-10-09 13:09:12 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-07 12:03:33 +00:00
										 |  |  | @mock_aws | 
					
						
							| 
									
										
										
										
											2021-10-09 13:09:12 +00:00
										 |  |  | def test_condition_expressions(): | 
					
						
							|  |  |  |     client = boto3.client("dynamodb", region_name="us-east-1") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Create the DynamoDB table. | 
					
						
							|  |  |  |     client.create_table( | 
					
						
							|  |  |  |         TableName="test1", | 
					
						
							|  |  |  |         AttributeDefinitions=[ | 
					
						
							|  |  |  |             {"AttributeName": "client", "AttributeType": "S"}, | 
					
						
							|  |  |  |             {"AttributeName": "app", "AttributeType": "S"}, | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |         KeySchema=[ | 
					
						
							|  |  |  |             {"AttributeName": "client", "KeyType": "HASH"}, | 
					
						
							|  |  |  |             {"AttributeName": "app", "KeyType": "RANGE"}, | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |         ProvisionedThroughput={"ReadCapacityUnits": 123, "WriteCapacityUnits": 123}, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     client.put_item( | 
					
						
							|  |  |  |         TableName="test1", | 
					
						
							|  |  |  |         Item={ | 
					
						
							|  |  |  |             "client": {"S": "client1"}, | 
					
						
							|  |  |  |             "app": {"S": "app1"}, | 
					
						
							|  |  |  |             "match": {"S": "match"}, | 
					
						
							|  |  |  |             "existing": {"S": "existing"}, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     client.put_item( | 
					
						
							|  |  |  |         TableName="test1", | 
					
						
							|  |  |  |         Item={ | 
					
						
							|  |  |  |             "client": {"S": "client1"}, | 
					
						
							|  |  |  |             "app": {"S": "app1"}, | 
					
						
							|  |  |  |             "match": {"S": "match"}, | 
					
						
							|  |  |  |             "existing": {"S": "existing"}, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         ConditionExpression="attribute_exists(#existing) AND attribute_not_exists(#nonexistent) AND #match = :match", | 
					
						
							|  |  |  |         ExpressionAttributeNames={ | 
					
						
							|  |  |  |             "#existing": "existing", | 
					
						
							|  |  |  |             "#nonexistent": "nope", | 
					
						
							|  |  |  |             "#match": "match", | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         ExpressionAttributeValues={":match": {"S": "match"}}, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     client.put_item( | 
					
						
							|  |  |  |         TableName="test1", | 
					
						
							|  |  |  |         Item={ | 
					
						
							|  |  |  |             "client": {"S": "client1"}, | 
					
						
							|  |  |  |             "app": {"S": "app1"}, | 
					
						
							|  |  |  |             "match": {"S": "match"}, | 
					
						
							|  |  |  |             "existing": {"S": "existing"}, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         ConditionExpression="NOT(attribute_exists(#nonexistent1) AND attribute_exists(#nonexistent2))", | 
					
						
							|  |  |  |         ExpressionAttributeNames={"#nonexistent1": "nope", "#nonexistent2": "nope2"}, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     client.put_item( | 
					
						
							|  |  |  |         TableName="test1", | 
					
						
							|  |  |  |         Item={ | 
					
						
							|  |  |  |             "client": {"S": "client1"}, | 
					
						
							|  |  |  |             "app": {"S": "app1"}, | 
					
						
							|  |  |  |             "match": {"S": "match"}, | 
					
						
							|  |  |  |             "existing": {"S": "existing"}, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         ConditionExpression="attribute_exists(#nonexistent) OR attribute_exists(#existing)", | 
					
						
							|  |  |  |         ExpressionAttributeNames={"#nonexistent": "nope", "#existing": "existing"}, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     client.put_item( | 
					
						
							|  |  |  |         TableName="test1", | 
					
						
							|  |  |  |         Item={ | 
					
						
							|  |  |  |             "client": {"S": "client1"}, | 
					
						
							|  |  |  |             "app": {"S": "app1"}, | 
					
						
							|  |  |  |             "match": {"S": "match"}, | 
					
						
							|  |  |  |             "existing": {"S": "existing"}, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         ConditionExpression="#client BETWEEN :a AND :z", | 
					
						
							|  |  |  |         ExpressionAttributeNames={"#client": "client"}, | 
					
						
							|  |  |  |         ExpressionAttributeValues={":a": {"S": "a"}, ":z": {"S": "z"}}, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     client.put_item( | 
					
						
							|  |  |  |         TableName="test1", | 
					
						
							|  |  |  |         Item={ | 
					
						
							|  |  |  |             "client": {"S": "client1"}, | 
					
						
							|  |  |  |             "app": {"S": "app1"}, | 
					
						
							|  |  |  |             "match": {"S": "match"}, | 
					
						
							|  |  |  |             "existing": {"S": "existing"}, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         ConditionExpression="#client IN (:client1, :client2)", | 
					
						
							|  |  |  |         ExpressionAttributeNames={"#client": "client"}, | 
					
						
							|  |  |  |         ExpressionAttributeValues={ | 
					
						
							|  |  |  |             ":client1": {"S": "client1"}, | 
					
						
							|  |  |  |             ":client2": {"S": "client2"}, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with pytest.raises(client.exceptions.ConditionalCheckFailedException): | 
					
						
							|  |  |  |         client.put_item( | 
					
						
							|  |  |  |             TableName="test1", | 
					
						
							|  |  |  |             Item={ | 
					
						
							|  |  |  |                 "client": {"S": "client1"}, | 
					
						
							|  |  |  |                 "app": {"S": "app1"}, | 
					
						
							|  |  |  |                 "match": {"S": "match"}, | 
					
						
							|  |  |  |                 "existing": {"S": "existing"}, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             ConditionExpression="attribute_exists(#nonexistent1) AND attribute_exists(#nonexistent2)", | 
					
						
							|  |  |  |             ExpressionAttributeNames={ | 
					
						
							|  |  |  |                 "#nonexistent1": "nope", | 
					
						
							|  |  |  |                 "#nonexistent2": "nope2", | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with pytest.raises(client.exceptions.ConditionalCheckFailedException): | 
					
						
							|  |  |  |         client.put_item( | 
					
						
							|  |  |  |             TableName="test1", | 
					
						
							|  |  |  |             Item={ | 
					
						
							|  |  |  |                 "client": {"S": "client1"}, | 
					
						
							|  |  |  |                 "app": {"S": "app1"}, | 
					
						
							|  |  |  |                 "match": {"S": "match"}, | 
					
						
							|  |  |  |                 "existing": {"S": "existing"}, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             ConditionExpression="NOT(attribute_not_exists(#nonexistent1) AND attribute_not_exists(#nonexistent2))", | 
					
						
							|  |  |  |             ExpressionAttributeNames={ | 
					
						
							|  |  |  |                 "#nonexistent1": "nope", | 
					
						
							|  |  |  |                 "#nonexistent2": "nope2", | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with pytest.raises(client.exceptions.ConditionalCheckFailedException): | 
					
						
							|  |  |  |         client.put_item( | 
					
						
							|  |  |  |             TableName="test1", | 
					
						
							|  |  |  |             Item={ | 
					
						
							|  |  |  |                 "client": {"S": "client1"}, | 
					
						
							|  |  |  |                 "app": {"S": "app1"}, | 
					
						
							|  |  |  |                 "match": {"S": "match"}, | 
					
						
							|  |  |  |                 "existing": {"S": "existing"}, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             ConditionExpression="attribute_exists(#existing) AND attribute_not_exists(#nonexistent) AND #match = :match", | 
					
						
							|  |  |  |             ExpressionAttributeNames={ | 
					
						
							|  |  |  |                 "#existing": "existing", | 
					
						
							|  |  |  |                 "#nonexistent": "nope", | 
					
						
							|  |  |  |                 "#match": "match", | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             ExpressionAttributeValues={":match": {"S": "match2"}}, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Make sure update_item honors ConditionExpression as well | 
					
						
							|  |  |  |     client.update_item( | 
					
						
							|  |  |  |         TableName="test1", | 
					
						
							|  |  |  |         Key={"client": {"S": "client1"}, "app": {"S": "app1"}}, | 
					
						
							|  |  |  |         UpdateExpression="set #match=:match", | 
					
						
							|  |  |  |         ConditionExpression="attribute_exists(#existing)", | 
					
						
							|  |  |  |         ExpressionAttributeNames={"#existing": "existing", "#match": "match"}, | 
					
						
							|  |  |  |         ExpressionAttributeValues={":match": {"S": "match"}}, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with pytest.raises(client.exceptions.ConditionalCheckFailedException) as exc: | 
					
						
							|  |  |  |         client.update_item( | 
					
						
							|  |  |  |             TableName="test1", | 
					
						
							|  |  |  |             Key={"client": {"S": "client1"}, "app": {"S": "app1"}}, | 
					
						
							|  |  |  |             UpdateExpression="set #match=:match", | 
					
						
							|  |  |  |             ConditionExpression="attribute_not_exists(#existing)", | 
					
						
							|  |  |  |             ExpressionAttributeValues={":match": {"S": "match"}}, | 
					
						
							|  |  |  |             ExpressionAttributeNames={"#existing": "existing", "#match": "match"}, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     _assert_conditional_check_failed_exception(exc) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with pytest.raises(client.exceptions.ConditionalCheckFailedException) as exc: | 
					
						
							|  |  |  |         client.update_item( | 
					
						
							|  |  |  |             TableName="test1", | 
					
						
							|  |  |  |             Key={"client": {"S": "client2"}, "app": {"S": "app1"}}, | 
					
						
							|  |  |  |             UpdateExpression="set #match=:match", | 
					
						
							|  |  |  |             ConditionExpression="attribute_exists(#existing)", | 
					
						
							|  |  |  |             ExpressionAttributeValues={":match": {"S": "match"}}, | 
					
						
							|  |  |  |             ExpressionAttributeNames={"#existing": "existing", "#match": "match"}, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     _assert_conditional_check_failed_exception(exc) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with pytest.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"}, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _assert_conditional_check_failed_exception(exc): | 
					
						
							|  |  |  |     err = exc.value.response["Error"] | 
					
						
							| 
									
										
										
										
											2023-07-13 10:21:47 +00:00
										 |  |  |     assert err["Code"] == "ConditionalCheckFailedException" | 
					
						
							|  |  |  |     assert err["Message"] == "The conditional request failed" | 
					
						
							| 
									
										
										
										
											2021-10-09 13:09:12 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-07 12:03:33 +00:00
										 |  |  | @mock_aws | 
					
						
							| 
									
										
										
										
											2021-10-09 13:09:12 +00:00
										 |  |  | def test_condition_expression_numerical_attribute(): | 
					
						
							|  |  |  |     dynamodb = boto3.resource("dynamodb", region_name="us-east-1") | 
					
						
							|  |  |  |     dynamodb.create_table( | 
					
						
							|  |  |  |         TableName="my-table", | 
					
						
							|  |  |  |         KeySchema=[{"AttributeName": "partitionKey", "KeyType": "HASH"}], | 
					
						
							|  |  |  |         AttributeDefinitions=[{"AttributeName": "partitionKey", "AttributeType": "S"}], | 
					
						
							| 
									
										
										
										
											2022-02-10 19:09:45 -01:00
										 |  |  |         BillingMode="PAY_PER_REQUEST", | 
					
						
							| 
									
										
										
										
											2021-10-09 13:09:12 +00:00
										 |  |  |     ) | 
					
						
							|  |  |  |     table = dynamodb.Table("my-table") | 
					
						
							|  |  |  |     table.put_item(Item={"partitionKey": "pk-pos", "myAttr": 5}) | 
					
						
							|  |  |  |     table.put_item(Item={"partitionKey": "pk-neg", "myAttr": -5}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # try to update the item we put in the table using numerical condition expression | 
					
						
							|  |  |  |     # Specifically, verify that we can compare with a zero-value | 
					
						
							|  |  |  |     # First verify that > and >= work on positive numbers | 
					
						
							|  |  |  |     update_numerical_con_expr( | 
					
						
							|  |  |  |         key="pk-pos", con_expr="myAttr > :zero", res="6", table=table | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     update_numerical_con_expr( | 
					
						
							|  |  |  |         key="pk-pos", con_expr="myAttr >= :zero", res="7", table=table | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     # Second verify that < and <= work on negative numbers | 
					
						
							|  |  |  |     update_numerical_con_expr( | 
					
						
							|  |  |  |         key="pk-neg", con_expr="myAttr < :zero", res="-4", table=table | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     update_numerical_con_expr( | 
					
						
							|  |  |  |         key="pk-neg", con_expr="myAttr <= :zero", res="-3", table=table | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def update_numerical_con_expr(key, con_expr, res, table): | 
					
						
							|  |  |  |     table.update_item( | 
					
						
							|  |  |  |         Key={"partitionKey": key}, | 
					
						
							|  |  |  |         UpdateExpression="ADD myAttr :one", | 
					
						
							|  |  |  |         ExpressionAttributeValues={":zero": 0, ":one": 1}, | 
					
						
							|  |  |  |         ConditionExpression=con_expr, | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-07-13 10:21:47 +00:00
										 |  |  |     assert table.get_item(Key={"partitionKey": key})["Item"]["myAttr"] == Decimal(res) | 
					
						
							| 
									
										
										
										
											2021-10-09 13:09:12 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-07 12:03:33 +00:00
										 |  |  | @mock_aws | 
					
						
							| 
									
										
										
										
											2021-10-09 13:09:12 +00:00
										 |  |  | def test_condition_expression__attr_doesnt_exist(): | 
					
						
							|  |  |  |     client = boto3.client("dynamodb", region_name="us-east-1") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     client.create_table( | 
					
						
							|  |  |  |         TableName="test", | 
					
						
							|  |  |  |         KeySchema=[{"AttributeName": "forum_name", "KeyType": "HASH"}], | 
					
						
							|  |  |  |         AttributeDefinitions=[{"AttributeName": "forum_name", "AttributeType": "S"}], | 
					
						
							|  |  |  |         ProvisionedThroughput={"ReadCapacityUnits": 1, "WriteCapacityUnits": 1}, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     client.put_item( | 
					
						
							|  |  |  |         TableName="test", Item={"forum_name": {"S": "foo"}, "ttl": {"N": "bar"}} | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def update_if_attr_doesnt_exist(): | 
					
						
							|  |  |  |         # Test nonexistent top-level attribute. | 
					
						
							|  |  |  |         client.update_item( | 
					
						
							|  |  |  |             TableName="test", | 
					
						
							|  |  |  |             Key={"forum_name": {"S": "the-key"}, "subject": {"S": "the-subject"}}, | 
					
						
							|  |  |  |             UpdateExpression="set #new_state=:new_state, #ttl=:ttl", | 
					
						
							|  |  |  |             ConditionExpression="attribute_not_exists(#new_state)", | 
					
						
							|  |  |  |             ExpressionAttributeNames={"#new_state": "foobar", "#ttl": "ttl"}, | 
					
						
							|  |  |  |             ExpressionAttributeValues={ | 
					
						
							|  |  |  |                 ":new_state": {"S": "some-value"}, | 
					
						
							|  |  |  |                 ":ttl": {"N": "12345.67"}, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             ReturnValues="ALL_NEW", | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     update_if_attr_doesnt_exist() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Second time should fail | 
					
						
							|  |  |  |     with pytest.raises(client.exceptions.ConditionalCheckFailedException) as exc: | 
					
						
							|  |  |  |         update_if_attr_doesnt_exist() | 
					
						
							|  |  |  |     _assert_conditional_check_failed_exception(exc) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-07 12:03:33 +00:00
										 |  |  | @mock_aws | 
					
						
							| 
									
										
										
										
											2021-10-09 13:09:12 +00:00
										 |  |  | def test_condition_expression__or_order(): | 
					
						
							|  |  |  |     client = boto3.client("dynamodb", region_name="us-east-1") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     client.create_table( | 
					
						
							|  |  |  |         TableName="test", | 
					
						
							|  |  |  |         KeySchema=[{"AttributeName": "forum_name", "KeyType": "HASH"}], | 
					
						
							|  |  |  |         AttributeDefinitions=[{"AttributeName": "forum_name", "AttributeType": "S"}], | 
					
						
							|  |  |  |         ProvisionedThroughput={"ReadCapacityUnits": 1, "WriteCapacityUnits": 1}, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # ensure that the RHS of the OR expression is not evaluated if the LHS | 
					
						
							|  |  |  |     # returns true (as it would result an error) | 
					
						
							|  |  |  |     client.update_item( | 
					
						
							|  |  |  |         TableName="test", | 
					
						
							|  |  |  |         Key={"forum_name": {"S": "the-key"}}, | 
					
						
							|  |  |  |         UpdateExpression="set #ttl=:ttl", | 
					
						
							|  |  |  |         ConditionExpression="attribute_not_exists(#ttl) OR #ttl <= :old_ttl", | 
					
						
							|  |  |  |         ExpressionAttributeNames={"#ttl": "ttl"}, | 
					
						
							|  |  |  |         ExpressionAttributeValues={":ttl": {"N": "6"}, ":old_ttl": {"N": "5"}}, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-07 12:03:33 +00:00
										 |  |  | @mock_aws | 
					
						
							| 
									
										
										
										
											2021-10-09 13:09:12 +00:00
										 |  |  | def test_condition_expression__and_order(): | 
					
						
							|  |  |  |     client = boto3.client("dynamodb", region_name="us-east-1") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     client.create_table( | 
					
						
							|  |  |  |         TableName="test", | 
					
						
							|  |  |  |         KeySchema=[{"AttributeName": "forum_name", "KeyType": "HASH"}], | 
					
						
							|  |  |  |         AttributeDefinitions=[{"AttributeName": "forum_name", "AttributeType": "S"}], | 
					
						
							|  |  |  |         ProvisionedThroughput={"ReadCapacityUnits": 1, "WriteCapacityUnits": 1}, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # ensure that the RHS of the AND expression is not evaluated if the LHS | 
					
						
							|  |  |  |     # returns true (as it would result an error) | 
					
						
							|  |  |  |     with pytest.raises(client.exceptions.ConditionalCheckFailedException) as exc: | 
					
						
							|  |  |  |         client.update_item( | 
					
						
							|  |  |  |             TableName="test", | 
					
						
							|  |  |  |             Key={"forum_name": {"S": "the-key"}}, | 
					
						
							|  |  |  |             UpdateExpression="set #ttl=:ttl", | 
					
						
							|  |  |  |             ConditionExpression="attribute_exists(#ttl) AND #ttl <= :old_ttl", | 
					
						
							|  |  |  |             ExpressionAttributeNames={"#ttl": "ttl"}, | 
					
						
							|  |  |  |             ExpressionAttributeValues={":ttl": {"N": "6"}, ":old_ttl": {"N": "5"}}, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     _assert_conditional_check_failed_exception(exc) | 
					
						
							| 
									
										
										
										
											2022-01-25 19:26:11 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-07 12:03:33 +00:00
										 |  |  | @mock_aws | 
					
						
							| 
									
										
										
										
											2022-01-25 19:26:11 +09:00
										 |  |  | def test_condition_expression_with_reserved_keyword_as_attr_name(): | 
					
						
							|  |  |  |     dynamodb = boto3.resource("dynamodb", region_name="us-east-2") | 
					
						
							|  |  |  |     table_name = "Test" | 
					
						
							|  |  |  |     dynamodb.create_table( | 
					
						
							|  |  |  |         TableName=table_name, | 
					
						
							|  |  |  |         KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], | 
					
						
							|  |  |  |         AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}], | 
					
						
							| 
									
										
										
										
											2022-02-10 19:09:45 -01:00
										 |  |  |         BillingMode="PAY_PER_REQUEST", | 
					
						
							| 
									
										
										
										
											2022-01-25 19:26:11 +09:00
										 |  |  |     ) | 
					
						
							|  |  |  |     table = dynamodb.Table(table_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     email_like_str = "test@foo.com" | 
					
						
							|  |  |  |     record = {"id": "key-0", "first": {email_like_str: {"end": {"VALUE"}}}} | 
					
						
							|  |  |  |     table.put_item(Item=record) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     expected_error_message = re.escape( | 
					
						
							|  |  |  |         "An error occurred (ValidationException) when " | 
					
						
							|  |  |  |         "calling the UpdateItem operation: Invalid ConditionExpression: Attribute name " | 
					
						
							|  |  |  |         "is a reserved keyword; reserved keyword: end" | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     with pytest.raises( | 
					
						
							|  |  |  |         dynamodb.meta.client.exceptions.ClientError, match=expected_error_message | 
					
						
							|  |  |  |     ): | 
					
						
							|  |  |  |         table.update_item( | 
					
						
							|  |  |  |             Key={"id": "key-0"}, | 
					
						
							|  |  |  |             UpdateExpression="REMOVE #first.#second, #other", | 
					
						
							|  |  |  |             ExpressionAttributeNames={ | 
					
						
							|  |  |  |                 "#first": "first", | 
					
						
							|  |  |  |                 "#second": email_like_str, | 
					
						
							|  |  |  |                 "#other": "other", | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             ExpressionAttributeValues={":value": "VALUE", ":one": 1}, | 
					
						
							|  |  |  |             ConditionExpression="size(#first.#second.end) = :one AND contains(#first.#second.end, :value)", | 
					
						
							|  |  |  |             ReturnValues="ALL_NEW", | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # table is unchanged | 
					
						
							|  |  |  |     item = table.get_item(Key={"id": "key-0"})["Item"] | 
					
						
							| 
									
										
										
										
											2023-07-13 10:21:47 +00:00
										 |  |  |     assert item == record | 
					
						
							| 
									
										
										
										
											2022-01-25 19:26:11 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # using attribute names solves the issue | 
					
						
							|  |  |  |     table.update_item( | 
					
						
							|  |  |  |         Key={"id": "key-0"}, | 
					
						
							|  |  |  |         UpdateExpression="REMOVE #first.#second, #other", | 
					
						
							|  |  |  |         ExpressionAttributeNames={ | 
					
						
							|  |  |  |             "#first": "first", | 
					
						
							|  |  |  |             "#second": email_like_str, | 
					
						
							|  |  |  |             "#other": "other", | 
					
						
							|  |  |  |             "#end": "end", | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         ExpressionAttributeValues={":value": "VALUE", ":one": 1}, | 
					
						
							|  |  |  |         ConditionExpression="size(#first.#second.#end) = :one AND contains(#first.#second.#end, :value)", | 
					
						
							|  |  |  |         ReturnValues="ALL_NEW", | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     item = table.get_item(Key={"id": "key-0"})["Item"] | 
					
						
							| 
									
										
										
										
											2023-07-13 10:21:47 +00:00
										 |  |  |     assert item == {"id": "key-0", "first": {}} | 
					
						
							| 
									
										
										
										
											2024-02-23 19:55:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_aws | 
					
						
							|  |  |  | def test_condition_check_failure_exception_is_raised_when_values_are_returned_for_an_item_with_a_top_level_list(): | 
					
						
							|  |  |  |     # This explicitly tests for a failure in handling JSONification of DynamoType | 
					
						
							|  |  |  |     # when lists are at the top level of an item. | 
					
						
							|  |  |  |     # This exception should not be raised: | 
					
						
							|  |  |  |     # TypeError: Object of type DynamoType is not JSON serializable | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dynamodb_client = boto3.client("dynamodb", region_name="us-east-1") | 
					
						
							|  |  |  |     dynamodb_client.create_table( | 
					
						
							|  |  |  |         TableName="example_table", | 
					
						
							|  |  |  |         KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], | 
					
						
							|  |  |  |         AttributeDefinitions=[ | 
					
						
							|  |  |  |             {"AttributeName": "id", "AttributeType": "S"}, | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |         BillingMode="PAY_PER_REQUEST", | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     record = { | 
					
						
							|  |  |  |         "id": {"S": "example_id"}, | 
					
						
							|  |  |  |         "some_list": {"L": [{"M": {"hello": {"S": "h"}}}]}, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     dynamodb_client.put_item( | 
					
						
							|  |  |  |         TableName="example_table", | 
					
						
							|  |  |  |         Item=record, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as error: | 
					
						
							|  |  |  |         dynamodb_client.update_item( | 
					
						
							|  |  |  |             TableName="example_table", | 
					
						
							|  |  |  |             Key={"id": {"S": "example_id"}}, | 
					
						
							|  |  |  |             UpdateExpression="set some_list=list_append(some_list, :w)", | 
					
						
							|  |  |  |             ExpressionAttributeValues={ | 
					
						
							|  |  |  |                 ":w": {"L": [{"M": {"world": {"S": "w"}}}]}, | 
					
						
							|  |  |  |                 ":id": {"S": "incorrect id"}, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             ConditionExpression="id = :id", | 
					
						
							|  |  |  |             ReturnValuesOnConditionCheckFailure="ALL_OLD", | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert error.type.__name__ == "ConditionalCheckFailedException" | 
					
						
							|  |  |  |     assert error.value.response["Error"] == { | 
					
						
							|  |  |  |         "Message": "The conditional request failed", | 
					
						
							|  |  |  |         "Code": "ConditionalCheckFailedException", | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert error.value.response["Item"] == { | 
					
						
							|  |  |  |         "id": {"S": "example_id"}, | 
					
						
							|  |  |  |         "some_list": {"L": [{"M": {"hello": {"S": "h"}}}]}, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_aws | 
					
						
							|  |  |  | def test_condition_check_failure_exception_is_raised_when_values_are_returned_for_an_item_with_a_top_level_string_set(): | 
					
						
							|  |  |  |     # This explicitly tests for a failure in handling JSONification of DynamoType | 
					
						
							|  |  |  |     # when string sets are at the top level of an item. | 
					
						
							|  |  |  |     # These exception should not be raised: | 
					
						
							|  |  |  |     # TypeError: Object of type DynamoType is not JSON serializable | 
					
						
							|  |  |  |     # AttributeError: 'str' object has no attribute 'to_regular_json' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dynamodb_client = boto3.client("dynamodb", region_name="us-east-1") | 
					
						
							|  |  |  |     dynamodb_client.create_table( | 
					
						
							|  |  |  |         TableName="example_table", | 
					
						
							|  |  |  |         KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], | 
					
						
							|  |  |  |         AttributeDefinitions=[ | 
					
						
							|  |  |  |             {"AttributeName": "id", "AttributeType": "S"}, | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |         BillingMode="PAY_PER_REQUEST", | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     record = { | 
					
						
							|  |  |  |         "id": {"S": "example_id"}, | 
					
						
							|  |  |  |         "some_list": {"SS": ["hello"]}, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     dynamodb_client.put_item( | 
					
						
							|  |  |  |         TableName="example_table", | 
					
						
							|  |  |  |         Item=record, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as error: | 
					
						
							|  |  |  |         dynamodb_client.update_item( | 
					
						
							|  |  |  |             TableName="example_table", | 
					
						
							|  |  |  |             Key={"id": {"S": "example_id"}}, | 
					
						
							|  |  |  |             UpdateExpression="set some_list=list_append(some_list, :w)", | 
					
						
							|  |  |  |             ExpressionAttributeValues={ | 
					
						
							|  |  |  |                 ":w": {"SS": ["world"]}, | 
					
						
							|  |  |  |                 ":id": {"S": "incorrect id"}, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             ConditionExpression="id = :id", | 
					
						
							|  |  |  |             ReturnValuesOnConditionCheckFailure="ALL_OLD", | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert error.type.__name__ == "ConditionalCheckFailedException" | 
					
						
							|  |  |  |     assert error.value.response["Error"] == { | 
					
						
							|  |  |  |         "Message": "The conditional request failed", | 
					
						
							|  |  |  |         "Code": "ConditionalCheckFailedException", | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert error.value.response["Item"] == { | 
					
						
							|  |  |  |         "id": {"S": "example_id"}, | 
					
						
							|  |  |  |         "some_list": {"SS": ["hello"]}, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_aws | 
					
						
							|  |  |  | def test_condition_check_failure_exception_is_raised_when_values_are_returned_for_an_item_with_a_list_in_a_map(): | 
					
						
							|  |  |  |     # This explicitly tests for a failure in handling JSONification of DynamoType | 
					
						
							|  |  |  |     # when lists are inside a map | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dynamodb_client = boto3.client("dynamodb", region_name="us-east-1") | 
					
						
							|  |  |  |     dynamodb_client.create_table( | 
					
						
							|  |  |  |         TableName="example_table", | 
					
						
							|  |  |  |         KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], | 
					
						
							|  |  |  |         AttributeDefinitions=[ | 
					
						
							|  |  |  |             {"AttributeName": "id", "AttributeType": "S"}, | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |         BillingMode="PAY_PER_REQUEST", | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     record = { | 
					
						
							|  |  |  |         "id": {"S": "example_id"}, | 
					
						
							|  |  |  |         "some_list_in_a_map": { | 
					
						
							|  |  |  |             "M": {"some_list": {"L": [{"M": {"hello": {"S": "h"}}}]}} | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     dynamodb_client.put_item( | 
					
						
							|  |  |  |         TableName="example_table", | 
					
						
							|  |  |  |         Item=record, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as error: | 
					
						
							|  |  |  |         dynamodb_client.update_item( | 
					
						
							|  |  |  |             TableName="example_table", | 
					
						
							|  |  |  |             Key={"id": {"S": "example_id"}}, | 
					
						
							|  |  |  |             UpdateExpression="set some_list_in_a_map.some_list=list_append(some_list_in_a_map.some_list, :w)", | 
					
						
							|  |  |  |             ExpressionAttributeValues={ | 
					
						
							|  |  |  |                 ":w": {"L": [{"M": {"world": {"S": "w"}}}]}, | 
					
						
							|  |  |  |                 ":id": {"S": "incorrect id"}, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             ConditionExpression="id = :id", | 
					
						
							|  |  |  |             ReturnValuesOnConditionCheckFailure="ALL_OLD", | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert error.type.__name__ == "ConditionalCheckFailedException" | 
					
						
							|  |  |  |     assert error.value.response["Error"] == { | 
					
						
							|  |  |  |         "Message": "The conditional request failed", | 
					
						
							|  |  |  |         "Code": "ConditionalCheckFailedException", | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert error.value.response["Item"] == { | 
					
						
							|  |  |  |         "id": {"S": "example_id"}, | 
					
						
							|  |  |  |         "some_list_in_a_map": { | 
					
						
							|  |  |  |             "M": {"some_list": {"L": [{"M": {"hello": {"S": "h"}}}]}} | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     } |