Skip to content

Commit 51eb1e8

Browse files
committed
Fixed DynamoDbEnhancedClient TableSchema::itemToMap to handle flattened members when ignoreNulls is false
1 parent d506c23 commit 51eb1e8

File tree

6 files changed

+229
-2
lines changed

6 files changed

+229
-2
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "bugfix",
3+
"category": "Amazon DynamoDB Enhanced Client",
4+
"contributor": "",
5+
"description": "Fixed DynamoDbEnhancedClient TableSchema::itemToMap to return a map that contains a consistent representation of null top-level (non-flattened) attributes and flattened attributes when their enclosing member is null and ignoreNulls is set to false."
6+
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ private B mapToItem(B thisBuilder,
126126
private Map<String, AttributeValue> itemToMap(T item, boolean ignoreNulls) {
127127
T1 otherItem = this.otherItemGetter.apply(item);
128128

129-
if (otherItem == null) {
129+
if (otherItem == null && ignoreNulls) {
130130
return Collections.emptyMap();
131131
}
132132

@@ -515,7 +515,9 @@ public Map<String, AttributeValue> itemToMap(T item, boolean ignoreNulls) {
515515

516516
attributeMappers.forEach(attributeMapper -> {
517517
String attributeKey = attributeMapper.attributeName();
518-
AttributeValue attributeValue = attributeMapper.attributeGetterMethod().apply(item);
518+
AttributeValue attributeValue = item == null ?
519+
AttributeValue.fromNul(true) :
520+
attributeMapper.attributeGetterMethod().apply(item);
519521

520522
if (!ignoreNulls || !isNullAttributeValue(attributeValue)) {
521523
attributeValueMap.put(attributeKey, attributeValue);

services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/mapper/BeanTableSchemaTest.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import software.amazon.awssdk.enhanced.dynamodb.internal.AttributeValues;
4646
import software.amazon.awssdk.enhanced.dynamodb.mapper.testbeans.AbstractBean;
4747
import software.amazon.awssdk.enhanced.dynamodb.mapper.testbeans.AbstractImmutable;
48+
import software.amazon.awssdk.enhanced.dynamodb.mapper.testbeans.AbstractNestedImmutable;
4849
import software.amazon.awssdk.enhanced.dynamodb.mapper.testbeans.AttributeConverterBean;
4950
import software.amazon.awssdk.enhanced.dynamodb.mapper.testbeans.AttributeConverterNoConstructorBean;
5051
import software.amazon.awssdk.enhanced.dynamodb.mapper.testbeans.CommonTypesBean;
@@ -55,6 +56,7 @@
5556
import software.amazon.awssdk.enhanced.dynamodb.mapper.testbeans.ExtendedBean;
5657
import software.amazon.awssdk.enhanced.dynamodb.mapper.testbeans.FlattenedBeanBean;
5758
import software.amazon.awssdk.enhanced.dynamodb.mapper.testbeans.FlattenedImmutableBean;
59+
import software.amazon.awssdk.enhanced.dynamodb.mapper.testbeans.FlattenedNestedImmutableBean;
5860
import software.amazon.awssdk.enhanced.dynamodb.mapper.testbeans.FluentSetterBean;
5961
import software.amazon.awssdk.enhanced.dynamodb.mapper.testbeans.IgnoredAttributeBean;
6062
import software.amazon.awssdk.enhanced.dynamodb.mapper.testbeans.InvalidBean;
@@ -257,6 +259,67 @@ public void dynamoDbFlatten_correctlyFlattensImmutableAttributes() {
257259
assertThat(itemMap, hasEntry("attribute2", stringValue("two")));
258260
}
259261

262+
@Test
263+
public void dynamoDbFlatten_correctlyFlattensNullImmutableAttributes() {
264+
BeanTableSchema<FlattenedImmutableBean> beanTableSchema = BeanTableSchema.create(FlattenedImmutableBean.class);
265+
AbstractImmutable abstractImmutable = AbstractImmutable.builder().build();
266+
FlattenedImmutableBean flattenedImmutableBean = new FlattenedImmutableBean();
267+
flattenedImmutableBean.setId("id-value");
268+
flattenedImmutableBean.setAbstractImmutable(abstractImmutable);
269+
270+
Map<String, AttributeValue> itemMap = beanTableSchema.itemToMap(flattenedImmutableBean, false);
271+
assertThat(itemMap.size(), is(3));
272+
assertThat(itemMap, hasEntry("id", stringValue("id-value")));
273+
assertThat(itemMap, hasEntry("attribute1", AttributeValue.fromNul(true)));
274+
assertThat(itemMap, hasEntry("attribute2", AttributeValue.fromNul(true)));
275+
}
276+
277+
@Test
278+
public void dynamoDbFlatten_correctlyFlattensNestedImmutableAttributes() {
279+
BeanTableSchema<FlattenedNestedImmutableBean> beanTableSchema =
280+
BeanTableSchema.create(FlattenedNestedImmutableBean.class);
281+
AbstractNestedImmutable abstractNestedImmutable2 =
282+
AbstractNestedImmutable.builder().attribute2("nested-two").build();
283+
AbstractNestedImmutable abstractNestedImmutable1 =
284+
AbstractNestedImmutable.builder().attribute2("two").abstractNestedImmutableOne(abstractNestedImmutable2).build();
285+
FlattenedNestedImmutableBean flattenedNestedImmutableBean = new FlattenedNestedImmutableBean();
286+
flattenedNestedImmutableBean.setId("id-value");
287+
flattenedNestedImmutableBean.setAttribute1("one");
288+
flattenedNestedImmutableBean.setAbstractNestedImmutable(abstractNestedImmutable1);
289+
290+
Map<String, AttributeValue> nestedAttributesMap = new HashMap<>();
291+
nestedAttributesMap.put("abstractNestedImmutableOne", AttributeValue.fromNul(true));
292+
nestedAttributesMap.put("attribute2", stringValue("nested-two"));
293+
294+
AttributeValue expectedNestedAttribute =
295+
AttributeValue.builder().m(Collections.unmodifiableMap(nestedAttributesMap)).build();
296+
297+
Map<String, AttributeValue> itemMap = beanTableSchema.itemToMap(flattenedNestedImmutableBean, false);
298+
assertThat(itemMap.size(), is(4));
299+
assertThat(itemMap, hasEntry("id", stringValue("id-value")));
300+
assertThat(itemMap, hasEntry("attribute1", stringValue("one")));
301+
assertThat(itemMap, hasEntry("attribute2", stringValue("two")));
302+
assertThat(itemMap, hasEntry("abstractNestedImmutableOne", expectedNestedAttribute));
303+
}
304+
305+
@Test
306+
public void dynamoDbFlatten_correctlyFlattensNullNestedImmutableAttributes() {
307+
BeanTableSchema<FlattenedNestedImmutableBean> beanTableSchema =
308+
BeanTableSchema.create(FlattenedNestedImmutableBean.class);
309+
AbstractNestedImmutable abstractNestedImmutable = AbstractNestedImmutable.builder().build();
310+
FlattenedNestedImmutableBean flattenedNestedImmutableBean = new FlattenedNestedImmutableBean();
311+
flattenedNestedImmutableBean.setId("id-value");
312+
flattenedNestedImmutableBean.setAttribute1("one");
313+
flattenedNestedImmutableBean.setAbstractNestedImmutable(abstractNestedImmutable);
314+
315+
Map<String, AttributeValue> itemMap = beanTableSchema.itemToMap(flattenedNestedImmutableBean, false);
316+
assertThat(itemMap.size(), is(4));
317+
assertThat(itemMap, hasEntry("id", stringValue("id-value")));
318+
assertThat(itemMap, hasEntry("attribute1", stringValue("one")));
319+
assertThat(itemMap, hasEntry("attribute2", AttributeValue.fromNul(true)));
320+
assertThat(itemMap, hasEntry("abstractNestedImmutableOne", AttributeValue.fromNul(true)));
321+
}
322+
260323
@Test
261324
public void documentBean_correctlyMapsBeanAttributes() {
262325
BeanTableSchema<DocumentBean> beanTableSchema = BeanTableSchema.create(DocumentBean.class);

services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticImmutableTableSchemaTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,6 +1388,24 @@ public void buildAbstractWithFlatten() {
13881388
is(singletonMap("documentString", AttributeValue.builder().s("test-string").build())));
13891389
}
13901390

1391+
@Test
1392+
public void buildAbstractWithFlattenAndIgnoreNullAsFalse() {
1393+
StaticTableSchema<FakeMappedItem> tableSchema =
1394+
StaticTableSchema.builder(FakeMappedItem.class)
1395+
.flatten(FAKE_DOCUMENT_TABLE_SCHEMA,
1396+
FakeMappedItem::getAFakeDocument,
1397+
FakeMappedItem::setAFakeDocument)
1398+
.build();
1399+
1400+
FakeDocument document = FakeDocument.of("test-string", null);
1401+
FakeMappedItem item = FakeMappedItem.builder().aFakeDocument(document).build();
1402+
1403+
Map<String, AttributeValue> attributeMapWithNulls = tableSchema.itemToMap(item, false);
1404+
assertThat(attributeMapWithNulls.size(), is(2));
1405+
assertThat(attributeMapWithNulls, hasEntry("documentString", AttributeValue.builder().s("test-string").build()));
1406+
assertThat(attributeMapWithNulls, hasEntry("documentInteger", AttributeValue.fromNul(true)));
1407+
}
1408+
13911409
@Test
13921410
public void buildAbstractExtends() {
13931411
StaticTableSchema<FakeAbstractSuperclass> superclassTableSchema =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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.mapper.testbeans;
17+
18+
import java.util.Objects;
19+
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbImmutable;
20+
21+
@DynamoDbImmutable(builder = AbstractNestedImmutable.Builder.class)
22+
public class AbstractNestedImmutable {
23+
private final String attribute2;
24+
private final AbstractNestedImmutable abstractNestedImmutableOne;
25+
26+
private AbstractNestedImmutable(Builder b) {
27+
this.attribute2 = b.attribute2;
28+
this.abstractNestedImmutableOne = b.abstractNestedImmutableOne;
29+
}
30+
31+
public String attribute2() {
32+
return attribute2;
33+
}
34+
35+
public AbstractNestedImmutable abstractNestedImmutableOne() {
36+
return abstractNestedImmutableOne;
37+
}
38+
39+
@Override
40+
public boolean equals(Object o) {
41+
if (this == o) {
42+
return true;
43+
}
44+
if (o == null || getClass() != o.getClass()) {
45+
return false;
46+
}
47+
48+
AbstractNestedImmutable that = (AbstractNestedImmutable) o;
49+
50+
if (!Objects.equals(attribute2, that.attribute2)) {
51+
return false;
52+
}
53+
return Objects.equals(abstractNestedImmutableOne, that.abstractNestedImmutableOne);
54+
}
55+
56+
@Override
57+
public int hashCode() {
58+
int result = attribute2 != null ? attribute2.hashCode() : 0;
59+
result = 31 * result + (abstractNestedImmutableOne != null ? abstractNestedImmutableOne.hashCode() : 0);
60+
return result;
61+
}
62+
63+
public static Builder builder() {
64+
return new Builder();
65+
}
66+
67+
public static final class Builder {
68+
private String attribute2;
69+
private AbstractNestedImmutable abstractNestedImmutableOne;
70+
71+
public Builder attribute2(String attribute2) {
72+
this.attribute2 = attribute2;
73+
return this;
74+
}
75+
76+
public Builder abstractNestedImmutableOne(AbstractNestedImmutable abstractNestedImmutableOne) {
77+
this.abstractNestedImmutableOne = abstractNestedImmutableOne;
78+
return this;
79+
}
80+
81+
public AbstractNestedImmutable build() {
82+
return new AbstractNestedImmutable(this);
83+
}
84+
}
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.mapper.testbeans;
17+
18+
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
19+
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbFlatten;
20+
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
21+
22+
@DynamoDbBean
23+
public class FlattenedNestedImmutableBean {
24+
private String id;
25+
private String attribute1;
26+
private AbstractNestedImmutable abstractNestedImmutable;
27+
28+
@DynamoDbPartitionKey
29+
public String getId() {
30+
return this.id;
31+
}
32+
33+
public void setId(String id) {
34+
this.id = id;
35+
}
36+
37+
public String getAttribute1() {
38+
return attribute1;
39+
}
40+
41+
public void setAttribute1(String attribute1) {
42+
this.attribute1 = attribute1;
43+
}
44+
45+
@DynamoDbFlatten
46+
public AbstractNestedImmutable getAbstractNestedImmutable() {
47+
return abstractNestedImmutable;
48+
}
49+
50+
public void setAbstractNestedImmutable(AbstractNestedImmutable abstractNestedImmutable) {
51+
this.abstractNestedImmutable = abstractNestedImmutable;
52+
}
53+
}

0 commit comments

Comments
 (0)