diff --git a/.changes/next-release/feature-AmazonDynamoDBEnhancedClient-d5e2088.json b/.changes/next-release/feature-AmazonDynamoDBEnhancedClient-d5e2088.json new file mode 100644 index 000000000000..2ba328c0ebe9 --- /dev/null +++ b/.changes/next-release/feature-AmazonDynamoDBEnhancedClient-d5e2088.json @@ -0,0 +1,6 @@ +{ + "type": "bugfix", + "category": "Amazon DynamoDB Enhanced Client", + "contributor": "", + "description": "Fix handling of UpdateBehavior.WRITE_IF_NOT_EXISTS on @DynamoDbAutoGeneratedUuid" +} diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedUuidExtension.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedUuidExtension.java index d92db8c60bbd..ccf4a222dfc3 100644 --- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedUuidExtension.java +++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedUuidExtension.java @@ -29,51 +29,53 @@ import software.amazon.awssdk.enhanced.dynamodb.EnhancedType; import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTag; import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableMetadata; -import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbUpdateBehavior; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.utils.StringUtils; import software.amazon.awssdk.utils.Validate; /** - * This extension facilitates the automatic generation of a unique UUID (Universally Unique Identifier) for a specified attribute - * every time a new record is written to the database. The generated UUID is obtained using the - * {@link java.util.UUID#randomUUID()} method. - *

- * This extension is not loaded by default when you instantiate a - * {@link software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient}. Therefore, you need to specify it in a custom - * extension when creating the enhanced client. - *

- * Example to add AutoGeneratedUuidExtension along with default extensions is - * {@snippet : - * DynamoDbEnhancedClient.builder().extensions(Stream.concat(ExtensionResolver.defaultExtensions().stream(), - * Stream.of(AutoGeneratedUuidExtension.create())).collect(Collectors.toList())).build(); - *} - *

- *

- * Example to just add AutoGeneratedUuidExtension without default extensions is - * {@snippet : - * DynamoDbEnhancedClient.builder().extensions(AutoGeneratedUuidExtension.create()).build(); - *} - *

- *

- * To utilize the auto-generated UUID feature, first, create a field in your model that will store the UUID for the attribute. - * This class field must be of type {@link java.lang.String}, and you need to tag it as the autoGeneratedUuidAttribute. If you are - * using the {@link software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema}, then you should use the - * {@link software.amazon.awssdk.enhanced.dynamodb.extensions.annotations.DynamoDbAutoGeneratedUuid} annotation. If you are using - * the {@link software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema}, then you should use the - * {@link - * software.amazon.awssdk.enhanced.dynamodb.extensions.AutoGeneratedUuidExtension.AttributeTags#autoGeneratedUuidAttribute()} - * static attribute tag. - *

- *

- * Every time a new record is successfully put into the database, the specified attribute will be automatically populated with a - * unique UUID generated using {@link java.util.UUID#randomUUID()}. If the UUID needs to be created only for `putItem` and should - * not be generated for an `updateItem`, then - * {@link software.amazon.awssdk.enhanced.dynamodb.mapper.UpdateBehavior#WRITE_IF_NOT_EXISTS} must be along with - * {@link DynamoDbUpdateBehavior} + * This extension facilitates the automatic generation of a unique UUID (Universally Unique Identifier) for attributes tagged with + * {@code @DynamoDbAutoGeneratedUuid} or {@link AutoGeneratedUuidExtension.AttributeTags#autoGeneratedUuidAttribute()}. The + * generated UUID is obtained using {@link java.util.UUID#randomUUID()}. * - *

+ *

Usage:

+ * + * + *

Update behavior:

+ * + * + *

Difference between putItem and updateItem:

+ * + * + *

Examples:

+ *
{@code
+ * DynamoDbEnhancedClient client = DynamoDbEnhancedClient.builder()
+ *     .dynamoDbClient(lowLevelClient)
+ *     .extensions(AutoGeneratedUuidExtension.create())
+ *     .build();
+ * }
*/ + @SdkPublicApi @ThreadSafe public final class AutoGeneratedUuidExtension implements DynamoDbEnhancedClientExtension { @@ -110,15 +112,18 @@ public WriteModification beforeWrite(DynamoDbExtensionContext.BeforeWrite contex } Map itemToTransform = new HashMap<>(context.items()); - customMetadataObject.forEach(key -> insertUuidInItemToTransform(itemToTransform, key)); + customMetadataObject.forEach(key -> insertUuidIfMissing(itemToTransform, key)); return WriteModification.builder() .transformedItem(Collections.unmodifiableMap(itemToTransform)) .build(); } - private void insertUuidInItemToTransform(Map itemToTransform, - String key) { - itemToTransform.put(key, AttributeValue.builder().s(UUID.randomUUID().toString()).build()); + private static void insertUuidIfMissing(Map item, String key) { + AttributeValue existing = item.get(key); + boolean missing = existing == null || StringUtils.isEmpty(existing.s()); + if (missing) { + item.put(key, AttributeValue.builder().s(UUID.randomUUID().toString()).build()); + } } public static final class AttributeTags { diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedUuidExtensionTest.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedUuidExtensionTest.java index cc69f503d50f..64e5f310d12f 100644 --- a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedUuidExtensionTest.java +++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedUuidExtensionTest.java @@ -66,11 +66,11 @@ public class AutoGeneratedUuidExtensionTest { .build(); @Test - public void beforeWrite_updateItemOperation_hasUuidInItem_doesNotCreateUpdateExpressionAndFilters() { + public void beforeWrite_withExistingUuid_doesNotOverwriteValue() { ItemWithUuid SimpleItem = new ItemWithUuid(); SimpleItem.setId(RECORD_ID); - String uuidAttribute = String.valueOf(UUID.randomUUID()); - SimpleItem.setUuidAttribute(uuidAttribute); + String initialUuid = String.valueOf(UUID.randomUUID()); + SimpleItem.setUuidAttribute(initialUuid); Map items = ITEM_WITH_UUID_MAPPER.itemToMap(SimpleItem, true); assertThat(items).hasSize(2); @@ -85,13 +85,15 @@ public void beforeWrite_updateItemOperation_hasUuidInItem_doesNotCreateUpdateExp Map transformedItem = result.transformedItem(); assertThat(transformedItem).isNotNull().hasSize(2); assertThat(transformedItem).containsEntry("id", AttributeValue.fromS(RECORD_ID)); - isValidUuid(transformedItem.get("uuidAttribute").s()); assertThat(result.updateExpression()).isNull(); + String uuidValue = result.transformedItem().get("uuidAttribute").s(); + isValidUuid(uuidValue); + assertThat(uuidValue).isEqualTo(initialUuid); } @Test - public void beforeWrite_updateItemOperation_hasNoUuidInItem_doesNotCreatesUpdateExpressionAndFilters() { + public void beforeWrite_withMissingUuid_generatesNewValue() { ItemWithUuid SimpleItem = new ItemWithUuid(); SimpleItem.setId(RECORD_ID); @@ -108,8 +110,31 @@ public void beforeWrite_updateItemOperation_hasNoUuidInItem_doesNotCreatesUpdate Map transformedItem = result.transformedItem(); assertThat(transformedItem).isNotNull().hasSize(2); assertThat(transformedItem).containsEntry("id", AttributeValue.fromS(RECORD_ID)); - isValidUuid(transformedItem.get("uuidAttribute").s()); assertThat(result.updateExpression()).isNull(); + + isValidUuid(result.transformedItem().get("uuidAttribute").s()); + assertThat(items).doesNotContainKey("uuidAttribute"); + } + + @Test + public void beforeWrite_withEmptyUuid_generatesNewValue() { + ItemWithUuid item = new ItemWithUuid(); + item.setId(RECORD_ID); + item.setUuidAttribute(""); // empty should be treated as missing + + Map items = ITEM_WITH_UUID_MAPPER.itemToMap(item, true); + + WriteModification result = atomicCounterExtension.beforeWrite( + DefaultDynamoDbExtensionContext.builder() + .items(items) + .tableMetadata(ITEM_WITH_UUID_MAPPER.tableMetadata()) + .operationName(OperationName.UPDATE_ITEM) + .operationContext(PRIMARY_CONTEXT) + .build()); + + assertThat(result.updateExpression()).isNull(); + String uuidValue = result.transformedItem().get("uuidAttribute").s(); + isValidUuid(uuidValue); } @Test diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedUuidRecordTest.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedUuidRecordTest.java index e59ea214399b..2cf5c6fe9bba 100644 --- a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedUuidRecordTest.java +++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedUuidRecordTest.java @@ -23,7 +23,6 @@ import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -55,13 +54,10 @@ import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbUpdateBehavior; import software.amazon.awssdk.enhanced.dynamodb.model.PutItemEnhancedRequest; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.enhanced.dynamodb.model.TransactWriteItemsEnhancedRequest; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException; import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; @RunWith(Parameterized.class) public class AutoGeneratedUuidRecordTest extends LocalDynamoDbSyncTestBase{ @@ -314,6 +310,216 @@ public void updateItemConditionTestFailure() { } + /** + | SCENARIO 1 (PUT → PUT), Case A: No UUIDs provided + | After first PUT: item is created; both UUIDs generated by extension + | After second PUT: item is updated; both UUIDs re-generated + */ + @Test + public void putItemTwice_withoutUuids_bothUuidsRegeneratedOnSecondPut() { + mappedTable.putItem(new Record().id("id1").attribute("p1")); + Record afterFirstPut = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id1"))); + assertRecordHasValidUuid(afterFirstPut); + + mappedTable.putItem(new Record().id("id1").attribute("p2")); + Record afterSecondPut = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id1"))); + Assertions.assertThat(afterSecondPut.getCreatedUuid()).isNotEqualTo(afterFirstPut.getCreatedUuid()); + Assertions.assertThat(afterSecondPut.getLastUpdatedUuid()).isNotEqualTo(afterFirstPut.getLastUpdatedUuid()); + assertRecordHasValidUuid(afterSecondPut); + } + + /** + | SCENARIO 1 (PUT → PUT), Case B: Manual UUIDs provided on requests + | After first PUT: item is created; UUID fields are taken from the request + | After second PUT: item is updated; UUID fields are taken from the new request + */ + @Test + public void putItemTwice_withManualUuidsOnRequest_requestValuesAppliedEachTime() { + Record first = new Record().id("id1").attribute("p1"); + String manualCreatedFirst = "UUID-" + UUID.randomUUID(); + String manualUpdatedFirst = "UUID-" + UUID.randomUUID(); + first.setCreatedUuid(manualCreatedFirst); + first.setLastUpdatedUuid(manualUpdatedFirst); + + mappedTable.putItem(first); + Record afterFirstPut = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id1"))); + Assertions.assertThat(afterFirstPut.getCreatedUuid()).isEqualTo(manualCreatedFirst); + Assertions.assertThat(afterFirstPut.getLastUpdatedUuid()).isEqualTo(manualUpdatedFirst); + + Record second = new Record().id("id1").attribute("p2"); + second.setCreatedUuid("UUID2-" + UUID.randomUUID()); + second.setLastUpdatedUuid("UUID2-" + UUID.randomUUID()); + + mappedTable.putItem(second); + Record afterSecondPut = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id1"))); + Assertions.assertThat(afterSecondPut.getCreatedUuid()).isNotEqualTo(manualCreatedFirst); + Assertions.assertThat(afterSecondPut.getLastUpdatedUuid()).isNotEqualTo(manualUpdatedFirst); + } + + /** + | SCENARIO 2 (UPDATE → UPDATE), Case A: No UUIDs provided + | After first UPDATE: item is created; both UUIDs generated + | After second UPDATE: createdUuid preserved; lastUpdatedUuid regenerated + */ + @Test + public void updateItemTwice_withoutUuidsOnRequest_thenCreatedUuidPreservedAndLastUpdatedUuidChanged() { + mappedTable.updateItem(new Record().id("id1").attribute("p1")); + Record afterFirstUpdate = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id1"))); + assertRecordHasValidUuid(afterFirstUpdate); + + mappedTable.updateItem(new Record().id("id1").attribute("secondItemToBeUpdated")); + Record afterSecondUpdate = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id1"))); + Assertions.assertThat(afterSecondUpdate.getCreatedUuid()).isEqualTo(afterFirstUpdate.getCreatedUuid()); + Assertions.assertThat(afterSecondUpdate.getLastUpdatedUuid()).isNotEqualTo(afterFirstUpdate.getLastUpdatedUuid()); + assertRecordHasValidUuid(afterSecondUpdate); + } + + /** + | SCENARIO 2 (UPDATE → UPDATE), Case B: Manual UUIDs on first request + | After first UPDATE: item is created; UUIDs taken from request + | After second UPDATE: createdUuid preserved; lastUpdatedUuid regenerated + */ + @Test + public void updateItemTwice_withManualUuidsFirst_thenCreatedPreserved_LastUpdatedChanges() { + Record firstItemToBeUpdated = new Record().id("id1").attribute("u1"); + String manualCreated = "createdUUID-" + UUID.randomUUID(); + String manualUpdated = "updatedUUID-" + UUID.randomUUID(); + firstItemToBeUpdated.setCreatedUuid(manualCreated); + firstItemToBeUpdated.setLastUpdatedUuid(manualUpdated); + + mappedTable.updateItem(firstItemToBeUpdated); + Record afterFirstUpdate = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id1"))); + Assertions.assertThat(afterFirstUpdate.getCreatedUuid()).isEqualTo(manualCreated); + Assertions.assertThat(afterFirstUpdate.getLastUpdatedUuid()).isEqualTo(manualUpdated); + + Record secondItemToBeUpdated = new Record().id("id1").attribute("u2"); + secondItemToBeUpdated.setCreatedUuid("UUID-" + UUID.randomUUID()); + mappedTable.updateItem(secondItemToBeUpdated); + + Record afterSecondUpdate = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id1"))); + Assertions.assertThat(afterSecondUpdate.getCreatedUuid()).isEqualTo(afterFirstUpdate.getCreatedUuid()); + Assertions.assertThat(afterSecondUpdate.getLastUpdatedUuid()).isNotEqualTo(afterFirstUpdate.getLastUpdatedUuid()); + } + + /** + | SCENARIO 3: transactWriteItems.addPutItem → putItem, Case A: No UUIDs provided + | After first PUT (txn): item created; both UUIDs generated + | After second PUT: item updated; both UUIDs re-generated + */ + @Test + public void transactPutThenPut_withoutUuids_bothUuidsRegeneratedEachTime() { + DynamoDbEnhancedClient client = DynamoDbEnhancedClient.builder() + .dynamoDbClient(getDynamoDbClient()) + .extensions(AutoGeneratedUuidExtension.create()) + .build(); + DynamoDbTable table = client.table(getConcreteTableName("table-name"), TABLE_SCHEMA); + + client.transactWriteItems(TransactWriteItemsEnhancedRequest.builder() + .addPutItem(table, new Record().id("id1").attribute("p1")) + .build()); + Record afterFirstUpdateRecord = table.getItem(r -> r.key(k -> k.partitionValue("id1"))); + String createdUuidAfterFirstUpdate = afterFirstUpdateRecord.getCreatedUuid(); + String lastUpdatedUuidAfterFirstUpdate = afterFirstUpdateRecord.getLastUpdatedUuid(); + assertRecordHasValidUuid(afterFirstUpdateRecord); + + table.putItem(new Record().id("id1").attribute("p2")); + Record afterSecondUpdateRecord = table.getItem(r -> r.key(k -> k.partitionValue("id1"))); + Assertions.assertThat(afterSecondUpdateRecord.getCreatedUuid()).isNotEqualTo(createdUuidAfterFirstUpdate); + Assertions.assertThat(afterSecondUpdateRecord.getLastUpdatedUuid()).isNotEqualTo(lastUpdatedUuidAfterFirstUpdate); + assertRecordHasValidUuid(afterSecondUpdateRecord); + } + + /** + | SCENARIO 3: transactWriteItems.addPutItem → putItem + | Case B: Manual UUIDs on first PUT (applied), no UUIDs provided on next PUT (regenerated) + */ + @Test + public void transactPutWithManualCreatedUuid_thenPutWithoutUuid_thenBothUuidsRegeneratedOnSecondPut() { + DynamoDbEnhancedClient client = DynamoDbEnhancedClient.builder() + .dynamoDbClient(getDynamoDbClient()) + .extensions(AutoGeneratedUuidExtension.create()) + .build(); + DynamoDbTable table = client.table(getConcreteTableName("table-name"), TABLE_SCHEMA); + + String manualCreated = "createdUUID-" + UUID.randomUUID(); + String manualUpdated = "updatedUUID-" + UUID.randomUUID(); + Record firstPut = new Record().id("id1").attribute("p1"); + firstPut.setCreatedUuid(manualCreated); + firstPut.setLastUpdatedUuid(manualUpdated); + client.transactWriteItems(TransactWriteItemsEnhancedRequest.builder() + .addPutItem(table, firstPut) + .build()); + Record afterFirstUpdateRecord = table.getItem(r -> r.key(k -> k.partitionValue("id1"))); + Assertions.assertThat(afterFirstUpdateRecord.getCreatedUuid()).isEqualTo(manualCreated); + Assertions.assertThat(afterFirstUpdateRecord.getLastUpdatedUuid()).isEqualTo(manualUpdated); + + table.putItem(new Record().id("id1").attribute("p2")); + Record afterSecondUpdateRecord = table.getItem(r -> r.key(k -> k.partitionValue("id1"))); + Assertions.assertThat(afterSecondUpdateRecord.getCreatedUuid()).isNotEqualTo(manualCreated); + Assertions.assertThat(afterSecondUpdateRecord.getLastUpdatedUuid()).isNotEqualTo(afterFirstUpdateRecord.getLastUpdatedUuid()); + assertRecordHasValidUuid(afterSecondUpdateRecord); + } + + /** + | SCENARIO 4: transactWriteItems.addUpdateItem → updateItem, Case A: No UUIDs provided + | After first UPDATE: item created; both UUIDs generated + | After second UPDATE: createdUuid preserved; lastUpdatedUuid regenerated + */ + @Test + public void transactUpdateThenUpdate_withoutUuidsOnRequest_thenCreatedUuidPreserved_andLastUpdatedUuidRegenerated() { + DynamoDbEnhancedClient client = DynamoDbEnhancedClient.builder() + .dynamoDbClient(getDynamoDbClient()) + .extensions(AutoGeneratedUuidExtension.create()) + .build(); + DynamoDbTable table = client.table(getConcreteTableName("table-name"), TABLE_SCHEMA); + + client.transactWriteItems(TransactWriteItemsEnhancedRequest.builder() + .addUpdateItem(table, new Record().id("id1").attribute("u1")) + .build()); + Record afterTransactionUpdateRecord = table.getItem(r -> r.key(k -> k.partitionValue("id1"))); + assertRecordHasValidUuid(afterTransactionUpdateRecord); + + table.updateItem(new Record().id("id1").attribute("u2")); + Record afterUpdateRecord = table.getItem(r -> r.key(k -> k.partitionValue("id1"))); + Assertions.assertThat(afterUpdateRecord.getCreatedUuid()).isEqualTo(afterTransactionUpdateRecord.getCreatedUuid()); + Assertions.assertThat(afterUpdateRecord.getLastUpdatedUuid()).isNotEqualTo(afterTransactionUpdateRecord.getLastUpdatedUuid()); + assertRecordHasValidUuid(afterUpdateRecord); + } + + /** + | SCENARIO 4: transactWriteItems.addUpdateItem → updateItem + | Case B: Manual UUIDs on first update (applied); + | on second update, createdUuid preserved; lastUpdatedUuid regenerated + */ + @Test + public void transactUpdateThenUpdate_withManualCreatedUuid_thenCreatedPreserved_LastUpdatedChanges() { + DynamoDbEnhancedClient client = DynamoDbEnhancedClient.builder() + .dynamoDbClient(getDynamoDbClient()) + .extensions(AutoGeneratedUuidExtension.create()) + .build(); + DynamoDbTable table = client.table(getConcreteTableName("table-name"), TABLE_SCHEMA); + + Record firstItemToBeUpdated = new Record().id("id1").attribute("u1"); + String manualCreated = "createdUUID-" + UUID.randomUUID(); + String manualUpdated = "updatedUUID-" + UUID.randomUUID(); + firstItemToBeUpdated.setCreatedUuid(manualCreated); + firstItemToBeUpdated.setLastUpdatedUuid(manualUpdated); + client.transactWriteItems(TransactWriteItemsEnhancedRequest.builder() + .addUpdateItem(table, firstItemToBeUpdated) + .build()); + Record afterFirstUpdateRecord = table.getItem(r -> r.key(k -> k.partitionValue("id1"))); + Assertions.assertThat(afterFirstUpdateRecord.getCreatedUuid()).isEqualTo(manualCreated); + Assertions.assertThat(afterFirstUpdateRecord.getLastUpdatedUuid()).isEqualTo(manualUpdated); + + Record secondItemToBeUpdated = new Record().id("id1").attribute("u2"); + secondItemToBeUpdated.setCreatedUuid("idUID-" + UUID.randomUUID()); + table.updateItem(secondItemToBeUpdated); + + Record afterSecondUpdateRecord = table.getItem(r -> r.key(k -> k.partitionValue("id1"))); + Assertions.assertThat(afterSecondUpdateRecord.getCreatedUuid()).isEqualTo(afterFirstUpdateRecord.getCreatedUuid()); + Assertions.assertThat(afterSecondUpdateRecord.getLastUpdatedUuid()).isNotEqualTo(afterFirstUpdateRecord.getLastUpdatedUuid()); + } + public static Record createUniqueFakeItem() { Record record = new Record(); record.setId(UUID.randomUUID().toString());