Skip to content

Commit 31be1b2

Browse files
authored
Improve method/field property name match, fix delimited name strategies (#2380)
* Improve method/field property name match, fix delimited name strategies Signed-off-by: Michael Edgar <[email protected]> * Additional test case, clear several Sonar warnings Signed-off-by: Michael Edgar <[email protected]> --------- Signed-off-by: Michael Edgar <[email protected]>
1 parent 8b84ea1 commit 31be1b2

File tree

4 files changed

+122
-33
lines changed

4 files changed

+122
-33
lines changed

core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/PropertyNamingStrategyFactory.java

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -100,19 +100,15 @@ public String apply(final String propertyName) {
100100

101101
for (int i = 0; i < propertyName.length(); i++) {
102102
final char c = propertyName.charAt(i);
103+
final char next = propertyName.length() > i + 1 ? propertyName.charAt(i + 1) : '\0';
104+
final char transformed = converter.apply(c);
103105

104-
if (Character.isUpperCase(c)) {
105-
final char transformed = converter.apply(c);
106-
107-
if (current.length() > 0) {
108-
global.append(current).append(separator);
109-
current.setLength(0);
110-
}
111-
112-
current.append(transformed);
113-
} else {
114-
current.append(c);
106+
if (current.length() > 0 && Character.isUpperCase(c) && !Character.isUpperCase(next)) {
107+
global.append(current).append(separator);
108+
current.setLength(0);
115109
}
110+
111+
current.append(transformed);
116112
}
117113

118114
if (current.length() > 0) {

core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/TypeResolver.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,11 +1029,28 @@ static String propertyName(Map<String, TypeResolver> properties, MethodInfo meth
10291029
}
10301030

10311031
if (nameStart > 0) {
1032-
StringBuilder nameBuffer = new StringBuilder(methodName.length());
1033-
nameBuffer.append(methodName);
1034-
nameBuffer.setCharAt(nameStart, Character.toLowerCase(methodName.charAt(nameStart)));
1032+
String rawPropertyName = methodName.substring(nameStart);
1033+
if (Optional.ofNullable(properties.get(rawPropertyName)).map(p -> p.field).isPresent()) {
1034+
/*
1035+
* The raw property name name matches the field name. Do not modify further.
1036+
* E.g. field is URL and method is getURL.
1037+
*/
1038+
return rawPropertyName;
1039+
}
10351040

1036-
propertyName = nameBuffer.substring(nameStart);
1041+
/*
1042+
* If first two chars are upper-case, do nothing
1043+
* (following java.beans.Introspector.decapitalize(String))
1044+
*/
1045+
if (rawPropertyName.length() > 1
1046+
&& Character.isUpperCase(rawPropertyName.charAt(1))
1047+
&& Character.isUpperCase(rawPropertyName.charAt(0))) {
1048+
propertyName = rawPropertyName;
1049+
} else {
1050+
StringBuilder nameBuffer = new StringBuilder(rawPropertyName);
1051+
nameBuffer.setCharAt(0, Character.toLowerCase(rawPropertyName.charAt(0)));
1052+
propertyName = nameBuffer.toString();
1053+
}
10371054
} else {
10381055
propertyName = methodName;
10391056
}

core/src/test/java/io/smallrye/openapi/runtime/scanner/PropertyNamingStrategyTest.java

Lines changed: 71 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
import static org.junit.jupiter.api.Assertions.assertThrows;
55

66
import java.math.BigDecimal;
7+
import java.util.ArrayList;
78
import java.util.Arrays;
9+
import java.util.Collections;
10+
import java.util.List;
811
import java.util.Map;
9-
import java.util.Set;
10-
import java.util.TreeSet;
1112

1213
import org.eclipse.microprofile.config.Config;
1314
import org.eclipse.microprofile.openapi.annotations.media.Schema;
@@ -49,42 +50,52 @@ void testJacksonNamingOverridesConfig() throws Exception {
4950
}
5051

5152
@Test
52-
void testInvalidNamingStrategyClass() throws Exception {
53+
void testInvalidNamingStrategyClass() {
5354
Config config = config(SmallRyeOASConfig.SMALLRYE_PROPERTY_NAMING_STRATEGY,
5455
"com.fasterxml.jackson.databind.PropertyNamingStrategies$InvalidStrategy");
5556
assertThrows(OpenApiRuntimeException.class, () -> scan(config, NameStrategyKebab.class));
5657
}
5758

5859
@Test
59-
void testNoValidTranslationMethods() throws Exception {
60+
void testNoValidTranslationMethods() {
6061
Config config = config(SmallRyeOASConfig.SMALLRYE_PROPERTY_NAMING_STRATEGY,
6162
NoValidTranslationMethods.class.getName());
6263
assertThrows(OpenApiRuntimeException.class, () -> scan(config, NameStrategyKebab.class));
6364
}
6465

6566
@Test
66-
void testInvalidPropertyNameTranslationAttempt() throws Exception {
67+
void testInvalidPropertyNameTranslationAttempt() {
6768
Config config = config(SmallRyeOASConfig.SMALLRYE_PROPERTY_NAMING_STRATEGY,
6869
TranslationThrowsException.class.getName());
6970
assertThrows(OpenApiRuntimeException.class, () -> scan(config, NameStrategyBean3.class));
7071
}
7172

7273
@ParameterizedTest(name = "testJsonbConstantStrategy-{0}")
7374
@CsvSource({
74-
JsonbConstants.IDENTITY + ", simpleStringOne|anotherField|Y|z",
75-
JsonbConstants.LOWER_CASE_WITH_DASHES + ", simple-string-one|another-field|y|z",
76-
JsonbConstants.LOWER_CASE_WITH_UNDERSCORES + ", simple_string_one|another_field|y|z",
77-
JsonbConstants.UPPER_CAMEL_CASE + ", SimpleStringOne|AnotherField|Y|Z",
78-
JsonbConstants.UPPER_CAMEL_CASE_WITH_SPACES + ", Simple String One|Another Field|Y|Z",
79-
JsonbConstants.CASE_INSENSITIVE + ", simpleStringOne|anotherField|Y|z"
75+
JsonbConstants.IDENTITY + ", simpleStringOne|anotherField|Y|z|SOMEValue",
76+
JsonbConstants.LOWER_CASE_WITH_DASHES + ", simple-string-one|another-field|y|z|some-value",
77+
JsonbConstants.LOWER_CASE_WITH_UNDERSCORES + ", simple_string_one|another_field|y|z|some_value",
78+
JsonbConstants.UPPER_CAMEL_CASE + ", SimpleStringOne|AnotherField|Y|Z|SOMEValue",
79+
JsonbConstants.UPPER_CAMEL_CASE_WITH_SPACES + ", Simple String One|Another Field|Y|Z|SOME Value",
80+
JsonbConstants.CASE_INSENSITIVE + ", simpleStringOne|anotherField|Y|z|SOMEValue"
8081
})
81-
void testJsonbConstantStrategy(String strategy, String expectedNames) throws Exception {
82+
void testJsonbConstantStrategy(String strategy, String expectedNames) {
8283
OpenAPI result = scan(config(SmallRyeOASConfig.SMALLRYE_PROPERTY_NAMING_STRATEGY, strategy), NameStrategyBean3.class);
83-
Set<String> expectedNameSet = new TreeSet<>(Arrays.asList(expectedNames.split("\\|")));
84+
List<String> expectedNameList = Arrays.asList(expectedNames.split("\\|"));
85+
Collections.sort(expectedNameList, String::compareToIgnoreCase);
86+
8487
Map<String, org.eclipse.microprofile.openapi.models.media.Schema> schemas = result.getComponents().getSchemas();
8588
org.eclipse.microprofile.openapi.models.media.Schema schema = schemas.get(NameStrategyBean3.class.getSimpleName());
86-
assertEquals(expectedNameSet.size(), schema.getProperties().size());
87-
assertEquals(expectedNameSet, schema.getProperties().keySet());
89+
List<String> actualNameList = new ArrayList<>(schema.getProperties().keySet());
90+
Collections.sort(actualNameList, String::compareToIgnoreCase);
91+
92+
assertEquals(expectedNameList, actualNameList);
93+
}
94+
95+
@Test
96+
void testMethodNamePreserved() throws Exception {
97+
OpenAPI result = scan(NameStrategyBean4.class);
98+
assertJsonEquals("components.schemas.method-name-preserved.json", result);
8899
}
89100

90101
@Schema
@@ -114,18 +125,60 @@ static class NameStrategyKebab {
114125
static class NameStrategyBean3 {
115126
String simpleStringOne;
116127
Integer anotherField;
117-
BigDecimal Y;
128+
BigDecimal Y; // NOSONAR - naming intentional
118129
double z;
130+
String SOMEValue; // NOSONAR - naming intentional
131+
132+
public String getSimpleStringOne() {
133+
return simpleStringOne;
134+
}
135+
136+
public Integer getAnotherField() {
137+
return anotherField;
138+
}
139+
140+
public BigDecimal getY() {
141+
return Y;
142+
}
143+
144+
public double getZ() {
145+
return z;
146+
}
147+
148+
public String getSOMEValue() {
149+
return SOMEValue;
150+
}
119151
}
120152

121-
public static class NoValidTranslationMethods {
122-
public NoValidTranslationMethods() {
153+
@Schema
154+
static class NameStrategyBean4 {
155+
@Schema(name = "TESTValue", title = "Test Value")
156+
Integer TestValue; // NOSONAR - naming intentional
157+
158+
@Schema(name = "eValue", title = "e-Value")
159+
String EValue; // NOSONAR - naming intentional
160+
161+
@Schema(description = "Property for TestValue")
162+
public Integer getTESTValue() {
163+
return TestValue;
164+
}
165+
166+
@Schema(description = "Property for e-Value")
167+
public String geteValue() {
168+
return EValue;
123169
}
124170

171+
}
172+
173+
public static class NoValidTranslationMethods {
125174
public String translate() {
126175
return null;
127176
}
128177

178+
/**
179+
* @param v1 unused, demonstrated unsuitable translate method signature
180+
* @param v2 unused, demonstrated unsuitable translate method signature
181+
*/
129182
public String translate(String v1, String v2) {
130183
return null;
131184
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"openapi" : "3.1.0",
3+
"components" : {
4+
"schemas" : {
5+
"NameStrategyBean4" : {
6+
"type" : "object",
7+
"properties" : {
8+
"TESTValue" : {
9+
"type" : "integer",
10+
"format" : "int32",
11+
"description" : "Property for TestValue",
12+
"title" : "Test Value"
13+
},
14+
"eValue" : {
15+
"type" : "string",
16+
"description" : "Property for e-Value",
17+
"title" : "e-Value"
18+
}
19+
}
20+
}
21+
}
22+
}
23+
}

0 commit comments

Comments
 (0)