Skip to content

Commit f870864

Browse files
feat(6418): Add customizable UUID supplier in AutoGeneratedUuidExtension
- Introduced a customizable `uuidSupplier` in `AutoGeneratedUuidExtension` with default `UUID.randomUUID()` functionality. - Added a builder pattern to create extensions with custom suppliers. - Enhanced tests to validate the custom UUID supplier functionality and maintain backward compatibility. - Refactoring all tests and add more scenarios; - Fixing AutoGeneratedUuidExtension`s JavaDoc
1 parent b3fe008 commit f870864

File tree

5 files changed

+414
-322
lines changed

5 files changed

+414
-322
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS DynamoDB Enhanced Client",
4+
"description": "Refactoring `AutoGeneratedUuidExtension` to accepted UUID supplier to make possible using other UUID versions.",
5+
"contributor": "marcusvoltolim"
6+
}

services-custom/dynamodb-enhanced/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
<properties>
3131
<awsjavasdk.version>${project.parent.version}</awsjavasdk.version>
3232
<jre.version>1.8</jre.version>
33+
<fasterxml-uuid.version>5.1.0</fasterxml-uuid.version>
3334
</properties>
3435

3536
<!-- Additional configuration required to enable DynamoDb-Local to work in tests -->
@@ -233,5 +234,12 @@
233234
<type>so</type>
234235
<scope>test</scope>
235236
</dependency>
237+
<dependency>
238+
<groupId>com.fasterxml.uuid</groupId>
239+
<artifactId>java-uuid-generator</artifactId>
240+
<version>${fasterxml-uuid.version}</version>
241+
<scope>test</scope>
242+
</dependency>
243+
236244
</dependencies>
237245
</project>

services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedUuidExtension.java

Lines changed: 91 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.util.Map;
2222
import java.util.UUID;
2323
import java.util.function.Consumer;
24+
import java.util.function.Supplier;
25+
import software.amazon.awssdk.annotations.NotThreadSafe;
2426
import software.amazon.awssdk.annotations.SdkPublicApi;
2527
import software.amazon.awssdk.annotations.ThreadSafe;
2628
import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType;
@@ -33,55 +35,70 @@
3335
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
3436
import software.amazon.awssdk.utils.Validate;
3537

36-
3738
/**
38-
* This extension facilitates the automatic generation of a unique UUID (Universally Unique Identifier) for a specified attribute
39-
* every time a new record is written to the database. The generated UUID is obtained using the
40-
* {@link java.util.UUID#randomUUID()} method.
39+
* This extension facilitates the automatic generation of a unique UUID (Universally Unique Identifier) for a specified attribute every time a new record is
40+
* written to the database. The generated UUID is obtained using the {@link java.util.UUID#randomUUID()} method by default or a custom UUID supplier instance
41+
* provided by the user.
4142
* <p>
42-
* This extension is not loaded by default when you instantiate a
43-
* {@link software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient}. Therefore, you need to specify it in a custom
44-
* extension when creating the enhanced client.
43+
* This extension is not loaded by default when you instantiate a {@link software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient}. Therefore, you need
44+
* to specify it in a custom extension when creating the enhanced client.
4545
* <p>
4646
* Example to add AutoGeneratedUuidExtension along with default extensions is
47-
* {@snippet :
48-
* DynamoDbEnhancedClient.builder().extensions(Stream.concat(ExtensionResolver.defaultExtensions().stream(),
49-
* Stream.of(AutoGeneratedUuidExtension.create())).collect(Collectors.toList())).build();
50-
*}
47+
* <code>DynamoDbEnhancedClient.builder().extensions(Stream.concat(ExtensionResolver.defaultExtensions().stream(),
48+
* Stream.of(AutoGeneratedUuidExtension.create())).collect(Collectors.toList())).build();</code>
5149
* </p>
5250
* <p>
5351
* Example to just add AutoGeneratedUuidExtension without default extensions is
54-
* {@snippet :
55-
* DynamoDbEnhancedClient.builder().extensions(AutoGeneratedUuidExtension.create()).build();
56-
*}
52+
* <code>DynamoDbEnhancedClient.builder().extensions(AutoGeneratedUuidExtension.create())).build();</code>
53+
* </p>
5754
* </p>
5855
* <p>
59-
* To utilize the auto-generated UUID feature, first, create a field in your model that will store the UUID for the attribute.
60-
* This class field must be of type {@link java.lang.String}, and you need to tag it as the autoGeneratedUuidAttribute. If you are
61-
* using the {@link software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema}, then you should use the
62-
* {@link software.amazon.awssdk.enhanced.dynamodb.extensions.annotations.DynamoDbAutoGeneratedUuid} annotation. If you are using
63-
* the {@link software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema}, then you should use the
64-
* {@link
65-
* software.amazon.awssdk.enhanced.dynamodb.extensions.AutoGeneratedUuidExtension.AttributeTags#autoGeneratedUuidAttribute()}
66-
* static attribute tag.
56+
* 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
57+
* {@link java.lang.String}, and you need to tag it as the autoGeneratedUuidAttribute. If you are using the
58+
* {@link software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema}, then you should use the
59+
* {@link software.amazon.awssdk.enhanced.dynamodb.extensions.annotations.DynamoDbAutoGeneratedUuid} annotation. If you are using the
60+
* {@link software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema}, then you should use the {@link AttributeTags#autoGeneratedUuidAttribute()} static
61+
* attribute tag.
6762
* </p>
6863
* <p>
69-
* Every time a new record is successfully put into the database, the specified attribute will be automatically populated with a
70-
* unique UUID generated using {@link java.util.UUID#randomUUID()}. If the UUID needs to be created only for `putItem` and should
71-
* not be generated for an `updateItem`, then
72-
* {@link software.amazon.awssdk.enhanced.dynamodb.mapper.UpdateBehavior#WRITE_IF_NOT_EXISTS} must be along with
73-
* {@link DynamoDbUpdateBehavior}
74-
*
64+
* Every time a new record is successfully put into the database, the specified attribute will be automatically populated with a unique UUID generated using
65+
* {@link java.util.UUID#randomUUID()}.
66+
* </br>
67+
* If the UUID needs to be created only for `putItem` and should not be generated for an `updateItem`, then
68+
* {@link software.amazon.awssdk.enhanced.dynamodb.mapper.UpdateBehavior#WRITE_IF_NOT_EXISTS} must be along with {@link DynamoDbUpdateBehavior}.
7569
* </p>
7670
*/
7771
@SdkPublicApi
7872
@ThreadSafe
7973
public final class AutoGeneratedUuidExtension implements DynamoDbEnhancedClientExtension {
80-
private static final String CUSTOM_METADATA_KEY =
81-
"software.amazon.awssdk.enhanced.dynamodb.extensions.AutoGeneratedUuidExtension:AutoGeneratedUuidAttribute";
74+
75+
private static final String CUSTOM_METADATA_KEY = "AutoGeneratedUuidExtension:AutoGeneratedUuidAttribute";
8276
private static final AutoGeneratedUuidAttribute AUTO_GENERATED_UUID_ATTRIBUTE = new AutoGeneratedUuidAttribute();
8377

78+
private final Supplier<UUID> uuidSupplier;
79+
8480
private AutoGeneratedUuidExtension() {
81+
this.uuidSupplier = UUID::randomUUID;
82+
}
83+
84+
private AutoGeneratedUuidExtension(final Builder builder) {
85+
this.uuidSupplier = builder.uuidSupplier == null ? UUID::randomUUID : builder.uuidSupplier;
86+
}
87+
88+
/**
89+
* Create a builder that can be used to create a {@link AutoGeneratedUuidExtension}.
90+
*
91+
* @return Builder to create AutoGeneratedUuidExtension,
92+
*/
93+
public static Builder builder() {
94+
return new Builder();
95+
}
96+
97+
/**
98+
* Returns a builder initialized with all existing values on the Extension object.
99+
*/
100+
public Builder toBuilder() {
101+
return builder().uuidSupplier(this.uuidSupplier);
85102
}
86103

87104
/**
@@ -98,9 +115,7 @@ public static AutoGeneratedUuidExtension create() {
98115
* @return WriteModification String updated with attribute updated with Extension.
99116
*/
100117
@Override
101-
public WriteModification beforeWrite(DynamoDbExtensionContext.BeforeWrite context) {
102-
103-
118+
public WriteModification beforeWrite(final DynamoDbExtensionContext.BeforeWrite context) {
104119
Collection<String> customMetadataObject = context.tableMetadata()
105120
.customMetadataObject(CUSTOM_METADATA_KEY, Collection.class)
106121
.orElse(null);
@@ -116,9 +131,8 @@ public WriteModification beforeWrite(DynamoDbExtensionContext.BeforeWrite contex
116131
.build();
117132
}
118133

119-
private void insertUuidInItemToTransform(Map<String, AttributeValue> itemToTransform,
120-
String key) {
121-
itemToTransform.put(key, AttributeValue.builder().s(UUID.randomUUID().toString()).build());
134+
private void insertUuidInItemToTransform(final Map<String, AttributeValue> itemToTransform, final String key) {
135+
itemToTransform.put(key, AttributeValue.builder().s(uuidSupplier.get().toString()).build());
122136
}
123137

124138
public static final class AttributeTags {
@@ -134,14 +148,15 @@ private AttributeTags() {
134148
public static StaticAttributeTag autoGeneratedUuidAttribute() {
135149
return AUTO_GENERATED_UUID_ATTRIBUTE;
136150
}
151+
137152
}
138153

139154
private static class AutoGeneratedUuidAttribute implements StaticAttributeTag {
140155

141156
@Override
142-
public <R> void validateType(String attributeName, EnhancedType<R> type,
143-
AttributeValueType attributeValueType) {
144-
157+
public <R> void validateType(final String attributeName,
158+
final EnhancedType<R> type,
159+
final AttributeValueType attributeValueType) {
145160
Validate.notNull(type, "type is null");
146161
Validate.notNull(type.rawClass(), "rawClass is null");
147162
Validate.notNull(attributeValueType, "attributeValueType is null");
@@ -154,10 +169,44 @@ public <R> void validateType(String attributeName, EnhancedType<R> type,
154169
}
155170

156171
@Override
157-
public Consumer<StaticTableMetadata.Builder> modifyMetadata(String attributeName,
158-
AttributeValueType attributeValueType) {
172+
public Consumer<StaticTableMetadata.Builder> modifyMetadata(final String attributeName,
173+
final AttributeValueType attributeValueType) {
159174
return metadata -> metadata.addCustomMetadataObject(CUSTOM_METADATA_KEY, Collections.singleton(attributeName))
160175
.markAttributeAsKey(attributeName, attributeValueType);
161176
}
177+
162178
}
163-
}
179+
180+
/**
181+
* Builder for a {@link AutoGeneratedUuidExtension}
182+
*/
183+
@NotThreadSafe
184+
public static final class Builder {
185+
186+
private Supplier<UUID> uuidSupplier;
187+
188+
private Builder() {
189+
}
190+
191+
/**
192+
* Sets the UUID supplier instance , else {@link UUID#randomUUID()} is used by default. Every time a new UUID is generated this supplier will be used to
193+
* get the current UUID. If a custom supplier is not specified, the default randomUUID supplier will be used.
194+
*
195+
* @param uuidSupplier Supplier instance to set the current UUID.
196+
* @return This builder for method chaining.
197+
*/
198+
public Builder uuidSupplier(final Supplier<UUID> uuidSupplier) {
199+
this.uuidSupplier = uuidSupplier;
200+
return this;
201+
}
202+
203+
/**
204+
* Builds an {@link AutoGeneratedUuidExtension} based on the values stored in this builder
205+
*/
206+
public AutoGeneratedUuidExtension build() {
207+
return new AutoGeneratedUuidExtension(this);
208+
}
209+
210+
}
211+
212+
}

0 commit comments

Comments
 (0)