Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "AWS DynamoDB Enhanced Client",
"description": "Refactoring `AutoGeneratedUuidExtension` to accepted UUID supplier to make possible using other UUID versions.",
"contributor": "marcusvoltolim"
}
8 changes: 8 additions & 0 deletions services-custom/dynamodb-enhanced/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<properties>
<awsjavasdk.version>${project.parent.version}</awsjavasdk.version>
<jre.version>1.8</jre.version>
<fasterxml-uuid.version>5.1.0</fasterxml-uuid.version>
</properties>

<!-- Additional configuration required to enable DynamoDb-Local to work in tests -->
Expand Down Expand Up @@ -233,5 +234,12 @@
<type>so</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.uuid</groupId>
<artifactId>java-uuid-generator</artifactId>
<version>${fasterxml-uuid.version}</version>
<scope>test</scope>
</dependency>

</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import java.util.Map;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Supplier;
import software.amazon.awssdk.annotations.NotThreadSafe;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.annotations.ThreadSafe;
import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType;
Expand All @@ -33,74 +35,71 @@
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.
* {@link java.util.UUID#randomUUID()} method by default or a custom UUID supplier instance provided by the user.
* <p>
* 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.
* <p>
* 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();
*}
* {@code DynamoDbEnhancedClient.builder().extensions(Stream.concat(ExtensionResolver.defaultExtensions().stream(),
* Stream.of(AutoGeneratedUuidExtension.create())).collect(Collectors.toList())).build();}
* </p>
* <p>
* Example to just add AutoGeneratedUuidExtension without default extensions is
* {@snippet :
* DynamoDbEnhancedClient.builder().extensions(AutoGeneratedUuidExtension.create()).build();
*}
* {@code DynamoDbEnhancedClient.builder().extensions(AutoGeneratedUuidExtension.create())).build();}
* </p>
* <p>
* 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.
* {@link AttributeTags#autoGeneratedUuidAttribute()} static attribute tag.
* </p>
* <p>
* 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
* unique UUID generated using {@link java.util.UUID#randomUUID()}.
* <br>
* 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}
*
* {@link DynamoDbUpdateBehavior}.
* </p>
*/
@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 static final String CUSTOM_METADATA_KEY = "AutoGeneratedUuidExtension:AutoGeneratedUuidAttribute";
private static final StaticAttributeTag AUTO_GENERATED_UUID_ATTRIBUTE = new AutoGeneratedUuidAttribute();

private final Supplier<UUID> uuidSupplier;

private AutoGeneratedUuidExtension() {
this.uuidSupplier = UUID::randomUUID;
}

private AutoGeneratedUuidExtension(Builder builder) {
this.uuidSupplier = builder.uuidSupplier == null ? UUID::randomUUID : builder.uuidSupplier;
}

public static Builder builder() {
return new Builder();
}

public Builder toBuilder() {
return builder().uuidSupplier(this.uuidSupplier);
}

/**
* @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<String> customMetadataObject = context.tableMetadata()
.customMetadataObject(CUSTOM_METADATA_KEY, Collection.class)
.orElse(null);
Expand All @@ -116,32 +115,25 @@ public WriteModification beforeWrite(DynamoDbExtensionContext.BeforeWrite contex
.build();
}

private void insertUuidInItemToTransform(Map<String, AttributeValue> itemToTransform,
String key) {
itemToTransform.put(key, AttributeValue.builder().s(UUID.randomUUID().toString()).build());
private void insertUuidInItemToTransform(Map<String, AttributeValue> itemToTransform, String key) {
itemToTransform.put(key, AttributeValue.builder().s(uuidSupplier.get().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 <R> void validateType(String attributeName, EnhancedType<R> type,
AttributeValueType attributeValueType) {

public <R> void validateType(String attributeName, EnhancedType<R> type, AttributeValueType attributeValueType) {
Validate.notNull(type, "type is null");
Validate.notNull(type.rawClass(), "rawClass is null");
Validate.notNull(attributeValueType, "attributeValueType is null");
Expand All @@ -159,5 +151,34 @@ public Consumer<StaticTableMetadata.Builder> modifyMetadata(String attributeName
return metadata -> metadata.addCustomMetadataObject(CUSTOM_METADATA_KEY, Collections.singleton(attributeName))
.markAttributeAsKey(attributeName, attributeValueType);
}

}

@NotThreadSafe
public static final class Builder {

private Supplier<UUID> uuidSupplier;

private Builder() {
}

/**
* 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 get the current UUID. If a custom supplier is not specified, the default randomUUID
* supplier will be used.
*
* @param uuidSupplier Supplier instance to set the current UUID.
* @return This builder for method chaining.
*/
public Builder uuidSupplier(Supplier<UUID> uuidSupplier) {
this.uuidSupplier = uuidSupplier;
return this;
}

public AutoGeneratedUuidExtension build() {
return new AutoGeneratedUuidExtension(this);
}

}
}

}
Loading