Skip to content

Commit be0483f

Browse files
committed
The builder for EnhancedDocument should not rely on the order in which attribute converters are added to it
1 parent a1f7443 commit be0483f

File tree

8 files changed

+669
-61
lines changed

8 files changed

+669
-61
lines changed

services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/document/DefaultEnhancedDocument.java

+31-20
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,12 @@ private DefaultEnhancedDocument(Map<String, AttributeValue> attributeValueMap) {
7171
}
7272

7373
public DefaultEnhancedDocument(DefaultBuilder builder) {
74-
attributeValueMap = Collections.unmodifiableMap(builder.getAttributeValueMap());
75-
attributeConverterProviders = ChainConverterProvider.create(builder.attributeConverterProviders);
74+
attributeConverterProviders =
75+
ChainConverterProvider.create(builder.attributeConverterProviders != null
76+
? builder.attributeConverterProviders
77+
: Collections.singletonList(AttributeConverterProvider.defaultProvider()));
78+
attributeValueMap = Collections.unmodifiableMap(objectMapToAttributeMap(builder.attributeValueMap,
79+
attributeConverterProviders));
7680
}
7781

7882
public static DefaultBuilder builder() {
@@ -339,31 +343,19 @@ public String toJsonPretty() {
339343

340344
public static class DefaultBuilder implements EnhancedDocument.Builder {
341345

342-
Map<String, AttributeValue> attributeValueMap = new LinkedHashMap<>();
346+
Map<String, Object> attributeValueMap = new LinkedHashMap<>();
343347

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

346-
public DefaultBuilder() {
347-
}
348-
349-
public Map<String, AttributeValue> getAttributeValueMap() {
350-
return attributeValueMap;
350+
private DefaultBuilder() {
351351
}
352352

353353
@Override
354354
public Builder add(String attributeName, Object value) {
355-
ChainConverterProvider attributeConverterProvider = providerFromBuildAndAppendDefault();
356-
attributeValueMap.put(attributeName, convert(value, attributeConverterProvider));
355+
attributeValueMap.put(attributeName, value);
357356
return this;
358357
}
359358

360-
private ChainConverterProvider providerFromBuildAndAppendDefault() {
361-
List<AttributeConverterProvider> converterProviders = new ArrayList<>(attributeConverterProviders);
362-
converterProviders.add(DefaultAttributeConverterProvider.create());
363-
ChainConverterProvider attributeConverterProvider = ChainConverterProvider.create(converterProviders);
364-
return attributeConverterProvider;
365-
}
366-
367359
@Override
368360
public Builder addString(String attributeName, String value) {
369361
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
@@ -440,7 +432,7 @@ public Builder addSdkBytesSet(String attributeName, Set<SdkBytes> values) {
440432
public Builder addList(String attributeName, List<?> value) {
441433
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
442434
if (!isNullValueAdded(attributeName, value)) {
443-
attributeValueMap.put(attributeName, convert(value, providerFromBuildAndAppendDefault()));
435+
attributeValueMap.put(attributeName, value);
444436
}
445437
return this;
446438
}
@@ -449,7 +441,7 @@ public Builder addList(String attributeName, List<?> value) {
449441
public Builder addMap(String attributeName, Map<String, ?> value) {
450442
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
451443
if (!isNullValueAdded(attributeName, value)) {
452-
attributeValueMap.put(attributeName, convert(value, providerFromBuildAndAppendDefault()));
444+
attributeValueMap.put(attributeName, value);
453445
}
454446
return this;
455447
}
@@ -507,7 +499,9 @@ public Builder json(String json) {
507499
throw new IllegalArgumentException("Could not parse argument json " + json);
508500
}
509501
AttributeValue attributeValue = JSON_ITEM_ATTRIBUTE_CONVERTER.transformFrom(jsonNode);
510-
this.attributeValueMap = attributeValue.m();
502+
if (attributeValue != null && attributeValue.hasM()) {
503+
attributeValueMap = new LinkedHashMap<>(attributeValue.m());
504+
}
511505
return this;
512506
}
513507

@@ -550,4 +544,21 @@ public int hashCode() {
550544
result = 31 * result + (attributeConverterProviders != null ? attributeConverterProviders.hashCode() : 0);
551545
return result;
552546
}
547+
548+
private static Map<String, AttributeValue> objectMapToAttributeMap(Map<String, Object> objectMap,
549+
AttributeConverterProvider attributeConverterProvider) {
550+
if (objectMap == null) {
551+
return null;
552+
}
553+
Map<String, AttributeValue> result = new LinkedHashMap<>(objectMap.size());
554+
objectMap.forEach((key, value) -> {
555+
if (value instanceof AttributeValue) {
556+
result.put(key, (AttributeValue) value);
557+
} else {
558+
result.put(key, convert(value, attributeConverterProvider));
559+
}
560+
});
561+
return result;
562+
}
563+
553564
}

services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/document/DocumentUtils.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,12 @@ public static AttributeValue convert(Object sourceObject, AttributeConverterProv
146146
return convertMapToAttributeValue((Map<?, ?>) sourceObject, attributeConverterProvider);
147147
}
148148
AttributeConverter attributeConverter = attributeConverterProvider.converterFor(EnhancedType.of(sourceObject.getClass()));
149+
if (attributeConverter == null) {
150+
throw new IllegalStateException("Converter not found for Class " + sourceObject.getClass().getSimpleName());
151+
}
149152
return attributeConverter.transformFrom(sourceObject);
150153
}
151154

152-
153155
/**
154156
* Coverts AttributeValue to simple java objects like String, SdkNumber, Boolean, List, Set, SdkBytes or Maps.
155157
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.enhanced.dynamodb.converters.document;
17+
18+
import java.util.Map;
19+
import software.amazon.awssdk.enhanced.dynamodb.AttributeConverter;
20+
import software.amazon.awssdk.enhanced.dynamodb.AttributeConverterProvider;
21+
import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType;
22+
import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
23+
import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.EnhancedAttributeValue;
24+
import software.amazon.awssdk.enhanced.dynamodb.internal.converter.string.IntegerStringConverter;
25+
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
26+
import software.amazon.awssdk.utils.ImmutableMap;
27+
28+
public class CustomAttributeForDocumentConverterProvider implements AttributeConverterProvider {
29+
private final Map<EnhancedType<?>, AttributeConverter<?>> converterCache = ImmutableMap.of(
30+
EnhancedType.of(CustomClassForDocumentAPI.class), new CustomClassForDocumentAttributeConverter(),
31+
EnhancedType.of(Integer.class), new CustomIntegerAttributeConverter()
32+
);
33+
34+
@SuppressWarnings("unchecked")
35+
@Override
36+
public <T> AttributeConverter<T> converterFor(EnhancedType<T> enhancedType) {
37+
return (AttributeConverter<T>) converterCache.get(enhancedType);
38+
}
39+
40+
public static CustomAttributeForDocumentConverterProvider create(){
41+
return new CustomAttributeForDocumentConverterProvider();
42+
}
43+
44+
45+
private static class CustomStringAttributeConverter implements AttributeConverter<String> {
46+
47+
final static String DEFAULT_SUFFIX = "-custom";
48+
49+
@Override
50+
public AttributeValue transformFrom(String input) {
51+
return EnhancedAttributeValue.fromString(input + DEFAULT_SUFFIX).toAttributeValue();
52+
}
53+
54+
@Override
55+
public String transformTo(AttributeValue input) {
56+
return input.s();
57+
}
58+
59+
@Override
60+
public EnhancedType<String> type() {
61+
return EnhancedType.of(String.class);
62+
}
63+
64+
@Override
65+
public AttributeValueType attributeValueType() {
66+
return AttributeValueType.S;
67+
}
68+
}
69+
70+
private static class CustomIntegerAttributeCo implements AttributeConverter<Integer> {
71+
72+
final static Integer DEFAULT_INCREMENT = 10;
73+
74+
@Override
75+
public AttributeValue transformFrom(Integer input) {
76+
return EnhancedAttributeValue.fromNumber(IntegerStringConverter.create().toString(input + DEFAULT_INCREMENT))
77+
.toAttributeValue();
78+
}
79+
80+
@Override
81+
public Integer transformTo(AttributeValue input) {
82+
return Integer.valueOf(input.n());
83+
}
84+
85+
@Override
86+
public EnhancedType<Integer> type() {
87+
return EnhancedType.of(Integer.class);
88+
}
89+
90+
@Override
91+
public AttributeValueType attributeValueType() {
92+
return AttributeValueType.N;
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)