Skip to content

Commit 708216e

Browse files
authored
Fix DDB Enhanced Client immutable class introspection fails for "is" prefix fields (#6349)
* Fix DynamoDB Enhanced Client immutable class introspection fails for is prefix fields issue * Add test for non-boolean fields with 'is' prefix
1 parent da179b4 commit 708216e

File tree

3 files changed

+181
-0
lines changed

3 files changed

+181
-0
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": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "Fix DynamoDB Enhanced Client immutable class introspection fails for \"is\" prefix fields. Fixes [#4446](https://github.com/aws/aws-sdk-java-v2/issues/4446)."
6+
}

services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/immutable/ImmutableIntrospector.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,14 @@ private String normalizeSetterName(Method setter) {
180180
return Character.toLowerCase(setterName.charAt(3)) + setterName.substring(4);
181181
}
182182

183+
if (setterName.length() > 2
184+
&& Character.isUpperCase(setterName.charAt(2))
185+
&& setterName.startsWith(IS_PREFIX)
186+
&& isSetterMethodBoolean(setter)) {
187+
188+
return Character.toLowerCase(setterName.charAt(2)) + setterName.substring(3);
189+
}
190+
183191
return setterName;
184192
}
185193

@@ -208,6 +216,11 @@ private boolean isMethodBoolean(Method method) {
208216
return method.getReturnType() == boolean.class || method.getReturnType() == Boolean.class;
209217
}
210218

219+
private boolean isSetterMethodBoolean(Method setter) {
220+
return setter.getParameterCount() == 1 &&
221+
(setter.getParameterTypes()[0] == boolean.class || setter.getParameterTypes()[0] == Boolean.class);
222+
}
223+
211224
private Optional<Method> extractBuildMethod(Map<String, Method> indexedBuilderMethods, Class<?> immutableClass) {
212225
Method buildMethod = indexedBuilderMethods.get(BUILD_METHOD);
213226

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
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;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import org.junit.Test;
21+
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbImmutable;
22+
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
23+
24+
/**
25+
* Tests that TableSchema.fromImmutableClass() works correctly with immutable classes
26+
* that have fields using "is" prefix.
27+
*/
28+
public class TableSchemaImmutableIsPrefixTest {
29+
30+
// Test class for boolean fields with "is" prefix
31+
@DynamoDbImmutable(builder = Car.Builder.class)
32+
public static final class Car {
33+
private final String licensePlate;
34+
private final boolean isRusty;
35+
private final boolean isImpounded;
36+
37+
private Car(Builder b) {
38+
this.licensePlate = b.licensePlate;
39+
this.isRusty = b.isRusty;
40+
this.isImpounded = b.isImpounded;
41+
}
42+
43+
@DynamoDbPartitionKey
44+
public String licensePlate() {
45+
return this.licensePlate;
46+
}
47+
48+
public boolean isRusty() {
49+
return this.isRusty;
50+
}
51+
52+
public boolean isImpounded() {
53+
return this.isImpounded;
54+
}
55+
56+
public static final class Builder {
57+
private String licensePlate;
58+
private boolean isRusty;
59+
private boolean isImpounded;
60+
61+
public Builder licensePlate(String licensePlate) {
62+
this.licensePlate = licensePlate;
63+
return this;
64+
}
65+
66+
public Builder isRusty(boolean isRusty) {
67+
this.isRusty = isRusty;
68+
return this;
69+
}
70+
71+
public Builder isImpounded(boolean isImpounded) {
72+
this.isImpounded = isImpounded;
73+
return this;
74+
}
75+
76+
public Car build() {
77+
return new Car(this);
78+
}
79+
}
80+
}
81+
82+
@Test
83+
public void fromImmutableClass_withIsPrefixBooleanSetters_shouldCreateSchemaSuccessfully() {
84+
// This should work without exception
85+
TableSchema<Car> schema = TableSchema.fromImmutableClass(Car.class);
86+
87+
// Verify the schema was created successfully without exception
88+
assertThat(schema).isNotNull();
89+
assertThat(schema.itemType().rawClass()).isEqualTo(Car.class);
90+
91+
// Verify all attributes are mapped correctly
92+
assertThat(schema.attributeNames()).containsExactlyInAnyOrder(
93+
"licensePlate", "rusty", "impounded"
94+
);
95+
}
96+
97+
// Test class for non-boolean fields with "is" prefix
98+
@DynamoDbImmutable(builder = Vehicle.Builder.class)
99+
public static final class Vehicle {
100+
private final String licensePlate;
101+
private final String isModel;
102+
private final Integer isYear;
103+
104+
private Vehicle(Builder b) {
105+
this.licensePlate = b.licensePlate;
106+
this.isModel = b.isModel;
107+
this.isYear = b.isYear;
108+
}
109+
110+
@DynamoDbPartitionKey
111+
public String licensePlate() {
112+
return this.licensePlate;
113+
}
114+
115+
public String isModel() {
116+
return this.isModel;
117+
}
118+
119+
public Integer isYear() {
120+
return this.isYear;
121+
}
122+
123+
public static final class Builder {
124+
private String licensePlate;
125+
private String isModel;
126+
private Integer isYear;
127+
128+
public Builder licensePlate(String licensePlate) {
129+
this.licensePlate = licensePlate;
130+
return this;
131+
}
132+
133+
public Builder isModel(String isModel) {
134+
this.isModel = isModel;
135+
return this;
136+
}
137+
138+
public Builder isYear(Integer isYear) {
139+
this.isYear = isYear;
140+
return this;
141+
}
142+
143+
public Vehicle build() {
144+
return new Vehicle(this);
145+
}
146+
}
147+
}
148+
149+
@Test
150+
public void fromImmutableClass_withIsPrefixNonBooleanFields_shouldNotNormalizeIsPrefix() {
151+
TableSchema<Vehicle> schema = TableSchema.fromImmutableClass(Vehicle.class);
152+
153+
// Verify the schema was created successfully
154+
assertThat(schema).isNotNull();
155+
assertThat(schema.itemType().rawClass()).isEqualTo(Vehicle.class);
156+
157+
// Verify non-boolean "is" prefix fields are not normalized
158+
assertThat(schema.attributeNames()).containsExactlyInAnyOrder(
159+
"licensePlate", "isModel", "isYear"
160+
);
161+
}
162+
}

0 commit comments

Comments
 (0)