Skip to content

Commit

Permalink
The builder for EnhancedDocument should not rely on the order in whic…
Browse files Browse the repository at this point in the history
…h attribute converters are added to it
  • Loading branch information
joviegas committed Feb 21, 2023
1 parent a1f7443 commit 2731251
Show file tree
Hide file tree
Showing 8 changed files with 671 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,16 @@ private DefaultEnhancedDocument(Map<String, AttributeValue> attributeValueMap) {
}

public DefaultEnhancedDocument(DefaultBuilder builder) {
attributeValueMap = Collections.unmodifiableMap(builder.getAttributeValueMap());
attributeConverterProviders = ChainConverterProvider.create(builder.attributeConverterProviders);
List<AttributeConverterProvider> providers = builder.attributeConverterProviders;
attributeConverterProviders =
ChainConverterProvider.create(providers != null && !providers.isEmpty()
? providers
: Collections.singletonList(AttributeConverterProvider.defaultProvider()));
attributeValueMap = Collections.unmodifiableMap(objectMapToAttributeMap(builder.attributeValueMap,
attributeConverterProviders));
}


public static DefaultBuilder builder() {
return new DefaultBuilder();

Expand Down Expand Up @@ -339,31 +345,19 @@ public String toJsonPretty() {

public static class DefaultBuilder implements EnhancedDocument.Builder {

Map<String, AttributeValue> attributeValueMap = new LinkedHashMap<>();
Map<String, Object> attributeValueMap = new LinkedHashMap<>();

List<AttributeConverterProvider> attributeConverterProviders = new ArrayList<>();

public DefaultBuilder() {
}

public Map<String, AttributeValue> getAttributeValueMap() {
return attributeValueMap;
private DefaultBuilder() {
}

@Override
public Builder add(String attributeName, Object value) {
ChainConverterProvider attributeConverterProvider = providerFromBuildAndAppendDefault();
attributeValueMap.put(attributeName, convert(value, attributeConverterProvider));
attributeValueMap.put(attributeName, value);
return this;
}

private ChainConverterProvider providerFromBuildAndAppendDefault() {
List<AttributeConverterProvider> converterProviders = new ArrayList<>(attributeConverterProviders);
converterProviders.add(DefaultAttributeConverterProvider.create());
ChainConverterProvider attributeConverterProvider = ChainConverterProvider.create(converterProviders);
return attributeConverterProvider;
}

@Override
public Builder addString(String attributeName, String value) {
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
Expand Down Expand Up @@ -440,7 +434,7 @@ public Builder addSdkBytesSet(String attributeName, Set<SdkBytes> values) {
public Builder addList(String attributeName, List<?> value) {
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
if (!isNullValueAdded(attributeName, value)) {
attributeValueMap.put(attributeName, convert(value, providerFromBuildAndAppendDefault()));
attributeValueMap.put(attributeName, value);
}
return this;
}
Expand All @@ -449,7 +443,7 @@ public Builder addList(String attributeName, List<?> value) {
public Builder addMap(String attributeName, Map<String, ?> value) {
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
if (!isNullValueAdded(attributeName, value)) {
attributeValueMap.put(attributeName, convert(value, providerFromBuildAndAppendDefault()));
attributeValueMap.put(attributeName, value);
}
return this;
}
Expand Down Expand Up @@ -507,7 +501,9 @@ public Builder json(String json) {
throw new IllegalArgumentException("Could not parse argument json " + json);
}
AttributeValue attributeValue = JSON_ITEM_ATTRIBUTE_CONVERTER.transformFrom(jsonNode);
this.attributeValueMap = attributeValue.m();
if (attributeValue != null && attributeValue.hasM()) {
attributeValueMap = new LinkedHashMap<>(attributeValue.m());
}
return this;
}

Expand Down Expand Up @@ -550,4 +546,21 @@ public int hashCode() {
result = 31 * result + (attributeConverterProviders != null ? attributeConverterProviders.hashCode() : 0);
return result;
}

private static Map<String, AttributeValue> objectMapToAttributeMap(Map<String, Object> objectMap,
AttributeConverterProvider attributeConverterProvider) {
if (objectMap == null) {
return null;
}
Map<String, AttributeValue> result = new LinkedHashMap<>(objectMap.size());
objectMap.forEach((key, value) -> {
if (value instanceof AttributeValue) {
result.put(key, (AttributeValue) value);
} else {
result.put(key, convert(value, attributeConverterProvider));
}
});
return result;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,12 @@ public static AttributeValue convert(Object sourceObject, AttributeConverterProv
return convertMapToAttributeValue((Map<?, ?>) sourceObject, attributeConverterProvider);
}
AttributeConverter attributeConverter = attributeConverterProvider.converterFor(EnhancedType.of(sourceObject.getClass()));
if (attributeConverter == null) {
throw new IllegalStateException("Converter not found for Class " + sourceObject.getClass().getSimpleName());
}
return attributeConverter.transformFrom(sourceObject);
}


/**
* Coverts AttributeValue to simple java objects like String, SdkNumber, Boolean, List, Set, SdkBytes or Maps.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* 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.document;

import java.util.Map;
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.converter.attribute.EnhancedAttributeValue;
import software.amazon.awssdk.enhanced.dynamodb.internal.converter.string.IntegerStringConverter;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.utils.ImmutableMap;

public class CustomAttributeForDocumentConverterProvider implements AttributeConverterProvider {
private final Map<EnhancedType<?>, AttributeConverter<?>> converterCache = ImmutableMap.of(
EnhancedType.of(CustomClassForDocumentAPI.class), new CustomClassForDocumentAttributeConverter(),
EnhancedType.of(Integer.class), new CustomIntegerAttributeConverter()
);

@SuppressWarnings("unchecked")
@Override
public <T> AttributeConverter<T> converterFor(EnhancedType<T> enhancedType) {
return (AttributeConverter<T>) converterCache.get(enhancedType);
}

public static CustomAttributeForDocumentConverterProvider create(){
return new CustomAttributeForDocumentConverterProvider();
}


private static class CustomStringAttributeConverter implements AttributeConverter<String> {

final static String DEFAULT_SUFFIX = "-custom";

@Override
public AttributeValue transformFrom(String input) {
return EnhancedAttributeValue.fromString(input + DEFAULT_SUFFIX).toAttributeValue();
}

@Override
public String transformTo(AttributeValue input) {
return input.s();
}

@Override
public EnhancedType<String> type() {
return EnhancedType.of(String.class);
}

@Override
public AttributeValueType attributeValueType() {
return AttributeValueType.S;
}
}

private static class CustomIntegerAttributeCo implements AttributeConverter<Integer> {

final static Integer DEFAULT_INCREMENT = 10;

@Override
public AttributeValue transformFrom(Integer input) {
return EnhancedAttributeValue.fromNumber(IntegerStringConverter.create().toString(input + DEFAULT_INCREMENT))
.toAttributeValue();
}

@Override
public Integer transformTo(AttributeValue input) {
return Integer.valueOf(input.n());
}

@Override
public EnhancedType<Integer> type() {
return EnhancedType.of(Integer.class);
}

@Override
public AttributeValueType attributeValueType() {
return AttributeValueType.N;
}
}
}
Loading

0 comments on commit 2731251

Please sign in to comment.