Skip to content

Include flattened mappers in converter resolution #3877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/next-release/bugfix-AmazonDynamoDB-965386f.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "bugfix",
"category": "Amazon DynamoDB Enhanced Client",
"contributor": "akiesler",
"description": "Include flattened mappers in attribute converter resolution"
}
Original file line number Diff line number Diff line change
Expand Up @@ -589,8 +589,15 @@ private B constructNewBuilder() {
@Override
public AttributeConverter<T> converterForAttribute(Object key) {
ResolvedImmutableAttribute<T, B> resolvedImmutableAttribute = indexedMappers.get(key);
return resolvedImmutableAttribute != null
? resolvedImmutableAttribute.attributeConverter()
: null;
if (resolvedImmutableAttribute != null) {
return resolvedImmutableAttribute.attributeConverter();
}

// If no resolvedAttribute is found look through flattened attributes
FlattenedMapper<T, B, ?> flattenedMapper = indexedFlattenedMappers.get(key);
if (flattenedMapper != null) {
return (AttributeConverter) flattenedMapper.getOtherItemTableSchema().converterForAttribute(key);
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ public class AutoGeneratedTimestampRecordTest extends LocalDynamoDbSyncTestBase
private static final String TABLE_NAME = "table-name";
private static final OperationContext PRIMARY_CONTEXT =
DefaultOperationContext.create(TABLE_NAME, TableMetadata.primaryIndexName());

private static final TableSchema<FlattenedRecord> FLATTENED_TABLE_SCHEMA =
StaticTableSchema.builder(FlattenedRecord.class)
.newItemSupplier(FlattenedRecord::new)
.addAttribute(Instant.class, a -> a.name("generated")
.getter(FlattenedRecord::getGenerated)
.setter(FlattenedRecord::setGenerated)
.tags(autoGeneratedTimestampAttribute()))
.build();

private static final TableSchema<Record> TABLE_SCHEMA =
StaticTableSchema.builder(Record.class)
.newItemSupplier(Record::new)
Expand Down Expand Up @@ -102,6 +112,7 @@ public class AutoGeneratedTimestampRecordTest extends LocalDynamoDbSyncTestBase
.setter(Record::setConvertedLastUpdatedDate)
.attributeConverter(TimeFormatUpdateTestConverter.create())
.tags(autoGeneratedTimestampAttribute()))
.flatten(FLATTENED_TABLE_SCHEMA, Record::getFlattenedRecord, Record::setFlattenedRecord)
.build();

private final List<Map<String, AttributeValue>> fakeItems =
Expand Down Expand Up @@ -154,12 +165,14 @@ public void putNewRecordSetsInitialAutoGeneratedTimestamp() {
mappedTable.putItem(r -> r.item(item));
Record result = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id")));
GetItemResponse itemAsStoredInDDB = getItemAsStoredFromDDB();
FlattenedRecord flattenedRecord = new FlattenedRecord().setGenerated(MOCKED_INSTANT_NOW);
Record expectedRecord = new Record().setId("id")
.setAttribute("one")
.setLastUpdatedDate(MOCKED_INSTANT_NOW)
.setConvertedLastUpdatedDate(MOCKED_INSTANT_NOW)
.setCreatedDate(MOCKED_INSTANT_NOW)
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_NOW);
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_NOW)
.setFlattenedRecord(flattenedRecord);
assertThat(result, is(expectedRecord));
// The data in DDB is stored in converted time format
assertThat(itemAsStoredInDDB.item().get("convertedLastUpdatedDate").s(), is("13 01 2019 14:00:00"));
Expand All @@ -169,12 +182,14 @@ public void putNewRecordSetsInitialAutoGeneratedTimestamp() {
public void updateNewRecordSetsAutoFormattedDate() {
Record result = mappedTable.updateItem(r -> r.item(new Record().setId("id").setAttribute("one")));
GetItemResponse itemAsStoredInDDB = getItemAsStoredFromDDB();
FlattenedRecord flattenedRecord = new FlattenedRecord().setGenerated(MOCKED_INSTANT_NOW);
Record expectedRecord = new Record().setId("id")
.setAttribute("one")
.setLastUpdatedDate(MOCKED_INSTANT_NOW)
.setConvertedLastUpdatedDate(MOCKED_INSTANT_NOW)
.setCreatedDate(MOCKED_INSTANT_NOW)
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_NOW);
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_NOW)
.setFlattenedRecord(flattenedRecord);
assertThat(result, is(expectedRecord));
// The data in DDB is stored in converted time format
assertThat(itemAsStoredInDDB.item().get("convertedLastUpdatedDate").s(), is("13 01 2019 14:00:00"));
Expand All @@ -185,12 +200,14 @@ public void putExistingRecordUpdatedWithAutoFormattedTimestamps() {
mappedTable.putItem(r -> r.item(new Record().setId("id").setAttribute("one")));
Record result = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id")));
GetItemResponse itemAsStoredInDDB = getItemAsStoredFromDDB();
FlattenedRecord flattenedRecord = new FlattenedRecord().setGenerated(MOCKED_INSTANT_NOW);
Record expectedRecord = new Record().setId("id")
.setAttribute("one")
.setLastUpdatedDate(MOCKED_INSTANT_NOW)
.setConvertedLastUpdatedDate(MOCKED_INSTANT_NOW)
.setCreatedDate(MOCKED_INSTANT_NOW)
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_NOW);
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_NOW)
.setFlattenedRecord(flattenedRecord);
assertThat(result, is(expectedRecord));
// The data in DDB is stored in converted time format
assertThat(itemAsStoredInDDB.item().get("convertedLastUpdatedDate").s(), is("13 01 2019 14:00:00"));
Expand All @@ -199,13 +216,15 @@ public void putExistingRecordUpdatedWithAutoFormattedTimestamps() {
mappedTable.putItem(r -> r.item(new Record().setId("id").setAttribute("one")));
result = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id")));
itemAsStoredInDDB = getItemAsStoredFromDDB();
flattenedRecord = new FlattenedRecord().setGenerated(MOCKED_INSTANT_UPDATE_ONE);
expectedRecord = new Record().setId("id")
.setAttribute("one")
.setLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
.setConvertedLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
// Note : Since we are doing PutItem second time, the createDate gets updated,
.setCreatedDate(MOCKED_INSTANT_UPDATE_ONE)
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_UPDATE_ONE);
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_UPDATE_ONE)
.setFlattenedRecord(flattenedRecord);

System.out.println("result "+result);
assertThat(result, is(expectedRecord));
Expand All @@ -218,12 +237,14 @@ public void putItemFollowedByUpdates() {
mappedTable.putItem(r -> r.item(new Record().setId("id").setAttribute("one")));
Record result = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id")));
GetItemResponse itemAsStoredInDDB = getItemAsStoredFromDDB();
FlattenedRecord flattenedRecord = new FlattenedRecord().setGenerated(MOCKED_INSTANT_NOW);
Record expectedRecord = new Record().setId("id")
.setAttribute("one")
.setLastUpdatedDate(MOCKED_INSTANT_NOW)
.setConvertedLastUpdatedDate(MOCKED_INSTANT_NOW)
.setCreatedDate(MOCKED_INSTANT_NOW)
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_NOW);
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_NOW)
.setFlattenedRecord(flattenedRecord);
assertThat(result, is(expectedRecord));
// The data in DDB is stored in converted time format
assertThat(itemAsStoredInDDB.item().get("convertedLastUpdatedDate").s(), is("13 01 2019 14:00:00"));
Expand All @@ -233,12 +254,14 @@ public void putItemFollowedByUpdates() {

result = mappedTable.updateItem(r -> r.item(new Record().setId("id").setAttribute("one")));
itemAsStoredInDDB = getItemAsStoredFromDDB();
flattenedRecord = new FlattenedRecord().setGenerated(MOCKED_INSTANT_UPDATE_ONE);
expectedRecord = new Record().setId("id")
.setAttribute("one")
.setLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
.setConvertedLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
.setCreatedDate(MOCKED_INSTANT_NOW)
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_UPDATE_ONE);
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_UPDATE_ONE)
.setFlattenedRecord(flattenedRecord);
assertThat(result, is(expectedRecord));
// The data in DDB is stored in converted time format
assertThat(itemAsStoredInDDB.item().get("convertedLastUpdatedDate").s(), is("14 01 2019 14:00:00"));
Expand All @@ -247,12 +270,14 @@ public void putItemFollowedByUpdates() {
Mockito.when(mockCLock.instant()).thenReturn(MOCKED_INSTANT_UPDATE_TWO);
result = mappedTable.updateItem(r -> r.item(new Record().setId("id").setAttribute("one")));
itemAsStoredInDDB = getItemAsStoredFromDDB();
flattenedRecord = new FlattenedRecord().setGenerated(MOCKED_INSTANT_UPDATE_TWO);
expectedRecord = new Record().setId("id")
.setAttribute("one")
.setLastUpdatedDate(MOCKED_INSTANT_UPDATE_TWO)
.setConvertedLastUpdatedDate(MOCKED_INSTANT_UPDATE_TWO)
.setCreatedDate(MOCKED_INSTANT_NOW)
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_UPDATE_TWO);
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_UPDATE_TWO)
.setFlattenedRecord(flattenedRecord);
assertThat(result, is(expectedRecord));
// The data in DDB is stored in converted time format
assertThat(itemAsStoredInDDB.item().get("convertedLastUpdatedDate").s(), is("15 01 2019 14:00:00"));
Expand All @@ -267,12 +292,14 @@ public void putExistingRecordWithConditionExpressions() {
mappedTable.putItem(r -> r.item(new Record().setId("id").setAttribute("one")));
Record result = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id")));
GetItemResponse itemAsStoredInDDB = getItemAsStoredFromDDB();
FlattenedRecord flattenedRecord = new FlattenedRecord().setGenerated(MOCKED_INSTANT_NOW);
Record expectedRecord = new Record().setId("id")
.setAttribute("one")
.setLastUpdatedDate(MOCKED_INSTANT_NOW)
.setConvertedLastUpdatedDate(MOCKED_INSTANT_NOW)
.setCreatedDate(MOCKED_INSTANT_NOW)
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_NOW);
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_NOW)
.setFlattenedRecord(flattenedRecord);
assertThat(result, is(expectedRecord));
// The data in DDB is stored in converted time format
assertThat(itemAsStoredInDDB.item().get("convertedLastUpdatedDate").s(), is("13 01 2019 14:00:00"));
Expand All @@ -291,13 +318,15 @@ public void putExistingRecordWithConditionExpressions() {
.build());

result = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id")));
flattenedRecord = new FlattenedRecord().setGenerated(MOCKED_INSTANT_UPDATE_ONE);
expectedRecord = new Record().setId("id")
.setAttribute("one")
.setLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
.setConvertedLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
// Note that this is a second putItem call so create date is updated.
.setCreatedDate(MOCKED_INSTANT_UPDATE_ONE)
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_UPDATE_ONE);
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_UPDATE_ONE)
.setFlattenedRecord(flattenedRecord);
assertThat(result, is(expectedRecord));
}

Expand All @@ -319,12 +348,14 @@ public void updateExistingRecordWithConditionExpressions() {
.conditionExpression(conditionExpression));

Record result = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id")));
FlattenedRecord flattenedRecord = new FlattenedRecord().setGenerated(MOCKED_INSTANT_UPDATE_ONE);
Record expectedRecord = new Record().setId("id")
.setAttribute("one")
.setLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
.setConvertedLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
.setCreatedDate(MOCKED_INSTANT_NOW)
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_UPDATE_ONE);
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_UPDATE_ONE)
.setFlattenedRecord(flattenedRecord);
assertThat(result, is(expectedRecord));
}

Expand Down Expand Up @@ -408,6 +439,7 @@ private static class Record {
private Instant lastUpdatedDate;
private Instant convertedLastUpdatedDate;
private Instant lastUpdatedDateInEpochMillis;
private FlattenedRecord flattenedRecord;

private String getId() {
return id;
Expand Down Expand Up @@ -463,6 +495,15 @@ private Record setLastUpdatedDateInEpochMillis(Instant lastUpdatedDateInEpochMil
return this;
}

public FlattenedRecord getFlattenedRecord() {
return flattenedRecord;
}

public Record setFlattenedRecord(FlattenedRecord flattenedRecord) {
this.flattenedRecord = flattenedRecord;
return this;
}

@Override
public boolean equals(Object o) {
if (this == o) {
Expand All @@ -477,13 +518,14 @@ public boolean equals(Object o) {
Objects.equals(lastUpdatedDate, record.lastUpdatedDate) &&
Objects.equals(createdDate, record.createdDate) &&
Objects.equals(lastUpdatedDateInEpochMillis, record.lastUpdatedDateInEpochMillis) &&
Objects.equals(convertedLastUpdatedDate, record.convertedLastUpdatedDate);
Objects.equals(convertedLastUpdatedDate, record.convertedLastUpdatedDate) &&
Objects.equals(flattenedRecord, record.flattenedRecord);
}

@Override
public int hashCode() {
return Objects.hash(id, attribute,
lastUpdatedDate, createdDate, lastUpdatedDateInEpochMillis, convertedLastUpdatedDate);
return Objects.hash(id, attribute, lastUpdatedDate, createdDate, lastUpdatedDateInEpochMillis,
convertedLastUpdatedDate, flattenedRecord);
}

@Override
Expand All @@ -495,6 +537,44 @@ public String toString() {
", lastUpdatedDate=" + lastUpdatedDate +
", convertedLastUpdatedDate=" + convertedLastUpdatedDate +
", lastUpdatedDateInEpochMillis=" + lastUpdatedDateInEpochMillis +
", flattenedRecord=" + flattenedRecord +
'}';
}
}

private static class FlattenedRecord {
private Instant generated;

public Instant getGenerated() {
return generated;
}

public FlattenedRecord setGenerated(Instant generated) {
this.generated = generated;
return this;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
FlattenedRecord that = (FlattenedRecord) o;
return Objects.equals(generated, that.generated);
}

@Override
public int hashCode() {
return Objects.hash(generated);
}

@Override
public String toString() {
return "FlattenedRecord{" +
"generated=" + generated +
'}';
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,13 @@ public void attributeNames() {
assertThat(result).containsExactlyInAnyOrder(ITEM_MAP.keySet().toArray(new String[]{}));
}

@Test
public void converterForAttribute() {
ITEM_MAP.forEach((key, attributeValue) -> {
assertThat(immutableTableSchema.converterForAttribute(key)).isNotNull();
});
}

public static class ImmutableRecord {
private final String id;
private final String attribute1;
Expand Down Expand Up @@ -272,4 +279,4 @@ public ImmutableRecord build() {
}
}
}
}
}