Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "bugfix",
"category": "Amazon DynamoDB Enhanced Client",
"contributor": "",
"description": "Fix AutoGeneratedTimestampRecordExtension failing on @DynamoDbConvertedBy list attributes"
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import software.amazon.awssdk.enhanced.dynamodb.AttributeConverter;
import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbImmutable;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.utils.CollectionUtils;
import software.amazon.awssdk.utils.StringUtils;
Expand All @@ -49,13 +51,18 @@
* If the provided key contains the nested object delimiter (e.g., {@code _NESTED_ATTR_UPDATE_}), the method traverses the
* nested hierarchy based on that path to locate the correct schema for the target attribute. Otherwise, it directly resolves
* the list element type from the root schema using reflection.
* <p>
* If the list element type is not annotated with {@code @DynamoDbBean} or {@code @DynamoDbImmutable} (e.g. when a custom
* converter handles serialization via {@code @DynamoDbConvertedBy}), this method returns {@code null} to indicate that no
* schema introspection is possible or necessary for that element type.
*
* @param rootSchema The root {@link TableSchema} representing the top-level entity.
* @param key The key representing the list attribute, either flat or nested (using a delimiter).
* @return The {@link TableSchema} representing the list element type of the specified attribute.
* @throws IllegalArgumentException If the list element class cannot be found via reflection.
* @return The {@link TableSchema} representing the list element type, or {@code null} if the element type is not a
* DynamoDB-annotated class.
* @throws IllegalArgumentException If no converter is found for the attribute or if the converter has no type parameters.
*/
public static TableSchema<?> getTableSchemaForListElement(TableSchema<?> rootSchema, String key) {

Check failure on line 65 in services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/extensions/utility/NestedRecordUtils.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove usage of generic wildcard type.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AoLSTzC4JOHO7Tw&open=AZ192AoLSTzC4JOHO7Tw&pullRequest=6854
return getTableSchemaForListElement(rootSchema, key, new HashMap<>());
}

Expand All @@ -63,7 +70,7 @@
* Same as {@link #getTableSchemaForListElement(TableSchema, String)} but allows callers to provide a shared per-operation
* cache for nested schema lookups.
*/
public static TableSchema<?> getTableSchemaForListElement(

Check failure on line 73 in services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/extensions/utility/NestedRecordUtils.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove usage of generic wildcard type.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AoLSTzC4JOHO7Tx&open=AZ192AoLSTzC4JOHO7Tx&pullRequest=6854
TableSchema<?> rootSchema,
String key,
Map<SchemaLookupKey, Optional<TableSchema<?>>> nestedSchemaCache) {
Expand All @@ -85,7 +92,12 @@
if (CollectionUtils.isNullOrEmpty(rawClassParameters)) {
throw new IllegalArgumentException("No type parameters found for list attribute: " + key);
}
return TableSchema.fromClass(rawClassParameters.get(0).rawClass());
Class<?> elementClass = rawClassParameters.get(0).rawClass();
if (elementClass.getAnnotation(DynamoDbBean.class) == null
&& elementClass.getAnnotation(DynamoDbImmutable.class) == null) {
return null;
}
return TableSchema.fromClass(elementClass);
}

private static TableSchema<?> listElementSchemaForDelimitedKey(
Expand Down Expand Up @@ -212,10 +224,11 @@
/**
* Cached wrapper for resolving list element schema, storing results (including null) in the provided cache.
* <p>
* Note: {@link #getTableSchemaForListElement(TableSchema, String, Map)} does not return null today, but this helper is used
* by callers that previously cached the list element schema separately, and it keeps the "cache null" behavior.
* {@link #getTableSchemaForListElement(TableSchema, String, Map)} returns {@code null} when the list element type is not a
* DynamoDB-annotated class (e.g. when a custom converter handles serialization). Callers should check for null before
* attempting to introspect the returned schema.
*/
public static TableSchema<?> getListElementSchemaCached(

Check failure on line 231 in services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/extensions/utility/NestedRecordUtils.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove usage of generic wildcard type.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AoLSTzC4JOHO7Ty&open=AZ192AoLSTzC4JOHO7Ty&pullRequest=6854
Map<SchemaLookupKey, TableSchema<?>> cache,
TableSchema<?> parentSchema,
String attributeName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@
import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampTestModels.BeanWithInvalidNestedAttributeName;
import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampTestModels.BeanWithInvalidNestedAttributeName.BeanWithInvalidNestedAttributeNameChild;
import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampTestModels.BeanWithInvalidRootAttributeName;
import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampTestModels.BeanWithCustomConvertedList;
import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampTestModels.BeanWithCustomConvertedMap;
import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampTestModels.CustomConvertedPojo;
import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampTestModels.NestedBeanChild;
import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampTestModels.NestedBeanWithList;
import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampTestModels.NestedImmutableChildRecordWithList;
Expand Down Expand Up @@ -136,7 +139,7 @@
.build();

@Rule
public ExpectedException thrown = ExpectedException.none();

Check warning on line 142 in services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedTimestampExtensionTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "none"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AthSTzC4JOHO7UE&open=AZ192AthSTzC4JOHO7UE&pullRequest=6854

@Before
public void setup() {
Expand Down Expand Up @@ -1279,7 +1282,7 @@
@Test
public void beforeWrite_itemContainsOnlyStringList_thenStringListUnchanged() {
TableSchema<SimpleBeanWithList> schema = BeanTableSchema.create(SimpleBeanWithList.class);
SimpleBeanWithList record = new SimpleBeanWithList()

Check warning on line 1285 in services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedTimestampExtensionTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this variable to not match a restricted identifier.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AthSTzC4JOHO7UF&open=AZ192AthSTzC4JOHO7UF&pullRequest=6854
.setId("1")
.setChildStringList(Arrays.asList("a", "b"));
record.setChildList(new ArrayList<>());
Expand All @@ -1296,7 +1299,7 @@
public void beforeWrite_topLevelChildListHasMapsThenString_thenMapsTimestampedAndStringUnchanged() {
String expectedWrittenInstant = MOCKED_INSTANT_NOW.toString();
TableSchema<SimpleBeanWithList> schema = BeanTableSchema.create(SimpleBeanWithList.class);
SimpleBeanWithList record = new SimpleBeanWithList()

Check warning on line 1302 in services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedTimestampExtensionTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this variable to not match a restricted identifier.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AthSTzC4JOHO7UG&open=AZ192AthSTzC4JOHO7UG&pullRequest=6854
.setId("1")
.setChildList(Arrays.asList(new SimpleBeanChild().setId("c1"), new SimpleBeanChild().setId("c2")));

Expand All @@ -1322,7 +1325,7 @@
TableSchema<NestedBeanWithList> schema = BeanTableSchema.create(NestedBeanWithList.class);
NestedBeanChild level2 = new NestedBeanChild();
level2.setChildList(Arrays.asList(new SimpleBeanChild().setId("a"), new SimpleBeanChild().setId("b")));
NestedBeanWithList record = new NestedBeanWithList().setId("1").setLevel2(level2);

Check warning on line 1328 in services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedTimestampExtensionTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this variable to not match a restricted identifier.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AthSTzC4JOHO7UH&open=AZ192AthSTzC4JOHO7UH&pullRequest=6854

Map<String, AttributeValue> putItem = new HashMap<>(schema.itemToMap(record, false));
Map<String, AttributeValue> level2Map = new HashMap<>(putItem.get("level2").m());
Expand All @@ -1345,7 +1348,7 @@
@Test
public void beforeWrite_nestedRecordHoldsAttributeNotOnSchema_thenThatSubtreeUnchanged() {
TableSchema<NestedRecord> schema = createNestedRecordSchema();
NestedRecord record = new NestedRecord()

Check warning on line 1351 in services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedTimestampExtensionTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this variable to not match a restricted identifier.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AthSTzC4JOHO7UI&open=AZ192AthSTzC4JOHO7UI&pullRequest=6854
.setId("1")
.setAttribute("x")
.setNestedRecord(new NestedBeanChild().setChildList(Collections.emptyList()));
Expand All @@ -1369,7 +1372,7 @@
TableSchema<?> tableSchema = mock(TableSchema.class);
TableMetadata tableMetadata = mock(TableMetadata.class);
when(tableSchema.tableMetadata()).thenReturn(tableMetadata);
when(tableMetadata.customMetadataObject(eq(AUTO_TS_EXTENSION_METADATA_KEY), eq(Collection.class)))

Check warning on line 1375 in services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedTimestampExtensionTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this and every subsequent useless "eq(...)" invocation; pass the values directly.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AthSTzC4JOHO7UJ&open=AZ192AthSTzC4JOHO7UJ&pullRequest=6854
.thenReturn(Optional.of(Arrays.asList("validTs", "missingTs")));

AttributeConverter<?> instantConverter =
Expand All @@ -1391,7 +1394,7 @@
@Test
public void beforeWrite_nestedMetadataNamesNonSchemaTimestamp_thenDeclaredInstantAttributeStillWritten() {
TableSchema<RootWithNestedUnknownMetadataTimestampKey> schema = schemaRootWithNestedUnknownMetadataTimestampKey();
RootWithNestedUnknownMetadataTimestampKey record = new RootWithNestedUnknownMetadataTimestampKey();

Check warning on line 1397 in services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedTimestampExtensionTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this variable to not match a restricted identifier.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AthSTzC4JOHO7UK&open=AZ192AthSTzC4JOHO7UK&pullRequest=6854
record.setId("1");
record.setNested(new NestedWithUnknownMetadataTimestampKey());

Expand All @@ -1407,7 +1410,7 @@
public void beforeWrite_nestedDocumentHasTwoAutoTimestampFields_thenBothMatchClockInstant() {
String expectedWrittenInstant = MOCKED_INSTANT_NOW.toString();
TableSchema<RootWithNestedTwoAutoTimestamps> schema = schemaRootWithNestedTwoAutoTimestamps();
RootWithNestedTwoAutoTimestamps record = new RootWithNestedTwoAutoTimestamps();

Check warning on line 1413 in services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedTimestampExtensionTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this variable to not match a restricted identifier.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AthSTzC4JOHO7UL&open=AZ192AthSTzC4JOHO7UL&pullRequest=6854
record.setId("1");
record.setNested(new NestedWithTwoAutoTimestamps());

Expand All @@ -1426,7 +1429,7 @@
TableSchema<NestedBeanWithList> schema = BeanTableSchema.create(NestedBeanWithList.class);
NestedBeanChild level2 = new NestedBeanChild();
level2.setChildList(Collections.singletonList(new SimpleBeanChild().setId("c1")));
NestedBeanWithList record = new NestedBeanWithList().setId("1").setLevel2(level2);

Check warning on line 1432 in services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedTimestampExtensionTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this variable to not match a restricted identifier.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AthSTzC4JOHO7UM&open=AZ192AthSTzC4JOHO7UM&pullRequest=6854

Map<String, AttributeValue> putItem = new HashMap<>(schema.itemToMap(record, false));
Map<String, AttributeValue> level2Map = new HashMap<>(putItem.get("level2").m());
Expand All @@ -1447,6 +1450,49 @@
assertThat(writtenNestedList.get(1), is(childDocumentBeforeWrite));
}

@Test
public void beforeWrite_listWithCustomConverter_whenElementTypeNotAnnotated_thenTimestampSetAndListUnchanged() {
String expectedWrittenInstant = MOCKED_INSTANT_NOW.toString();
TableSchema<BeanWithCustomConvertedList> schema = BeanTableSchema.create(BeanWithCustomConvertedList.class);
BeanWithCustomConvertedList record = new BeanWithCustomConvertedList()

Check warning on line 1457 in services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedTimestampExtensionTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this variable to not match a restricted identifier.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AthSTzC4JOHO7UN&open=AZ192AthSTzC4JOHO7UN&pullRequest=6854
.setId("1")
.setCustomItems(Arrays.asList(
new CustomConvertedPojo("a", 1),
new CustomConvertedPojo("b", 2)));

Map<String, AttributeValue> putItem = new HashMap<>(schema.itemToMap(record, false));
AttributeValue originalList = putItem.get("customItems");

WriteModification modification = invokeBeforeWriteForPutItem(putItem, schema);
Map<String, AttributeValue> transformed = modification.transformedItem();

assertThat(transformed, is(notNullValue()));
assertThat(transformed.get("time").s(), is(expectedWrittenInstant));
assertThat(transformed.get("customItems"), is(originalList));
}

@Test
public void beforeWrite_mapWithCustomConverter_whenValueTypeNotAnnotated_thenTimestampSetAndMapUnchanged() {
String expectedWrittenInstant = MOCKED_INSTANT_NOW.toString();
TableSchema<BeanWithCustomConvertedMap> schema = BeanTableSchema.create(BeanWithCustomConvertedMap.class);
Map<String, CustomConvertedPojo> pojoMap = new HashMap<>();
pojoMap.put("first", new CustomConvertedPojo("x", 10));
pojoMap.put("second", new CustomConvertedPojo("y", 20));
BeanWithCustomConvertedMap record = new BeanWithCustomConvertedMap()

Check warning on line 1481 in services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedTimestampExtensionTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this variable to not match a restricted identifier.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AthSTzC4JOHO7UO&open=AZ192AthSTzC4JOHO7UO&pullRequest=6854
.setId("1")
.setCustomMap(pojoMap);

Map<String, AttributeValue> putItem = new HashMap<>(schema.itemToMap(record, false));
AttributeValue originalMap = putItem.get("customMap");

WriteModification modification = invokeBeforeWriteForPutItem(putItem, schema);
Map<String, AttributeValue> transformed = modification.transformedItem();

assertThat(transformed, is(notNullValue()));
assertThat(transformed.get("time").s(), is(expectedWrittenInstant));
assertThat(transformed.get("customMap"), is(originalMap));
}

private <T> WriteModification invokeBeforeWriteForPutItem(Map<String, AttributeValue> itemAttributes,
TableSchema<T> tableSchema) {
return invokeBeforeWriteForPutItem(itemAttributes, tableSchema.tableMetadata(), tableSchema);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,25 @@
import static software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTags.primaryPartitionKey;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import software.amazon.awssdk.enhanced.dynamodb.AttributeConverter;
import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType;
import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.extensions.annotations.DynamoDbAutoGeneratedTimestampAttribute;
import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticImmutableTableSchema;
import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbConvertedBy;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbImmutable;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;

/**
* Test models specifically designed for auto-generated timestamp functionality testing. These models focus on the "time"
Expand Down Expand Up @@ -730,7 +737,7 @@
@DynamoDbBean
public static class BeanWithInvalidRootAttributeName {
private String id;
private Instant attr_NESTED_ATTR_UPDATE_;

Check warning on line 740 in services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/AutogeneratedTimestampTestModels.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this field "attr_NESTED_ATTR_UPDATE_" to match the regular expression '^[a-z][a-zA-Z0-9]*$'.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AtESTzC4JOHO7UC&open=AZ192AtESTzC4JOHO7UC&pullRequest=6854

@DynamoDbPartitionKey
public String getId() {
Expand All @@ -747,7 +754,7 @@
return attr_NESTED_ATTR_UPDATE_;
}

public BeanWithInvalidRootAttributeName setAttr_NESTED_ATTR_UPDATE_(Instant attr_NESTED_ATTR_UPDATE_) {

Check warning on line 757 in services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/AutogeneratedTimestampTestModels.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this local variable to match the regular expression '^[a-z][a-zA-Z0-9]*$'.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AtESTzC4JOHO7UA&open=AZ192AtESTzC4JOHO7UA&pullRequest=6854
this.attr_NESTED_ATTR_UPDATE_ = attr_NESTED_ATTR_UPDATE_;
return this;
}
Expand Down Expand Up @@ -785,7 +792,7 @@
public static class BeanWithInvalidNestedAttributeNameChild {
private String id;
private BeanWithInvalidNestedAttributeNameChild nestedChildAttribute;
private Instant childAttr_NESTED_ATTR_UPDATE_;

Check warning on line 795 in services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/AutogeneratedTimestampTestModels.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this field "childAttr_NESTED_ATTR_UPDATE_" to match the regular expression '^[a-z][a-zA-Z0-9]*$'.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AtESTzC4JOHO7UD&open=AZ192AtESTzC4JOHO7UD&pullRequest=6854

@DynamoDbPartitionKey
public String getId() {
Expand All @@ -811,10 +818,233 @@
return childAttr_NESTED_ATTR_UPDATE_;
}

public BeanWithInvalidNestedAttributeNameChild setAttr_NESTED_ATTR_UPDATE_(Instant attr_NESTED_ATTR_UPDATE_) {

Check warning on line 821 in services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/AutogeneratedTimestampTestModels.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this local variable to match the regular expression '^[a-z][a-zA-Z0-9]*$'.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ192AtESTzC4JOHO7UB&open=AZ192AtESTzC4JOHO7UB&pullRequest=6854
this.childAttr_NESTED_ATTR_UPDATE_ = attr_NESTED_ATTR_UPDATE_;
return this;
}
}
}

/**
* Plain POJO with no DynamoDB annotations. Serialization is handled entirely by
* {@link CustomConvertedPojoListConverter}.
*/
public static class CustomConvertedPojo {
private String label;
private int count;

public CustomConvertedPojo() {
}

public CustomConvertedPojo(String label, int count) {
this.label = label;
this.count = count;
}

public String getLabel() {
return label;
}

public void setLabel(String label) {
this.label = label;
}

public int getCount() {
return count;
}

public void setCount(int count) {
this.count = count;
}
}

/**
* Custom converter for {@code List<CustomConvertedPojo>}. The SDK should not attempt to resolve a
* {@link TableSchema} for {@link CustomConvertedPojo} when this converter is present.
*/
public static class CustomConvertedPojoListConverter implements AttributeConverter<List<CustomConvertedPojo>> {

@Override
public AttributeValue transformFrom(List<CustomConvertedPojo> input) {
if (input == null) {
return AttributeValue.builder().nul(true).build();
}
List<AttributeValue> items = input.stream()
.map(pojo -> {
Map<String, AttributeValue> map = new HashMap<>();
map.put("label", AttributeValue.builder().s(pojo.getLabel()).build());
map.put("count", AttributeValue.builder().n(String.valueOf(pojo.getCount())).build());
return AttributeValue.builder().m(map).build();
})
.collect(Collectors.toList());
return AttributeValue.builder().l(items).build();
}

@Override
public List<CustomConvertedPojo> transformTo(AttributeValue input) {
if (input.l() == null) {
return new ArrayList<>();
}
return input.l().stream()
.map(av -> {
Map<String, AttributeValue> m = av.m();
CustomConvertedPojo pojo = new CustomConvertedPojo();
if (m.containsKey("label")) {
pojo.setLabel(m.get("label").s());
}
if (m.containsKey("count")) {
pojo.setCount(Integer.parseInt(m.get("count").n()));
}
return pojo;
})
.collect(Collectors.toList());
}

@Override
public EnhancedType<List<CustomConvertedPojo>> type() {
return EnhancedType.listOf(CustomConvertedPojo.class);
}

@Override
public AttributeValueType attributeValueType() {
return AttributeValueType.L;
}
}

/**
* Bean that combines {@code @DynamoDbAutoGeneratedTimestampAttribute} with a
* {@code @DynamoDbConvertedBy} list whose element type ({@link CustomConvertedPojo}) has no DynamoDB
* annotations. Reproduces the regression described in GitHub issue #6852.
*/
@DynamoDbBean
public static class BeanWithCustomConvertedList {
private String id;
private Instant time;
private List<CustomConvertedPojo> customItems;

@DynamoDbPartitionKey
public String getId() {
return id;
}

public BeanWithCustomConvertedList setId(String id) {
this.id = id;
return this;
}

@DynamoDbAutoGeneratedTimestampAttribute
public Instant getTime() {
return time;
}

public BeanWithCustomConvertedList setTime(Instant time) {
this.time = time;
return this;
}

@DynamoDbConvertedBy(CustomConvertedPojoListConverter.class)
public List<CustomConvertedPojo> getCustomItems() {
return customItems;
}

public BeanWithCustomConvertedList setCustomItems(List<CustomConvertedPojo> customItems) {
this.customItems = customItems;
return this;
}
}

/**
* Custom converter for {@code Map<String, CustomConvertedPojo>}. Serializes the map as a DynamoDB M attribute
* whose values are themselves maps. The SDK should not attempt to resolve a {@link TableSchema} for
* {@link CustomConvertedPojo} when this converter is present.
*/
public static class CustomConvertedPojoMapConverter implements AttributeConverter<Map<String, CustomConvertedPojo>> {

@Override
public AttributeValue transformFrom(Map<String, CustomConvertedPojo> input) {
if (input == null) {
return AttributeValue.builder().nul(true).build();
}
Map<String, AttributeValue> map = new HashMap<>();
for (Map.Entry<String, CustomConvertedPojo> entry : input.entrySet()) {
Map<String, AttributeValue> inner = new HashMap<>();
inner.put("label", AttributeValue.builder().s(entry.getValue().getLabel()).build());
inner.put("count", AttributeValue.builder().n(String.valueOf(entry.getValue().getCount())).build());
map.put(entry.getKey(), AttributeValue.builder().m(inner).build());
}
return AttributeValue.builder().m(map).build();
}

@Override
public Map<String, CustomConvertedPojo> transformTo(AttributeValue input) {
if (input.m() == null) {
return new HashMap<>();
}
Map<String, CustomConvertedPojo> result = new HashMap<>();
for (Map.Entry<String, AttributeValue> entry : input.m().entrySet()) {
Map<String, AttributeValue> m = entry.getValue().m();
CustomConvertedPojo pojo = new CustomConvertedPojo();
if (m.containsKey("label")) {
pojo.setLabel(m.get("label").s());
}
if (m.containsKey("count")) {
pojo.setCount(Integer.parseInt(m.get("count").n()));
}
result.put(entry.getKey(), pojo);
}
return result;
}

@Override
public EnhancedType<Map<String, CustomConvertedPojo>> type() {
return EnhancedType.mapOf(String.class, CustomConvertedPojo.class);
}

@Override
public AttributeValueType attributeValueType() {
return AttributeValueType.M;
}
}

/**
* Bean that combines {@code @DynamoDbAutoGeneratedTimestampAttribute} with a
* {@code @DynamoDbConvertedBy} map whose value type ({@link CustomConvertedPojo}) has no DynamoDB
* annotations. Verifies the map path is safe alongside the list regression test.
*/
@DynamoDbBean
public static class BeanWithCustomConvertedMap {
private String id;
private Instant time;
private Map<String, CustomConvertedPojo> customMap;

@DynamoDbPartitionKey
public String getId() {
return id;
}

public BeanWithCustomConvertedMap setId(String id) {
this.id = id;
return this;
}

@DynamoDbAutoGeneratedTimestampAttribute
public Instant getTime() {
return time;
}

public BeanWithCustomConvertedMap setTime(Instant time) {
this.time = time;
return this;
}

@DynamoDbConvertedBy(CustomConvertedPojoMapConverter.class)
public Map<String, CustomConvertedPojo> getCustomMap() {
return customMap;
}

public BeanWithCustomConvertedMap setCustomMap(Map<String, CustomConvertedPojo> customMap) {
this.customMap = customMap;
return this;
}
}
}
Loading
Loading