Skip to content

Commit ad6ec3f

Browse files
committed
refactor: Address SonarQube findings on TypeMapper/RequestHandler change
1 parent f124828 commit ad6ec3f

6 files changed

Lines changed: 85 additions & 48 deletions

File tree

src/main/java/com/retailsvc/http/internal/RequestPreparationFilter.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
public final class RequestPreparationFilter extends Filter {
2525

26+
private static final String BODY_POINTER = "/body";
27+
2628
private final Spec spec;
2729
private final Router router;
2830
private final Validator validator;
@@ -126,7 +128,7 @@ private Object validateAndParseBody(HttpExchange exchange, Operation op, byte[]
126128
if (body.length == 0) {
127129
if (rb.get().required()) {
128130
throw new ValidationException(
129-
new ValidationError("/body", "required", "request body is required", null));
131+
new ValidationError(BODY_POINTER, "required", "request body is required", null));
130132
}
131133
return null;
132134
}
@@ -136,13 +138,13 @@ private Object validateAndParseBody(HttpExchange exchange, Operation op, byte[]
136138
if (mt == null) {
137139
throw new ValidationException(
138140
new ValidationError(
139-
"/body", "content-type", "unsupported content type: " + mediaType, null));
141+
BODY_POINTER, "content-type", "unsupported content type: " + mediaType, null));
140142
}
141143
TypeMapper mapper = bodyMappers.get(mediaType);
142144
if (mapper == null) {
143145
throw new ValidationException(
144146
new ValidationError(
145-
"/body", "content-type", "unsupported content type: " + mediaType, null));
147+
BODY_POINTER, "content-type", "unsupported content type: " + mediaType, null));
146148
}
147149
Object parsed = mapper.readFrom(body, header);
148150
if (mediaType.equals("application/x-www-form-urlencoded") && parsed instanceof Map<?, ?> map) {

src/main/java/com/retailsvc/http/internal/gson/GsonJsonMapper.java

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -70,39 +70,56 @@ public byte[] writeTo(Object value) {
7070
private static Object toJavaObject(JsonElement element) {
7171
if (element == null || element instanceof JsonNull) {
7272
return null;
73-
} else if (element instanceof JsonObject obj) {
74-
Map<String, Object> map = new LinkedHashMap<>();
75-
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
76-
map.put(entry.getKey(), toJavaObject(entry.getValue()));
77-
}
78-
return map;
79-
} else if (element instanceof JsonArray arr) {
80-
List<Object> list = new ArrayList<>(arr.size());
81-
for (JsonElement item : arr) {
82-
list.add(toJavaObject(item));
83-
}
84-
return list;
85-
} else if (element instanceof JsonPrimitive prim) {
86-
if (prim.isBoolean()) {
87-
return prim.getAsBoolean();
88-
} else if (prim.isString()) {
89-
return prim.getAsString();
90-
} else {
91-
// Number
92-
String raw = prim.getAsString();
93-
if (raw.indexOf('.') < 0 && raw.indexOf('e') < 0 && raw.indexOf('E') < 0) {
94-
try {
95-
return Long.parseLong(raw);
96-
} catch (NumberFormatException _) {
97-
// falls through to Double for out-of-range integers
98-
}
99-
}
100-
return Double.parseDouble(raw);
101-
}
73+
}
74+
if (element instanceof JsonObject obj) {
75+
return toMap(obj);
76+
}
77+
if (element instanceof JsonArray arr) {
78+
return toList(arr);
79+
}
80+
if (element instanceof JsonPrimitive prim) {
81+
return toPrimitive(prim);
10282
}
10383
throw new IllegalStateException("Unexpected JsonElement type: " + element.getClass());
10484
}
10585

86+
private static Map<String, Object> toMap(JsonObject obj) {
87+
Map<String, Object> map = new LinkedHashMap<>();
88+
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
89+
map.put(entry.getKey(), toJavaObject(entry.getValue()));
90+
}
91+
return map;
92+
}
93+
94+
private static List<Object> toList(JsonArray arr) {
95+
List<Object> list = new ArrayList<>(arr.size());
96+
for (JsonElement item : arr) {
97+
list.add(toJavaObject(item));
98+
}
99+
return list;
100+
}
101+
102+
private static Object toPrimitive(JsonPrimitive prim) {
103+
if (prim.isBoolean()) {
104+
return prim.getAsBoolean();
105+
}
106+
if (prim.isString()) {
107+
return prim.getAsString();
108+
}
109+
return toNumber(prim.getAsString());
110+
}
111+
112+
private static Object toNumber(String raw) {
113+
if (raw.indexOf('.') < 0 && raw.indexOf('e') < 0 && raw.indexOf('E') < 0) {
114+
try {
115+
return Long.parseLong(raw);
116+
} catch (NumberFormatException _) {
117+
// Falls through to Double for out-of-Long-range integers.
118+
}
119+
}
120+
return Double.parseDouble(raw);
121+
}
122+
106123
private static <T> TypeAdapter<T> isoStringWriter(Function<T, String> toIso) {
107124
return new TypeAdapter<T>() {
108125
@Override

src/test/java/com/retailsvc/http/OpenApiServerTest.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,28 @@ void shouldStartHttpServerWithValidConfiguration() {
2929

3030
@Test
3131
void shouldThrowExceptionWhenSpecIsNull() {
32-
assertThatThrownBy(() -> OpenApiServer.builder().handlers(emptyMap()).port(0).build())
32+
OpenApiServer.Builder builder = OpenApiServer.builder().handlers(emptyMap()).port(0);
33+
34+
assertThatThrownBy(builder::build)
3335
.isInstanceOf(NullPointerException.class)
3436
.hasMessageContaining("Spec must not be null");
3537
}
3638

3739
@Test
3840
void shouldThrowExceptionWhenHandlersMapIsNull() {
39-
Spec validSpec = testSpec();
41+
OpenApiServer.Builder builder = OpenApiServer.builder().spec(testSpec()).port(0);
4042

41-
assertThatThrownBy(() -> OpenApiServer.builder().spec(validSpec).port(0).build())
43+
assertThatThrownBy(builder::build)
4244
.isInstanceOf(NullPointerException.class)
4345
.hasMessageContaining("handlers must not be null");
4446
}
4547

4648
@Test
4749
void testExceptionIsThrownOnInvalidHttpPort() {
48-
Spec validSpec = testSpec();
50+
OpenApiServer.Builder builder =
51+
OpenApiServer.builder().spec(testSpec()).handlers(emptyMap()).port(-1);
4952

50-
assertThatThrownBy(
51-
() -> OpenApiServer.builder().spec(validSpec).handlers(emptyMap()).port(-1).build())
52-
.isInstanceOf(IllegalArgumentException.class);
53+
assertThatThrownBy(builder::build).isInstanceOf(IllegalArgumentException.class);
5354
}
5455

5556
private Spec testSpec() {

src/test/java/com/retailsvc/http/TypeMapperRegistrationTest.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.net.http.HttpRequest.BodyPublishers;
1313
import java.nio.charset.StandardCharsets;
1414
import java.util.Map;
15+
import java.util.concurrent.atomic.AtomicBoolean;
1516
import org.junit.jupiter.api.Test;
1617

1718
class TypeMapperRegistrationTest extends ServerBaseTest {
@@ -52,10 +53,12 @@ void gsonFallbackIsAutoRegisteredWhenNoJsonMapperConfigured() throws Exception {
5253

5354
@Test
5455
void userSuppliedMapperOverridesDefault() throws Exception {
56+
AtomicBoolean readFromCalled = new AtomicBoolean();
5557
TypeMapper marker =
5658
new TypeMapper() {
5759
@Override
5860
public Object readFrom(byte[] b, String h) {
61+
readFromCalled.set(true);
5962
return Map.of("aList", java.util.List.of("x"), "feelingGood", true);
6063
}
6164

@@ -65,20 +68,31 @@ public byte[] writeTo(Object v) {
6568
}
6669
};
6770
RequestHandler echo = req -> req.respond(200).empty();
68-
OpenApiServer s =
71+
server =
6972
OpenApiServer.builder()
7073
.spec(spec)
7174
.bodyMapper("application/json", marker)
7275
.handlers(Map.of("get-data", echo, "post-data", echo))
7376
.port(0)
7477
.build();
75-
s.close();
78+
HttpClient.newHttpClient()
79+
.send(
80+
HttpRequest.newBuilder()
81+
.uri(URI.create("http://localhost:%d/api/v1/data".formatted(server.listenPort())))
82+
.header("Content-Type", "application/json")
83+
.POST(BodyPublishers.ofString("{\"anything\":\"goes\"}"))
84+
.build(),
85+
ofString());
86+
87+
assertThat(readFromCalled).isTrue();
7688
}
7789

7890
@Test
7991
void bodyMapperRejectsNullArgs() {
80-
var b = OpenApiServer.builder();
81-
assertThatThrownBy(() -> b.bodyMapper(null, new GsonOnlyMapper()))
92+
OpenApiServer.Builder b = OpenApiServer.builder();
93+
TypeMapper anyMapper = new GsonOnlyMapper();
94+
95+
assertThatThrownBy(() -> b.bodyMapper(null, anyMapper))
8296
.isInstanceOf(NullPointerException.class);
8397
assertThatThrownBy(() -> b.bodyMapper("application/json", null))
8498
.isInstanceOf(NullPointerException.class);

src/test/java/com/retailsvc/http/internal/FormTypeMapperTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ void readsKeyValuePairs() {
2323

2424
@Test
2525
void writeToIsUnsupported() {
26-
assertThatThrownBy(() -> mapper.writeTo(Map.of("k", "v")))
26+
Map<String, String> value = Map.of("k", "v");
27+
28+
assertThatThrownBy(() -> mapper.writeTo(value))
2729
.isInstanceOf(UnsupportedOperationException.class);
2830
}
2931
}

src/test/java/com/retailsvc/http/internal/gson/GsonJsonMapperTest.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@ void readBasicTypes() {
4141
(Map<String, Object>)
4242
mapper.readFrom(
4343
bytes("{\"s\":\"hi\",\"b\":true,\"n\":null,\"a\":[1,2]}"), "application/json");
44-
assertThat(parsed.get("s")).isEqualTo("hi");
45-
assertThat(parsed.get("b")).isEqualTo(Boolean.TRUE);
46-
assertThat(parsed.get("n")).isNull();
47-
assertThat(parsed.get("a")).isEqualTo(List.of(1L, 2L));
44+
assertThat(parsed)
45+
.containsEntry("s", "hi")
46+
.containsEntry("b", Boolean.TRUE)
47+
.containsEntry("n", null)
48+
.containsEntry("a", List.of(1L, 2L));
4849
}
4950

5051
@Test

0 commit comments

Comments
 (0)