Skip to content

Commit

Permalink
DynamoDB: put_item() now returns old item for ConditionalCheckFailed …
Browse files Browse the repository at this point in the history
…exceptions (#7068)
  • Loading branch information
ojongerius committed Nov 27, 2023
1 parent 807dca6 commit bfac8a8
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 1 deletion.
2 changes: 2 additions & 0 deletions moto/dynamodb/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ def put_item(
expression_attribute_names: Optional[Dict[str, Any]] = None,
expression_attribute_values: Optional[Dict[str, Any]] = None,
overwrite: bool = False,
return_values_on_condition_check_failure: Optional[str] = None,
) -> Item:
table = self.get_table(table_name)
return table.put_item(
Expand All @@ -234,6 +235,7 @@ def put_item(
expression_attribute_names,
expression_attribute_values,
overwrite,
return_values_on_condition_check_failure,
)

def get_table_keys_name(
Expand Down
9 changes: 8 additions & 1 deletion moto/dynamodb/models/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ def put_item(
expression_attribute_names: Optional[Dict[str, str]] = None,
expression_attribute_values: Optional[Dict[str, Any]] = None,
overwrite: bool = False,
return_values_on_condition_check_failure: Optional[str] = None,
) -> Item:
if self.hash_key_attr not in item_attrs.keys():
raise MockValidationException(
Expand Down Expand Up @@ -571,7 +572,13 @@ def put_item(
expression_attribute_values,
)
if not condition_op.expr(current):
raise ConditionalCheckFailed
if (
return_values_on_condition_check_failure == "ALL_OLD"
and current is not None
):
raise ConditionalCheckFailed(item=current.to_json()["Attributes"])
else:
raise ConditionalCheckFailed

if range_value:
self.items[hash_value][range_value] = item
Expand Down
4 changes: 4 additions & 0 deletions moto/dynamodb/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,9 @@ def put_item(self) -> str:
name = self.body["TableName"]
item = self.body["Item"]
return_values = self.body.get("ReturnValues", "NONE")
return_values_on_condition_check_failure = self.body.get(
"ReturnValuesOnConditionCheckFailure"
)

if return_values not in ("ALL_OLD", "NONE"):
raise MockValidationException("Return values set to invalid value")
Expand Down Expand Up @@ -498,6 +501,7 @@ def put_item(self) -> str:
expression_attribute_names,
expression_attribute_values,
overwrite,
return_values_on_condition_check_failure,
)

item_dict = result.to_json()
Expand Down
42 changes: 42 additions & 0 deletions tests/test_dynamodb/exceptions/test_dynamodb_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,48 @@ def test_put_item_empty_set():
)


@mock_dynamodb
def test_put_item_returns_old_item():
dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
table = dynamodb.create_table(
TableName="test-table",
KeySchema=[{"AttributeName": "pk", "KeyType": "HASH"}],
AttributeDefinitions=[{"AttributeName": "pk", "AttributeType": "S"}],
BillingMode="PAY_PER_REQUEST",
)

table.put_item(Item={"pk": "foo", "bar": "baz"})

with pytest.raises(ClientError) as exc:
table.put_item(
Item={"pk": "foo", "bar": "quuz"},
ConditionExpression="attribute_not_exists(pk)",
)
resp = exc.value.response
assert resp["Error"] == {
"Message": "The conditional request failed",
"Code": "ConditionalCheckFailedException",
}
assert resp["message"] == "The conditional request failed"
assert "Item" not in resp

table.put_item(Item={"pk": "foo", "bar": "baz"})

with pytest.raises(ClientError) as exc:
table.put_item(
Item={"pk": "foo", "bar": "quuz"},
ReturnValuesOnConditionCheckFailure="ALL_OLD",
ConditionExpression="attribute_not_exists(pk)",
)
resp = exc.value.response
assert resp["Error"] == {
"Message": "The conditional request failed",
"Code": "ConditionalCheckFailedException",
}
assert "message" not in resp
assert resp["Item"] == {"pk": {"S": "foo"}, "bar": {"S": "baz"}}


@mock_dynamodb
def test_update_expression_with_trailing_comma():
resource = boto3.resource(service_name="dynamodb", region_name="us-east-1")
Expand Down

0 comments on commit bfac8a8

Please sign in to comment.