diff --git a/.changes/next-release/feature-AWSDynamoDBEnhancedClient-faf3991.json b/.changes/next-release/feature-AWSDynamoDBEnhancedClient-faf3991.json
new file mode 100644
index 000000000000..c735d40e8f35
--- /dev/null
+++ b/.changes/next-release/feature-AWSDynamoDBEnhancedClient-faf3991.json
@@ -0,0 +1,6 @@
+{
+ "type": "feature",
+ "category": "AWS DynamoDB Enhanced Client",
+ "contributor": "",
+ "description": "Added support for `@DynamoDBAutoGeneratedUUID` to facilitate the automatic updating of DynamoDB attributes with random UUID."
+}
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
new file mode 100644
index 000000000000..d92db8c60bbd
--- /dev/null
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedUuidExtension.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.enhanced.dynamodb.extensions;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Consumer;
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.annotations.ThreadSafe;
+import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType;
+import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClientExtension;
+import software.amazon.awssdk.enhanced.dynamodb.DynamoDbExtensionContext;
+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.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}
+ *
+ *
+ */
+@SdkPublicApi
+@ThreadSafe
+public final class AutoGeneratedUuidExtension implements DynamoDbEnhancedClientExtension {
+ private static final String CUSTOM_METADATA_KEY =
+ "software.amazon.awssdk.enhanced.dynamodb.extensions.AutoGeneratedUuidExtension:AutoGeneratedUuidAttribute";
+ private static final AutoGeneratedUuidAttribute AUTO_GENERATED_UUID_ATTRIBUTE = new AutoGeneratedUuidAttribute();
+
+ private AutoGeneratedUuidExtension() {
+ }
+
+ /**
+ * @return an Instance of {@link AutoGeneratedUuidExtension}
+ */
+ public static AutoGeneratedUuidExtension create() {
+ return new AutoGeneratedUuidExtension();
+ }
+
+ /**
+ * Modifies the WriteModification UUID string with the attribute updated with the extension.
+ *
+ * @param context The {@link DynamoDbExtensionContext.BeforeWrite} context containing the state of the execution.
+ * @return WriteModification String updated with attribute updated with Extension.
+ */
+ @Override
+ public WriteModification beforeWrite(DynamoDbExtensionContext.BeforeWrite context) {
+
+
+ Collection customMetadataObject = context.tableMetadata()
+ .customMetadataObject(CUSTOM_METADATA_KEY, Collection.class)
+ .orElse(null);
+
+ if (customMetadataObject == null) {
+ return WriteModification.builder().build();
+ }
+
+ Map itemToTransform = new HashMap<>(context.items());
+ customMetadataObject.forEach(key -> insertUuidInItemToTransform(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());
+ }
+
+ public static final class AttributeTags {
+
+ private AttributeTags() {
+ }
+
+ /**
+ * Tags which indicate that the given attribute is supported wih Auto Generated UUID Record Extension.
+ *
+ * @return Tag name for AutoGenerated UUID Records
+ */
+ public static StaticAttributeTag autoGeneratedUuidAttribute() {
+ return AUTO_GENERATED_UUID_ATTRIBUTE;
+ }
+ }
+
+ private static class AutoGeneratedUuidAttribute implements StaticAttributeTag {
+
+ @Override
+ public void validateType(String attributeName, EnhancedType type,
+ AttributeValueType attributeValueType) {
+
+ Validate.notNull(type, "type is null");
+ Validate.notNull(type.rawClass(), "rawClass is null");
+ Validate.notNull(attributeValueType, "attributeValueType is null");
+
+ if (!type.rawClass().equals(String.class)) {
+ throw new IllegalArgumentException(String.format(
+ "Attribute '%s' of Class type %s is not a suitable Java Class type to be used as a Auto Generated "
+ + "Uuid attribute. Only String Class type is supported.", attributeName, type.rawClass()));
+ }
+ }
+
+ @Override
+ public Consumer modifyMetadata(String attributeName,
+ AttributeValueType attributeValueType) {
+ return metadata -> metadata.addCustomMetadataObject(CUSTOM_METADATA_KEY, Collections.singleton(attributeName))
+ .markAttributeAsKey(attributeName, attributeValueType);
+ }
+ }
+}
\ No newline at end of file
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/extensions/annotations/DynamoDbAutoGeneratedUuid.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/extensions/annotations/DynamoDbAutoGeneratedUuid.java
new file mode 100644
index 000000000000..6df85903c20a
--- /dev/null
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/extensions/annotations/DynamoDbAutoGeneratedUuid.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.enhanced.dynamodb.extensions.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.UUID;
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.enhanced.dynamodb.internal.extensions.AutoGeneratedUuidTag;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.BeanTableSchemaAttributeTag;
+
+/**
+ * Denotes this attribute as recording the auto generated UUID string for the record. Every time a record with this
+ * attribute is written to the database it will update the attribute with a {@link UUID#randomUUID} string.
+ */
+@SdkPublicApi
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@BeanTableSchemaAttributeTag(AutoGeneratedUuidTag.class)
+public @interface DynamoDbAutoGeneratedUuid {
+}
\ No newline at end of file
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/extensions/AutoGeneratedUuidTag.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/extensions/AutoGeneratedUuidTag.java
new file mode 100644
index 000000000000..17872e71954b
--- /dev/null
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/extensions/AutoGeneratedUuidTag.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.enhanced.dynamodb.internal.extensions;
+
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.enhanced.dynamodb.extensions.AutoGeneratedUuidExtension;
+import software.amazon.awssdk.enhanced.dynamodb.extensions.annotations.DynamoDbAutoGeneratedUuid;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTag;
+
+@SdkInternalApi
+public final class AutoGeneratedUuidTag {
+
+ private AutoGeneratedUuidTag() {
+ }
+
+ public static StaticAttributeTag attributeTagFor(DynamoDbAutoGeneratedUuid annotation) {
+ return AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute();
+ }
+
+}
\ No newline at end of file
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
new file mode 100644
index 000000000000..cc69f503d50f
--- /dev/null
+++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedUuidExtensionTest.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.enhanced.dynamodb.extensions;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTags.primaryPartitionKey;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.regex.Pattern;
+import org.junit.jupiter.api.Test;
+import software.amazon.awssdk.enhanced.dynamodb.OperationContext;
+import software.amazon.awssdk.enhanced.dynamodb.TableMetadata;
+import software.amazon.awssdk.enhanced.dynamodb.internal.extensions.DefaultDynamoDbExtensionContext;
+import software.amazon.awssdk.enhanced.dynamodb.internal.operations.DefaultOperationContext;
+import software.amazon.awssdk.enhanced.dynamodb.internal.operations.OperationName;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema;
+import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
+
+public class AutoGeneratedUuidExtensionTest {
+
+ private static final String UUID_REGEX =
+ "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$";
+
+ private static final Pattern UUID_PATTERN = Pattern.compile(UUID_REGEX);
+
+ private static final String RECORD_ID = "id123";
+
+ private static final String TABLE_NAME = "table-name";
+ private static final OperationContext PRIMARY_CONTEXT =
+ DefaultOperationContext.create(TABLE_NAME, TableMetadata.primaryIndexName());
+
+ private final AutoGeneratedUuidExtension atomicCounterExtension = AutoGeneratedUuidExtension.create();
+
+
+ private static final StaticTableSchema ITEM_WITH_UUID_MAPPER =
+ StaticTableSchema.builder(ItemWithUuid.class)
+ .newItemSupplier(ItemWithUuid::new)
+ .addAttribute(String.class, a -> a.name("id")
+ .getter(ItemWithUuid::getId)
+ .setter(ItemWithUuid::setId)
+ .addTag(primaryPartitionKey()))
+ .addAttribute(String.class, a -> a.name("uuidAttribute")
+ .getter(ItemWithUuid::getUuidAttribute)
+ .setter(ItemWithUuid::setUuidAttribute)
+ .addTag(AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute())
+ )
+ .addAttribute(String.class, a -> a.name("simpleString")
+ .getter(ItemWithUuid::getSimpleString)
+ .setter(ItemWithUuid::setSimpleString))
+ .build();
+
+ @Test
+ public void beforeWrite_updateItemOperation_hasUuidInItem_doesNotCreateUpdateExpressionAndFilters() {
+ ItemWithUuid SimpleItem = new ItemWithUuid();
+ SimpleItem.setId(RECORD_ID);
+ String uuidAttribute = String.valueOf(UUID.randomUUID());
+ SimpleItem.setUuidAttribute(uuidAttribute);
+
+ Map items = ITEM_WITH_UUID_MAPPER.itemToMap(SimpleItem, true);
+ assertThat(items).hasSize(2);
+
+ WriteModification result =
+ atomicCounterExtension.beforeWrite(DefaultDynamoDbExtensionContext.builder()
+ .items(items)
+ .tableMetadata(ITEM_WITH_UUID_MAPPER.tableMetadata())
+ .operationName(OperationName.UPDATE_ITEM)
+ .operationContext(PRIMARY_CONTEXT).build());
+
+ 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();
+
+ }
+
+ @Test
+ public void beforeWrite_updateItemOperation_hasNoUuidInItem_doesNotCreatesUpdateExpressionAndFilters() {
+ ItemWithUuid SimpleItem = new ItemWithUuid();
+ SimpleItem.setId(RECORD_ID);
+
+ Map items = ITEM_WITH_UUID_MAPPER.itemToMap(SimpleItem, true);
+ assertThat(items).hasSize(1);
+
+ WriteModification result =
+ atomicCounterExtension.beforeWrite(DefaultDynamoDbExtensionContext.builder()
+ .items(items)
+ .tableMetadata(ITEM_WITH_UUID_MAPPER.tableMetadata())
+ .operationName(OperationName.UPDATE_ITEM)
+ .operationContext(PRIMARY_CONTEXT).build());
+
+ 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();
+ }
+
+ @Test
+ public void beforeWrite_updateItemOperation_UuidNotPresent_newUuidCreated() {
+ ItemWithUuid item = new ItemWithUuid();
+ item.setId(RECORD_ID);
+
+ Map items = ITEM_WITH_UUID_MAPPER.itemToMap(item, true);
+ assertThat(items).hasSize(1);
+
+ WriteModification result =
+ atomicCounterExtension.beforeWrite(DefaultDynamoDbExtensionContext.builder()
+ .items(items)
+ .tableMetadata(ITEM_WITH_UUID_MAPPER.tableMetadata())
+ .operationName(OperationName.UPDATE_ITEM)
+ .operationContext(PRIMARY_CONTEXT).build());
+ assertThat(result.transformedItem()).isNotNull();
+ assertThat(result.updateExpression()).isNull();
+ assertThat(result.transformedItem()).hasSize(2);
+ assertThat(isValidUuid(result.transformedItem().get("uuidAttribute").s())).isTrue();
+ }
+
+ @Test
+ void IllegalArgumentException_for_AutogeneratedUuid_withNonStringType() {
+
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> StaticTableSchema.builder(ItemWithUuid.class)
+ .newItemSupplier(ItemWithUuid::new)
+ .addAttribute(String.class, a -> a.name("id")
+ .getter(ItemWithUuid::getId)
+ .setter(ItemWithUuid::setId)
+ .addTag(primaryPartitionKey()))
+ .addAttribute(Integer.class, a -> a.name("intAttribute")
+ .getter(ItemWithUuid::getIntAttribute)
+ .setter(ItemWithUuid::setIntAttribute)
+ .addTag(AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute())
+ )
+ .addAttribute(String.class, a -> a.name("simpleString")
+ .getter(ItemWithUuid::getSimpleString)
+ .setter(ItemWithUuid::setSimpleString))
+ .build())
+
+ .withMessage("Attribute 'intAttribute' of Class type class java.lang.Integer is not a suitable Java Class type"
+ + " to be used as a Auto Generated Uuid attribute. Only String Class type is supported.");
+ }
+
+ public static boolean isValidUuid(String uuid) {
+ return UUID_PATTERN.matcher(uuid).matches();
+ }
+
+ private static class ItemWithUuid {
+
+ private String id;
+ private String uuidAttribute;
+ private String simpleString;
+ private Integer intAttribute;
+
+ public Integer getIntAttribute() {
+ return intAttribute;
+ }
+
+ public void setIntAttribute(Integer intAttribute) {
+ this.intAttribute = intAttribute;
+ }
+
+ public ItemWithUuid() {
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getUuidAttribute() {
+ return uuidAttribute;
+ }
+
+ public void setUuidAttribute(String uuidAttribute) {
+ this.uuidAttribute = uuidAttribute;
+ }
+
+ public String getSimpleString() {
+ return simpleString;
+ }
+
+ public void setSimpleString(String simpleString) {
+ this.simpleString = simpleString;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ItemWithUuid that = (ItemWithUuid) o;
+ return Objects.equals(id, that.id) && Objects.equals(uuidAttribute, that.uuidAttribute) && Objects.equals(simpleString, that.simpleString);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, uuidAttribute, simpleString);
+ }
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000000..12ca834b0f8c
--- /dev/null
+++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedUuidRecordTest.java
@@ -0,0 +1,488 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.enhanced.dynamodb.functionaltests;
+
+import static java.util.stream.Collectors.toList;
+import static software.amazon.awssdk.enhanced.dynamodb.extensions.AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute;
+import static software.amazon.awssdk.enhanced.dynamodb.internal.AttributeValues.stringValue;
+import static software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTags.primaryPartitionKey;
+import static software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTags.updateBehavior;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.regex.Pattern;
+import java.util.stream.IntStream;
+import org.assertj.core.api.Assertions;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
+import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
+import software.amazon.awssdk.enhanced.dynamodb.Expression;
+import software.amazon.awssdk.enhanced.dynamodb.OperationContext;
+import software.amazon.awssdk.enhanced.dynamodb.TableMetadata;
+import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
+import software.amazon.awssdk.enhanced.dynamodb.extensions.AutoGeneratedUuidExtension;
+import software.amazon.awssdk.enhanced.dynamodb.extensions.annotations.DynamoDbAutoGeneratedUuid;
+import software.amazon.awssdk.enhanced.dynamodb.internal.operations.DefaultOperationContext;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.UpdateBehavior;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbFlatten;
+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.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{
+
+ private static final String UUID_REGEX =
+ "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$";
+
+ private static final Pattern UUID_PATTERN = Pattern.compile(UUID_REGEX);
+
+ public static void assertValidUuid(String uuid) {
+ Assertions.assertThat(UUID_PATTERN.matcher(uuid).matches()).isTrue();
+ }
+
+ private static final String TABLE_NAME = "table-name";
+ private static final OperationContext PRIMARY_CONTEXT =
+ DefaultOperationContext.create(TABLE_NAME, TableMetadata.primaryIndexName());
+
+
+ public AutoGeneratedUuidRecordTest(String testName, TableSchema recordTableSchema) {
+ this.mappedTable = DynamoDbEnhancedClient.builder()
+ .dynamoDbClient(getDynamoDbClient())
+ .extensions(AutoGeneratedUuidExtension.create())
+ .build().table(getConcreteTableName("table-name"),
+ recordTableSchema);
+ this.testCaseName = testName;
+ }
+
+ private static final TableSchema FLATTENED_TABLE_SCHEMA =
+ StaticTableSchema.builder(FlattenedRecord.class)
+ .newItemSupplier(FlattenedRecord::new)
+ .addAttribute(String.class, a -> a.name("generated")
+ .getter(FlattenedRecord::getGenerated)
+ .setter(FlattenedRecord::generated)
+ .tags(autoGeneratedUuidAttribute()))
+ .build();
+
+ private static final TableSchema TABLE_SCHEMA =
+ StaticTableSchema.builder(Record.class)
+ .newItemSupplier(Record::new)
+ .addAttribute(String.class, a -> a.name("id")
+ .getter(Record::getId)
+ .setter(Record::id)
+ .tags(primaryPartitionKey()))
+ .addAttribute(String.class, a -> a.name("attribute")
+ .getter(Record::getAttribute)
+ .setter(Record::attribute))
+ .addAttribute(String.class, a -> a.name("lastUpdatedUuid")
+ .getter(Record::getLastUpdatedUuid)
+ .setter(Record::lastUpdatedUuid)
+ .tags(autoGeneratedUuidAttribute()))
+ .addAttribute(String.class, a -> a.name("createdUuid")
+ .getter(Record::getCreatedUuid)
+ .setter(Record::createdUuid)
+ .tags(autoGeneratedUuidAttribute(),
+ updateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS)))
+ .flatten(FLATTENED_TABLE_SCHEMA, Record::getFlattenedRecord, Record::flattenedRecord)
+ .build();
+
+
+ private final List