Skip to content

Commit 764fc02

Browse files
authored
DocumentTableSchema Implementation (#3758)
* DocumentTableSchema Implementation * Handle review comments -1 * Handle review comments 2
1 parent 27d1948 commit 764fc02

File tree

7 files changed

+611
-54
lines changed

7 files changed

+611
-54
lines changed

services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/DefaultAttributeConverterProvider.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@
8888
@ThreadSafe
8989
@Immutable
9090
public final class DefaultAttributeConverterProvider implements AttributeConverterProvider {
91+
private static DefaultAttributeConverterProvider INSTANCE = getDefaultBuilder().build();
92+
9193
private static final Logger log = Logger.loggerFor(DefaultAttributeConverterProvider.class);
9294

9395
private final ConcurrentHashMap<EnhancedType<?>, AttributeConverter<?>> converterCache =
@@ -117,10 +119,9 @@ public DefaultAttributeConverterProvider() {
117119
* Returns an attribute converter provider with all default converters set.
118120
*/
119121
public static DefaultAttributeConverterProvider create() {
120-
return getDefaultBuilder().build();
122+
return INSTANCE;
121123
}
122124

123-
124125
/**
125126
* Equivalent to {@code builder(EnhancedType.of(Object.class))}.
126127
*/

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

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import software.amazon.awssdk.enhanced.dynamodb.DefaultAttributeConverterProvider;
2828
import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
2929
import software.amazon.awssdk.enhanced.dynamodb.internal.document.DefaultEnhancedDocument;
30+
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
3031

3132
/**
3233
* Interface representing Document API for DynamoDB. Document API operations are used to carry open content i.e. data with no
@@ -39,9 +40,6 @@
3940
@SdkPublicApi
4041
public interface EnhancedDocument {
4142

42-
DefaultAttributeConverterProvider defaultProvider = DefaultAttributeConverterProvider.create();
43-
44-
4543
/**
4644
* Convenience factory method - instantiates an <code>EnhancedDocument</code> from the given JSON String.
4745
*
@@ -54,7 +52,22 @@ static EnhancedDocument fromJson(String json) {
5452
}
5553
return DefaultEnhancedDocument.builder()
5654
.json(json)
57-
.addAttributeConverterProvider(defaultProvider)
55+
.attributeConverterProviders(DefaultAttributeConverterProvider.create())
56+
.build();
57+
}
58+
59+
/**
60+
* Convenience factory method - instantiates an <code>EnhancedDocument</code> from the given AttributeValueMap.
61+
* @param attributeValueMap - Map with Attributes as String keys and AttributeValue as Value.
62+
* @return A new instance of EnhancedDocument.
63+
*/
64+
static EnhancedDocument fromAttributeValueMap(Map<String, AttributeValue> attributeValueMap) {
65+
if (attributeValueMap == null) {
66+
return null;
67+
}
68+
return DefaultEnhancedDocument.builder()
69+
.attributeValueMap(attributeValueMap)
70+
.attributeConverterProviders(DefaultAttributeConverterProvider.create())
5871
.build();
5972
}
6073

@@ -70,7 +83,7 @@ static EnhancedDocument fromMap(Map<String, Object> attributes) {
7083
}
7184
DefaultEnhancedDocument.DefaultBuilder defaultBuilder = DefaultEnhancedDocument.builder();
7285
attributes.entrySet().forEach(key -> defaultBuilder.add(key.getKey(), key.getValue()));
73-
return defaultBuilder.addAttributeConverterProvider(defaultProvider)
86+
return defaultBuilder.addAttributeConverterProvider(DefaultAttributeConverterProvider.create())
7487
.build();
7588
}
7689

@@ -343,6 +356,12 @@ <T extends Number> Map<String, T> getMapOfNumbers(String attributeName,
343356
*/
344357
String toJsonPretty();
345358

359+
/**
360+
* Gets the current EnhancedDocument as a <String, AttributeValue> Map.
361+
* @return EnhancedDocument as a Map with Keys as String attributes and Values as AttributeValue.
362+
*/
363+
Map<String, AttributeValue> toAttributeValueMap();
364+
346365
@NotThreadSafe
347366
interface Builder {
348367
/**

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

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
import software.amazon.awssdk.protocols.jsoncore.JsonNode;
4848
import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser;
4949
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
50+
import software.amazon.awssdk.utils.StringUtils;
51+
import software.amazon.awssdk.utils.Validate;
5052

5153
/**
5254
* Default implementation of {@link EnhancedDocument}. This class is used by SDK to create Enhanced Documents.
@@ -57,8 +59,6 @@
5759
@SdkInternalApi
5860
public class DefaultEnhancedDocument implements EnhancedDocument {
5961

60-
private static final DefaultAttributeConverterProvider DEFAULT_PROVIDER = DefaultAttributeConverterProvider.create();
61-
6262
private static final JsonItemAttributeConverter JSON_ITEM_ATTRIBUTE_CONVERTER = JsonItemAttributeConverter.create();
6363

6464
private final Map<String, AttributeValue> attributeValueMap;
@@ -67,7 +67,7 @@ public class DefaultEnhancedDocument implements EnhancedDocument {
6767

6868
private DefaultEnhancedDocument(Map<String, AttributeValue> attributeValueMap) {
6969
this.attributeValueMap = attributeValueMap;
70-
this.attributeConverterProviders = ChainConverterProvider.create(DEFAULT_PROVIDER);
70+
this.attributeConverterProviders = ChainConverterProvider.create(DefaultAttributeConverterProvider.create());
7171
}
7272

7373
public DefaultEnhancedDocument(DefaultBuilder builder) {
@@ -89,7 +89,8 @@ public Builder toBuilder() {
8989

9090
}
9191

92-
public Map<String, AttributeValue> getAttributeValueMap() {
92+
@Override
93+
public Map<String, AttributeValue> toAttributeValueMap() {
9394
return attributeValueMap;
9495
}
9596

@@ -358,93 +359,120 @@ public Builder add(String attributeName, Object value) {
358359

359360
private ChainConverterProvider providerFromBuildAndAppendDefault() {
360361
List<AttributeConverterProvider> converterProviders = new ArrayList<>(attributeConverterProviders);
361-
converterProviders.add(DEFAULT_PROVIDER);
362+
converterProviders.add(DefaultAttributeConverterProvider.create());
362363
ChainConverterProvider attributeConverterProvider = ChainConverterProvider.create(converterProviders);
363364
return attributeConverterProvider;
364365
}
365366

366367
@Override
367368
public Builder addString(String attributeName, String value) {
368-
attributeValueMap.put(attributeName, AttributeValue.fromS(value));
369+
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
370+
if (!isNullValueAdded(attributeName, value)) {
371+
attributeValueMap.put(attributeName, AttributeValue.fromS(value));
372+
}
369373
return this;
370374
}
371375

372376
@Override
373377
public Builder addNumber(String attributeName, Number value) {
374-
attributeValueMap.put(attributeName, AttributeValue.fromN(value != null ? String.valueOf(value) : null));
378+
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
379+
if (!isNullValueAdded(attributeName, value)) {
380+
attributeValueMap.put(attributeName, AttributeValue.fromN(String.valueOf(value)));
381+
}
375382
return this;
376383
}
377384

378385
@Override
379386
public Builder addSdkBytes(String attributeName, SdkBytes value) {
380-
attributeValueMap.put(attributeName, AttributeValue.fromB(value));
387+
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
388+
if (!isNullValueAdded(attributeName, value)) {
389+
attributeValueMap.put(attributeName, AttributeValue.fromB(value));
390+
}
381391
return this;
382392
}
383393

384394
@Override
385395
public Builder addBoolean(String attributeName, boolean value) {
386-
attributeValueMap.put(attributeName, AttributeValue.fromBool(value));
396+
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
397+
if (!isNullValueAdded(attributeName, value)) {
398+
attributeValueMap.put(attributeName, AttributeValue.fromBool(value));
399+
}
387400
return this;
388401
}
389402

390403
@Override
391404
public Builder addNull(String attributeName) {
405+
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
392406
attributeValueMap.put(attributeName, NULL_ATTRIBUTE_VALUE);
393407
return this;
394408
}
395409

396410
@Override
397411
public Builder addStringSet(String attributeName, Set<String> values) {
398-
attributeValueMap.put(attributeName, AttributeValue.fromSs(values.stream().collect(Collectors.toList())));
412+
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
413+
if (!isNullValueAdded(attributeName, values)) {
414+
attributeValueMap.put(attributeName, AttributeValue.fromSs(values.stream().collect(Collectors.toList())));
415+
}
399416
return this;
400417
}
401418

402419
@Override
403420
public Builder addNumberSet(String attributeName, Set<Number> values) {
404-
List<String> collect = values.stream().map(value -> value.toString()).collect(Collectors.toList());
405-
attributeValueMap.put(attributeName, AttributeValue.fromNs(collect));
421+
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
422+
if (!isNullValueAdded(attributeName, values)) {
423+
List<String> collect = values.stream().map(value -> value.toString()).collect(Collectors.toList());
424+
attributeValueMap.put(attributeName, AttributeValue.fromNs(collect));
425+
426+
}
406427
return this;
407428
}
408429

409430
@Override
410431
public Builder addSdkBytesSet(String attributeName, Set<SdkBytes> values) {
411-
attributeValueMap.put(attributeName, AttributeValue.fromBs(values.stream().collect(Collectors.toList())));
432+
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
433+
if (!isNullValueAdded(attributeName, values)) {
434+
attributeValueMap.put(attributeName, AttributeValue.fromBs(values.stream().collect(Collectors.toList())));
435+
}
412436
return this;
413437
}
414438

415439
@Override
416440
public Builder addList(String attributeName, List<?> value) {
417-
attributeValueMap.put(attributeName, convert(value, providerFromBuildAndAppendDefault()));
441+
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
442+
if (!isNullValueAdded(attributeName, value)) {
443+
attributeValueMap.put(attributeName, convert(value, providerFromBuildAndAppendDefault()));
444+
}
418445
return this;
419446
}
420447

421448
@Override
422449
public Builder addMap(String attributeName, Map<String, ?> value) {
423-
attributeValueMap.put(attributeName, convert(value, providerFromBuildAndAppendDefault()));
450+
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
451+
if (!isNullValueAdded(attributeName, value)) {
452+
attributeValueMap.put(attributeName, convert(value, providerFromBuildAndAppendDefault()));
453+
}
424454
return this;
425455
}
426456

427457
@Override
428458
public Builder addJson(String attributeName, String json) {
429-
JsonItemAttributeConverter jsonItemAttributeConverter = JsonItemAttributeConverter.create();
430-
JsonNodeParser build = JsonNodeParser.builder().build();
431-
JsonNode jsonNode = build.parse(json);
432-
AttributeValue attributeValue = jsonItemAttributeConverter.transformFrom(jsonNode);
433-
attributeValueMap.put(attributeName, attributeValue);
459+
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
460+
if (!isNullValueAdded(attributeName, json)) {
461+
JsonItemAttributeConverter jsonItemAttributeConverter = JsonItemAttributeConverter.create();
462+
JsonNodeParser build = JsonNodeParser.builder().build();
463+
JsonNode jsonNode = build.parse(json);
464+
AttributeValue attributeValue = jsonItemAttributeConverter.transformFrom(jsonNode);
465+
attributeValueMap.put(attributeName, attributeValue);
466+
}
434467
return this;
435468
}
436469

437470
@Override
438471
public Builder addEnhancedDocument(String attributeName, EnhancedDocument enhancedDocument) {
439-
if (enhancedDocument == null) {
440-
attributeValueMap.put(attributeName, NULL_ATTRIBUTE_VALUE);
441-
return this;
472+
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
473+
if (!isNullValueAdded(attributeName, enhancedDocument)) {
474+
attributeValueMap.put(attributeName, AttributeValue.fromM(enhancedDocument.toAttributeValueMap()));
442475
}
443-
DefaultEnhancedDocument defaultEnhancedDocument =
444-
enhancedDocument instanceof DefaultEnhancedDocument
445-
? (DefaultEnhancedDocument) enhancedDocument
446-
: (DefaultEnhancedDocument) enhancedDocument.toBuilder().json(enhancedDocument.toJson()).build();
447-
attributeValueMap.put(attributeName, AttributeValue.fromM(defaultEnhancedDocument.attributeValueMap));
448476
return this;
449477
}
450478

@@ -492,6 +520,14 @@ public DefaultBuilder attributeValueMap(Map<String, AttributeValue> attributeVal
492520
this.attributeValueMap = attributeValueMap != null ? new LinkedHashMap<>(attributeValueMap) : null;
493521
return this;
494522
}
523+
524+
private boolean isNullValueAdded(String attributeName, Object value) {
525+
if (value == null) {
526+
addNull(attributeName);
527+
return true;
528+
}
529+
return false;
530+
}
495531
}
496532

497533
@Override

0 commit comments

Comments
 (0)