diff --git a/.changes/next-release/bugfix-AmazonDynamoDB-2bb1833.json b/.changes/next-release/bugfix-AmazonDynamoDB-2bb1833.json new file mode 100644 index 000000000000..70004958c862 --- /dev/null +++ b/.changes/next-release/bugfix-AmazonDynamoDB-2bb1833.json @@ -0,0 +1,6 @@ +{ + "category": "Amazon DynamoDB", + "contributor": "martinKindall", + "type": "bugfix", + "description": "Created static method EnumAttributeConverter::createWithNameAsKeys which creates a converter based on the Enum::name method to identify enums, rather than Enum::toString. This is preferable because Enum::name is final and cannot be overwritten, as opposed to Enum::toString. EnumAttributeConverter::create is kept as it is, for backward compatibility." +} diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/DefaultAttributeConverterProvider.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/DefaultAttributeConverterProvider.java index 45db89f5283c..88cfbe39e82f 100644 --- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/DefaultAttributeConverterProvider.java +++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/DefaultAttributeConverterProvider.java @@ -43,7 +43,6 @@ import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.DocumentAttributeConverter; import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.DoubleAttributeConverter; import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.DurationAttributeConverter; -import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.EnumAttributeConverter; import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.FloatAttributeConverter; import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.InstantAsStringAttributeConverter; import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.IntegerAttributeConverter; diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/EnumAttributeConverter.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/EnumAttributeConverter.java new file mode 100644 index 000000000000..a44a5e2070f0 --- /dev/null +++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/EnumAttributeConverter.java @@ -0,0 +1,138 @@ +/* + * 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; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Function; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.utils.Validate; + +/** + * A converter between an {@link Enum} and {@link AttributeValue}. + * + *

+ * This stores values in DynamoDB as a string. + * + *

+ * Use EnumAttributeConverter::create in order to use Enum::toString as the enum identifier + * + *

+ * Use EnumAttributeConverter::createWithNameAsKeys in order to use Enum::name as the enum identifier + * + *

+ * This can be created via {@link #create(Class)}. + */ +@SdkPublicApi +public final class EnumAttributeConverter> implements AttributeConverter { + + private final Class enumClass; + private final Map enumValueMap; + + private final Function keyExtractor; + + private EnumAttributeConverter(Class enumClass, Function keyExtractor) { + this.enumClass = enumClass; + this.keyExtractor = keyExtractor; + + Map mutableEnumValueMap = new LinkedHashMap<>(); + Arrays.stream(enumClass.getEnumConstants()) + .forEach(enumConstant -> mutableEnumValueMap.put(keyExtractor.apply(enumConstant), enumConstant)); + + this.enumValueMap = Collections.unmodifiableMap(mutableEnumValueMap); + } + + /** + * Creates an EnumAttributeConverter for an {@link Enum}. + * + *

+ * Uses Enum::toString as the enum identifier. + * + * @param enumClass The enum class to be used + * @return an EnumAttributeConverter + * @param the enum subclass + */ + public static > EnumAttributeConverter create(Class enumClass) { + return new EnumAttributeConverter<>(enumClass, Enum::toString); + } + + /** + * Creates an EnumAttributeConverter for an {@link Enum}. + * + *

+ * Uses Enum::name as the enum identifier. + * + * @param enumClass The enum class to be used + * @return an EnumAttributeConverter + * @param the enum subclass + */ + public static > EnumAttributeConverter createWithNameAsKeys(Class enumClass) { + return new EnumAttributeConverter<>(enumClass, Enum::name); + } + + /** + * Returns the proper {@link AttributeValue} for the given enum type. + * + * @param input the enum type to be converted + * @return AttributeValue + */ + @Override + public AttributeValue transformFrom(T input) { + return AttributeValue.builder().s(keyExtractor.apply(input)).build(); + } + + /** + * Returns the proper enum type for the given {@link AttributeValue} input. + * + * @param input the AttributeValue to be converted + * @return an enum type + */ + @Override + public T transformTo(AttributeValue input) { + Validate.isTrue(input.s() != null, "Cannot convert non-string value to enum."); + T returnValue = enumValueMap.get(input.s()); + + if (returnValue == null) { + throw new IllegalArgumentException(String.format("Unable to convert string value '%s' to enum type '%s'", + input.s(), enumClass)); + } + + return returnValue; + } + + /** + * Returns the {@link EnhancedType} of the converter. + * + * @return EnhancedType + */ + @Override + public EnhancedType type() { + return EnhancedType.of(enumClass); + } + + /** + * Returns the {@link AttributeValueType} of the converter. + * + * @return AttributeValueType + */ + @Override + public AttributeValueType attributeValueType() { + return AttributeValueType.S; + } +} diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/EnumAttributeConverter.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/EnumAttributeConverter.java deleted file mode 100644 index 18395a82656b..000000000000 --- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/EnumAttributeConverter.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.converter.attribute; - -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.enhanced.dynamodb.AttributeConverter; -import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType; -import software.amazon.awssdk.enhanced.dynamodb.EnhancedType; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.utils.Validate; - -/** - * A converter between an {@link Enum} and {@link AttributeValue}. - * - *

- * This stores values in DynamoDB as a string. - * - *

- * This can be created via {@link #create(Class)}. - */ -@SdkInternalApi -public class EnumAttributeConverter> implements AttributeConverter { - - private final Class enumClass; - private final Map enumValueMap; - - private EnumAttributeConverter(Class enumClass) { - this.enumClass = enumClass; - - Map mutableEnumValueMap = new LinkedHashMap<>(); - Arrays.stream(enumClass.getEnumConstants()) - .forEach(enumConstant -> mutableEnumValueMap.put(enumConstant.toString(), enumConstant)); - - this.enumValueMap = Collections.unmodifiableMap(mutableEnumValueMap); - } - - public static > EnumAttributeConverter create(Class enumClass) { - return new EnumAttributeConverter<>(enumClass); - } - - @Override - public AttributeValue transformFrom(T input) { - return AttributeValue.builder().s(input.toString()).build(); - } - - @Override - public T transformTo(AttributeValue input) { - Validate.isTrue(input.s() != null, "Cannot convert non-string value to enum."); - T returnValue = enumValueMap.get(input.s()); - - if (returnValue == null) { - throw new IllegalArgumentException(String.format("Unable to convert string value '%s' to enum type '%s'", - input.s(), enumClass)); - } - - return returnValue; - } - - @Override - public EnhancedType type() { - return EnhancedType.of(enumClass); - } - - @Override - public AttributeValueType attributeValueType() { - return AttributeValueType.S; - } -} diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/converters/attribute/EnumAttributeConverterTest.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/converters/attribute/EnumAttributeConverterTest.java new file mode 100644 index 000000000000..fe17f3050533 --- /dev/null +++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/converters/attribute/EnumAttributeConverterTest.java @@ -0,0 +1,113 @@ +/* + * 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.converters.attribute; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.enhanced.dynamodb.EnumAttributeConverter; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; + +import static org.assertj.core.api.Assertions.assertThat; + +public class EnumAttributeConverterTest { + + @Test + public void transformFromDefault_returnsToString() { + EnumAttributeConverter vehicleConverter = EnumAttributeConverter.create(Vehicle.class); + AttributeValue attribute = vehicleConverter.transformFrom(Vehicle.TRUCK); + + assertThat(attribute.s()).isEqualTo("TRUCK"); + } + + @Test + public void transformToDefault_returnsEnum() { + EnumAttributeConverter vehicleConverter = EnumAttributeConverter.create(Vehicle.class); + + Vehicle bike = vehicleConverter.transformTo(AttributeValue.fromS("BIKE")); + + assertThat(bike).isEqualTo(Vehicle.BIKE); + } + + @Test + public void transformFromDefault_returnsToString_2() { + EnumAttributeConverter animalConverter = EnumAttributeConverter.create(Animal.class); + AttributeValue attribute = animalConverter.transformFrom(Animal.CAT); + + assertThat(attribute.s()).isEqualTo("I am a Cat!"); + } + + @Test + public void transformToDefault_returnsEnum_2() { + EnumAttributeConverter animalConverter = EnumAttributeConverter.create(Animal.class); + + Animal dog = animalConverter.transformTo(AttributeValue.fromS("I am a Dog!")); + + assertThat(dog).isEqualTo(Animal.DOG); + } + + @Test + public void transformFromWithNames_returnsName() { + EnumAttributeConverter personConverter = EnumAttributeConverter.createWithNameAsKeys(Person.class); + AttributeValue attribute = personConverter.transformFrom(Person.JANE); + + assertThat(attribute.s()).isEqualTo("JANE"); + + assertThat(Person.JANE.toString()).isEqualTo("I am a cool person"); + } + + @Test + public void transformToWithNames_returnsEnum() { + EnumAttributeConverter personConverter = EnumAttributeConverter.createWithNameAsKeys(Person.class); + + Person john = personConverter.transformTo(AttributeValue.fromS("JOHN")); + + assertThat(Person.JOHN.toString()).isEqualTo("I am a cool person"); + + assertThat(john).isEqualTo(Person.JOHN); + } + + private static enum Vehicle { + CAR, + BIKE, + TRUCK + } + + private static enum Animal { + DOG, + CAT; + + @Override + public String toString() { + switch (this) { + case DOG: + return "I am a Dog!"; + case CAT: + return "I am a Cat!"; + default: + return null; + } + } + } + + private static enum Person { + JOHN, + JANE; + + @Override + public String toString() { + return "I am a cool person"; + } + } +}