diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/mapper/ResolvedImmutableAttribute.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/mapper/ResolvedImmutableAttribute.java index 4a3a3402894..3c087a717b8 100644 --- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/mapper/ResolvedImmutableAttribute.java +++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/mapper/ResolvedImmutableAttribute.java @@ -27,33 +27,40 @@ import software.amazon.awssdk.services.dynamodb.model.AttributeValue; @SdkInternalApi -public final class ResolvedImmutableAttribute { +public final class ResolvedImmutableAttribute { private final String attributeName; - private final Function getAttributeMethod; private final BiConsumer updateBuilderMethod; private final StaticTableMetadata tableMetadata; private final AttributeConverter attributeConverter; + private final Function getter; + private final AttributeType attributeType; + private ResolvedImmutableAttribute(String attributeName, - Function getAttributeMethod, BiConsumer updateBuilderMethod, StaticTableMetadata tableMetadata, - AttributeConverter attributeConverter) { + AttributeConverter attributeConverter, + Function getter, + AttributeType attributeType) { this.attributeName = attributeName; - this.getAttributeMethod = getAttributeMethod; this.updateBuilderMethod = updateBuilderMethod; this.tableMetadata = tableMetadata; this.attributeConverter = attributeConverter; + this.getter = getter; + this.attributeType = attributeType; + } + + AttributeValue getAttributeValue(T item) { + R value = getter.apply(item); + return value == null ? nullAttributeValue() + : attributeType.objectToAttributeValue(value); } - public static ResolvedImmutableAttribute create(ImmutableAttribute immutableAttribute, + public static ResolvedImmutableAttribute create(ImmutableAttribute immutableAttribute, AttributeConverter attributeConverter) { AttributeType attributeType = StaticAttributeType.create(attributeConverter); - Function getAttributeValueWithTransform = item -> { - R value = immutableAttribute.getter().apply(item); - return value == null ? nullAttributeValue() : attributeType.objectToAttributeValue(value); - }; + Function getter = immutableAttribute.getter(); // When setting a value on the java object, do not explicitly set nulls as this can cause an NPE to be thrown // if the target attribute type is a primitive. @@ -78,27 +85,27 @@ public static ResolvedImmutableAttribute create(ImmutableAttribu tag.modifyMetadata(immutableAttribute.name(), attributeType.attributeValueType()).accept(tableMetadataBuilder); }); return new ResolvedImmutableAttribute<>(immutableAttribute.name(), - getAttributeValueWithTransform, updateBuilderWithTransform, tableMetadataBuilder.build(), - attributeConverter); + attributeConverter, + getter, + attributeType); } - public ResolvedImmutableAttribute transform( + public ResolvedImmutableAttribute transform( Function transformItem, Function transformBuilder) { return new ResolvedImmutableAttribute<>( attributeName, + (item, value) -> updateBuilderMethod.accept(transformBuilder.apply(item), value), + tableMetadata, + attributeConverter, item -> { T otherItem = transformItem.apply(item); - - // If the containing object is null don't attempt to read attributes from it - return otherItem == null ? - nullAttributeValue() : getAttributeMethod.apply(otherItem); + return otherItem == null ? null : getter.apply(otherItem); }, - (item, value) -> updateBuilderMethod.accept(transformBuilder.apply(item), value), - tableMetadata, attributeConverter); + attributeType); } public String attributeName() { @@ -106,7 +113,7 @@ public String attributeName() { } public Function attributeGetterMethod() { - return getAttributeMethod; + return this::getAttributeValue; } public BiConsumer updateItemMethod() { diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableAttribute.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableAttribute.java index d5622bfc6df..d390c03a91d 100644 --- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableAttribute.java +++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableAttribute.java @@ -173,7 +173,7 @@ public Builder toBuilder() { } - ResolvedImmutableAttribute resolve(AttributeConverterProvider attributeConverterProvider) { + ResolvedImmutableAttribute resolve(AttributeConverterProvider attributeConverterProvider) { return ResolvedImmutableAttribute.create(this, converterFrom(attributeConverterProvider)); } diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticImmutableTableSchema.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticImmutableTableSchema.java index 793d370c8a1..d1363df57ac 100644 --- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticImmutableTableSchema.java +++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticImmutableTableSchema.java @@ -78,10 +78,10 @@ @SdkPublicApi @ThreadSafe public final class StaticImmutableTableSchema implements TableSchema { - private final List> attributeMappers; + private final List> attributeMappers; private final Supplier newBuilderSupplier; private final Function buildItemFunction; - private final Map> indexedMappers; + private final Map> indexedMappers; private final StaticTableMetadata tableMetadata; private final EnhancedType itemType; private final AttributeConverterProvider attributeConverterProvider; @@ -203,12 +203,12 @@ private StaticImmutableTableSchema(Builder builder) { ConverterProviderResolver.resolveProviders(builder.attributeConverterProviders); // Resolve declared attributes and find converters for them - Stream> attributesStream = builder.attributes == null ? + Stream> attributesStream = builder.attributes == null ? Stream.empty() : builder.attributes.stream().map(a -> a.resolve(this.attributeConverterProvider)); // Merge resolved declared attributes - List> mutableAttributeMappers = new ArrayList<>(); - Map> mutableIndexedMappers = new HashMap<>(); + List> mutableAttributeMappers = new ArrayList<>(); + Map> mutableIndexedMappers = new HashMap<>(); Set mutableAttributeNames = new LinkedHashSet<>(); Stream.concat(attributesStream, builder.additionalAttributes.stream()).forEach( resolvedAttribute -> { @@ -300,7 +300,7 @@ public static Builder builder(EnhancedType itemType, EnhancedTyp public static final class Builder { private final EnhancedType itemType; private final EnhancedType builderType; - private final List> additionalAttributes = new ArrayList<>(); + private final List> additionalAttributes = new ArrayList<>(); private final List> flattenedObjectMappers = new ArrayList<>(); private FlattenedMapperForMaps flattenedMapMapper; @@ -447,7 +447,7 @@ public Builder flatten(String mapName, Function extend(StaticImmutableTableSchema superTableSchema) { - Stream> attributeStream = + Stream> attributeStream = upcastingTransformForAttributes(superTableSchema.attributeMappers); attributeStream.forEach(this.additionalAttributes::add); return this; @@ -508,8 +508,8 @@ public StaticImmutableTableSchema build() { return new StaticImmutableTableSchema<>(this); } - private static Stream> - upcastingTransformForAttributes(Collection> superAttributes) { + private static Stream> + upcastingTransformForAttributes(Collection> superAttributes) { return superAttributes.stream().map(attribute -> attribute.transform(x -> x, x -> x)); } @@ -538,7 +538,7 @@ public T mapToItem(Map attributeMap, boolean preserveEmp AttributeValue value = entry.getValue(); if (!isNullAttributeValue(value)) { - ResolvedImmutableAttribute attributeMapper = indexedMappers.get(key); + ResolvedImmutableAttribute attributeMapper = indexedMappers.get(key); if (attributeMapper != null) { if (builder == null) { @@ -647,7 +647,7 @@ public Map itemToMap(T item, Collection attribut @Override public AttributeValue attributeValue(T item, String key) { - ResolvedImmutableAttribute attributeMapper = indexedMappers.get(key); + ResolvedImmutableAttribute attributeMapper = indexedMappers.get(key); if (attributeMapper == null) { FlattenedMapper flattenedMapper = flattenedObjectMappers.get(key); @@ -700,7 +700,7 @@ private B constructNewBuilder() { @Override public AttributeConverter converterForAttribute(Object key) { - ResolvedImmutableAttribute resolvedImmutableAttribute = indexedMappers.get(key); + ResolvedImmutableAttribute resolvedImmutableAttribute = indexedMappers.get(key); if (resolvedImmutableAttribute != null) { return resolvedImmutableAttribute.attributeConverter(); } diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableAttributeTest.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableAttributeTest.java index fac9c11388e..36ee5f3d849 100644 --- a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableAttributeTest.java +++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableAttributeTest.java @@ -29,6 +29,7 @@ import org.mockito.junit.MockitoJUnitRunner; import software.amazon.awssdk.enhanced.dynamodb.AttributeConverter; import software.amazon.awssdk.enhanced.dynamodb.AttributeConverterProvider; +import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType; import software.amazon.awssdk.enhanced.dynamodb.EnhancedType; import software.amazon.awssdk.enhanced.dynamodb.internal.mapper.ResolvedImmutableAttribute; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; @@ -219,7 +220,7 @@ public void resolve_uses_customConverter() { .attributeConverter(attributeConverter) .build(); - ResolvedImmutableAttribute resolvedAttribute = + ResolvedImmutableAttribute resolvedAttribute = staticAttribute.resolve(AttributeConverterProvider.defaultProvider()); Function attributeValueFunction = resolvedAttribute.attributeGetterMethod(); @@ -229,4 +230,43 @@ public void resolve_uses_customConverter() { assertThat(resultAttributeValue.s()).isEqualTo("test-string-custom"); } + + @Test + public void getAttributeValue_getterThrowsRuntimeException_propagatesException() { + ImmutableAttribute staticAttribute = + ImmutableAttribute.builder(SimpleItem.class, SimpleItem.class, String.class) + .name("test-attribute") + .getter(item -> { throw new RuntimeException("getter failed"); }) + .setter(SimpleItem::setAString) + .build(); + + ResolvedImmutableAttribute resolvedAttribute = + staticAttribute.resolve(AttributeConverterProvider.defaultProvider()); + + Function attributeValueFunction = resolvedAttribute.attributeGetterMethod(); + SimpleItem item = new SimpleItem("test"); + + assertThatThrownBy(() -> attributeValueFunction.apply(item)) + .isInstanceOf(RuntimeException.class) + .hasMessage("getter failed"); + } + + @Test + public void getAttributeValue_nullFromGetter_returnsNullAttributeValue() { + ImmutableAttribute staticAttribute = + ImmutableAttribute.builder(SimpleItem.class, SimpleItem.class, String.class) + .name("test-attribute") + .getter(item -> null) + .setter(SimpleItem::setAString) + .build(); + + ResolvedImmutableAttribute resolvedAttribute = + staticAttribute.resolve(AttributeConverterProvider.defaultProvider()); + + Function attributeValueFunction = resolvedAttribute.attributeGetterMethod(); + SimpleItem item = new SimpleItem("test"); + AttributeValue result = attributeValueFunction.apply(item); + + assertThat(result.nul()).isTrue(); + } } \ No newline at end of file