Skip to content

Commit d0c22e8

Browse files
committed
Fixed incorrect 'duplicate key' error triggered when flattening a TableSchema that has key tags and more than one attribute
1 parent d4c036d commit d0c22e8

File tree

3 files changed

+203
-1
lines changed

3 files changed

+203
-1
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "bugfix",
3+
"category": "AWS DynamoDB Enhanced Client",
4+
"description": "Fixed incorrect 'duplicate key' error triggered when flattening a TableSchema that has key tags and more than one attribute."
5+
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,10 @@ private StaticImmutableTableSchema(Builder<T, B> builder) {
188188

189189
mutableAttributeNames.add(attributeName);
190190
mutableFlattenedMappers.put(attributeName, flattenedMapper);
191-
tableMetadataBuilder.mergeWith(flattenedMapper.getOtherItemTableSchema().tableMetadata());
192191
}
193192
);
193+
194+
tableMetadataBuilder.mergeWith(flattenedMapper.getOtherItemTableSchema().tableMetadata());
194195
}
195196
);
196197

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
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.functionaltests;
17+
18+
import static org.hamcrest.MatcherAssert.assertThat;
19+
import static org.hamcrest.Matchers.is;
20+
import static software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTags.primaryPartitionKey;
21+
import static software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTags.primarySortKey;
22+
23+
import java.util.Objects;
24+
25+
import org.junit.After;
26+
import org.junit.Before;
27+
import org.junit.Test;
28+
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
29+
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
30+
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
31+
import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema;
32+
import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest;
33+
34+
public class FlattenWithTagsTest extends LocalDynamoDbSyncTestBase {
35+
private static class Record {
36+
private String id;
37+
private Document document;
38+
39+
private String getId() {
40+
return id;
41+
}
42+
43+
private Record setId(String id) {
44+
this.id = id;
45+
return this;
46+
}
47+
48+
private Document getDocument() {
49+
return document;
50+
}
51+
52+
private Record setDocument(Document document) {
53+
this.document = document;
54+
return this;
55+
}
56+
57+
@Override
58+
public boolean equals(Object o) {
59+
if (this == o) return true;
60+
if (o == null || getClass() != o.getClass()) return false;
61+
Record record = (Record) o;
62+
return Objects.equals(id, record.id) &&
63+
Objects.equals(document, record.document);
64+
}
65+
66+
@Override
67+
public int hashCode() {
68+
return Objects.hash(id, document);
69+
}
70+
}
71+
72+
private static class Document {
73+
private String documentAttribute1;
74+
private String documentAttribute2;
75+
private String documentAttribute3;
76+
77+
private String getDocumentAttribute1() {
78+
return documentAttribute1;
79+
}
80+
81+
private Document setDocumentAttribute1(String documentAttribute1) {
82+
this.documentAttribute1 = documentAttribute1;
83+
return this;
84+
}
85+
86+
private String getDocumentAttribute2() {
87+
return documentAttribute2;
88+
}
89+
90+
private Document setDocumentAttribute2(String documentAttribute2) {
91+
this.documentAttribute2 = documentAttribute2;
92+
return this;
93+
}
94+
95+
private String getDocumentAttribute3() {
96+
return documentAttribute3;
97+
}
98+
99+
private Document setDocumentAttribute3(String documentAttribute3) {
100+
this.documentAttribute3 = documentAttribute3;
101+
return this;
102+
}
103+
104+
@Override
105+
public boolean equals(Object o) {
106+
if (this == o) return true;
107+
if (o == null || getClass() != o.getClass()) return false;
108+
Document document = (Document) o;
109+
return Objects.equals(documentAttribute1, document.documentAttribute1) &&
110+
Objects.equals(documentAttribute2, document.documentAttribute2) &&
111+
Objects.equals(documentAttribute3, document.documentAttribute3);
112+
}
113+
114+
@Override
115+
public int hashCode() {
116+
return Objects.hash(documentAttribute1, documentAttribute2, documentAttribute3);
117+
}
118+
}
119+
120+
private static final StaticTableSchema<Document> DOCUMENT_SCHEMA =
121+
StaticTableSchema.builder(Document.class)
122+
.newItemSupplier(Document::new)
123+
.addAttribute(String.class, a -> a.name("documentAttribute1")
124+
.getter(Document::getDocumentAttribute1)
125+
.setter(Document::setDocumentAttribute1)
126+
.addTag(primarySortKey()))
127+
.addAttribute(String.class, a -> a.name("documentAttribute2")
128+
.getter(Document::getDocumentAttribute2)
129+
.setter(Document::setDocumentAttribute2))
130+
.addAttribute(String.class, a -> a.name("documentAttribute3")
131+
.getter(Document::getDocumentAttribute3)
132+
.setter(Document::setDocumentAttribute3))
133+
.build();
134+
135+
private static final TableSchema<Record> TABLE_SCHEMA =
136+
StaticTableSchema.builder(Record.class)
137+
.newItemSupplier(Record::new)
138+
.addAttribute(String.class, a -> a.name("id")
139+
.getter(Record::getId)
140+
.setter(Record::setId)
141+
.tags(primaryPartitionKey()))
142+
.flatten(DOCUMENT_SCHEMA, Record::getDocument, Record::setDocument)
143+
.build();
144+
145+
146+
private DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
147+
.dynamoDbClient(getDynamoDbClient())
148+
.build();
149+
150+
private DynamoDbTable<Record> mappedTable = enhancedClient.table(getConcreteTableName("table-name"), TABLE_SCHEMA);
151+
152+
@Before
153+
public void createTable() {
154+
mappedTable.createTable(r -> r.provisionedThroughput(getDefaultProvisionedThroughput()));
155+
}
156+
157+
@After
158+
public void deleteTable() {
159+
getDynamoDbClient().deleteTable(DeleteTableRequest.builder()
160+
.tableName(getConcreteTableName("table-name"))
161+
.build());
162+
}
163+
164+
@Test
165+
public void update_allValues() {
166+
Document document = new Document()
167+
.setDocumentAttribute1("one")
168+
.setDocumentAttribute2("two")
169+
.setDocumentAttribute3("three");
170+
Record record = new Record()
171+
.setId("id-value")
172+
.setDocument(document);
173+
174+
Record updatedRecord = mappedTable.updateItem(r -> r.item(record));
175+
Record fetchedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id-value").sortValue("one")));
176+
177+
assertThat(updatedRecord, is(record));
178+
assertThat(fetchedRecord, is(record));
179+
}
180+
181+
@Test
182+
public void update_someValues() {
183+
Document document = new Document()
184+
.setDocumentAttribute1("one")
185+
.setDocumentAttribute2("two");
186+
Record record = new Record()
187+
.setId("id-value")
188+
.setDocument(document);
189+
190+
Record updatedRecord = mappedTable.updateItem(r -> r.item(record));
191+
Record fetchedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id-value").sortValue("one")));
192+
193+
assertThat(updatedRecord, is(record));
194+
assertThat(fetchedRecord, is(record));
195+
}
196+
}

0 commit comments

Comments
 (0)