From bcc7fd347bdb43a926daefa5eaa6d6c1d2370765 Mon Sep 17 00:00:00 2001 From: Anandh Somasundaram Date: Fri, 22 May 2026 23:43:23 +0000 Subject: [PATCH] fix: omit empty Attributes on UpdateItem UPDATED_NEW/UPDATED_OLD --- crates/engine/src/update_item.rs | 12 ++++---- tests/test_item_operations.py | 47 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/crates/engine/src/update_item.rs b/crates/engine/src/update_item.rs index e90ce2a..cd869e5 100755 --- a/crates/engine/src/update_item.rs +++ b/crates/engine/src/update_item.rs @@ -196,12 +196,12 @@ pub async fn handle_update_item( ReturnValues::None => None, ReturnValues::AllOld => old_item, ReturnValues::AllNew => new_item, - ReturnValues::UpdatedOld => { - old_item.map(|item| filter_to_updated_attrs(&item, &actions, &maps)) - } - ReturnValues::UpdatedNew => { - new_item.map(|item| filter_to_updated_attrs(&item, &actions, &maps)) - } + ReturnValues::UpdatedOld => old_item + .map(|item| filter_to_updated_attrs(&item, &actions, &maps)) + .filter(|item| !item.is_empty()), + ReturnValues::UpdatedNew => new_item + .map(|item| filter_to_updated_attrs(&item, &actions, &maps)) + .filter(|item| !item.is_empty()), }; let output = UpdateItemOutput { diff --git a/tests/test_item_operations.py b/tests/test_item_operations.py index 0aaa39a..cff0ca4 100755 --- a/tests/test_item_operations.py +++ b/tests/test_item_operations.py @@ -454,6 +454,53 @@ def test_update_item_ne_comparison_missing_attribute(self, dynamodb_client, upd_ resp = dynamodb_client.get_item(TableName=upd_table, Key={"pk": {"S": "ne-missing"}}) assert resp["Item"]["data"]["S"] == "ok" + def test_update_item_remove_with_updated_new_omits_attributes(self, dynamodb_client, upd_table): + """REMOVE leaves nothing in UPDATED_NEW: Attributes field must be omitted, not returned as {}.""" + dynamodb_client.put_item( + TableName=upd_table, + Item={"pk": {"S": "remove-empty"}, "map_attr": {"M": {"child": {"S": "old"}}}}, + ) + resp = dynamodb_client.update_item( + TableName=upd_table, + Key={"pk": {"S": "remove-empty"}}, + UpdateExpression="REMOVE map_attr", + ReturnValues="UPDATED_NEW", + ) + assert "Attributes" not in resp, f"expected Attributes omitted, got {resp.get('Attributes')!r}" + + def test_update_item_set_new_attribute_with_updated_old_omits_attributes( + self, dynamodb_client, upd_table + ): + """SET on a brand-new attribute has no prior value: UPDATED_OLD must omit Attributes.""" + dynamodb_client.put_item( + TableName=upd_table, + Item={"pk": {"S": "set-new-old"}}, + ) + resp = dynamodb_client.update_item( + TableName=upd_table, + Key={"pk": {"S": "set-new-old"}}, + UpdateExpression="SET fresh_attr = :v", + ExpressionAttributeValues={":v": {"S": "new"}}, + ReturnValues="UPDATED_OLD", + ) + assert "Attributes" not in resp, f"expected Attributes omitted, got {resp.get('Attributes')!r}" + + def test_update_item_legacy_delete_with_updated_new_omits_attributes( + self, dynamodb_client, upd_table + ): + """Legacy AttributeUpdates DELETE on a Map mirrors REMOVE: UPDATED_NEW must omit Attributes.""" + dynamodb_client.put_item( + TableName=upd_table, + Item={"pk": {"S": "legacy-delete"}, "map_attr": {"M": {"child": {"S": "old"}}}}, + ) + resp = dynamodb_client.update_item( + TableName=upd_table, + Key={"pk": {"S": "legacy-delete"}}, + AttributeUpdates={"map_attr": {"Action": "DELETE"}}, + ReturnValues="UPDATED_NEW", + ) + assert "Attributes" not in resp, f"expected Attributes omitted, got {resp.get('Attributes')!r}" + # --------------------------------------------------------------------------- # DeleteItem tests