Skip to content

Commit e11a18e

Browse files
authored
fix: deserialize a JsonError as a GenericResource (#10625)
This pull request adds the JsonError test to verify JsonMapper can deserialize a JsonError as a GenericResource introduced in #10526 as a TCK test.
1 parent f070f7c commit e11a18e

File tree

2 files changed

+91
-3
lines changed

2 files changed

+91
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2017-2024 original authors
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+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.micronaut.http.server.tck.tests.hateoas;
17+
18+
import io.micronaut.http.hateoas.JsonError;
19+
import io.micronaut.http.hateoas.Link;
20+
import io.micronaut.http.hateoas.Resource;
21+
import io.micronaut.http.tck.ServerUnderTest;
22+
import io.micronaut.http.tck.ServerUnderTestProviderUtils;
23+
import io.micronaut.json.JsonMapper;
24+
import org.junit.jupiter.api.Test;
25+
26+
import java.io.IOException;
27+
import java.util.Collections;
28+
29+
import static org.junit.jupiter.api.Assertions.assertEquals;
30+
import static org.junit.jupiter.api.Assertions.assertFalse;
31+
import static org.junit.jupiter.api.Assertions.assertNotNull;
32+
import static org.junit.jupiter.api.Assertions.assertTrue;
33+
34+
@SuppressWarnings({
35+
"java:S5960", // We're allowed assertions, as these are used in tests only
36+
"checkstyle:MissingJavadocType",
37+
"checkstyle:DesignForExtension"
38+
})
39+
public class JsonErrorSerdeTest {
40+
41+
private static final String JSON_ERROR = """
42+
{"_links":{"self":[{"href":"/resolve","templated":false}]},"_embedded":{"errors":[{"message":"Internal Server Error: Something bad happened"}]},"message":"Internal Server Error"}""";
43+
private static final String SPEC_NAME = "JsonErrorSerdeTest";
44+
45+
/**
46+
* @throws IOException Exception thrown while getting the server under test.
47+
*/
48+
@Test
49+
void canDeserializeAJsonErrorAsAGenericResource() throws IOException {
50+
try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer(SPEC_NAME, Collections.emptyMap())) {
51+
JsonMapper jsonMapper = server.getApplicationContext().getBean(JsonMapper.class);
52+
//when:
53+
Resource resource = jsonMapper.readValue(JSON_ERROR, Resource.class);
54+
//then:
55+
testResource(resource);
56+
}
57+
}
58+
59+
/**
60+
* @throws IOException Exception thrown while getting the server under test.
61+
*/
62+
@Test
63+
void jsonErrorShouldBeDeserializableFromAString() throws IOException {
64+
try (ServerUnderTest server = ServerUnderTestProviderUtils.getServerUnderTestProvider().getServer(SPEC_NAME, Collections.emptyMap())) {
65+
JsonMapper jsonMapper = server.getApplicationContext().getBean(JsonMapper.class);
66+
//when:
67+
JsonError jsonError = jsonMapper.readValue(JSON_ERROR, JsonError.class);
68+
//then:
69+
testResource(jsonError);
70+
}
71+
}
72+
73+
private <T extends Resource> void testResource(T resource) {
74+
assertNotNull(resource);
75+
assertTrue(resource.getEmbedded().getFirst("errors").isPresent(), "errors should be present");
76+
assertTrue(resource.getLinks().getFirst("self").isPresent(), "self link should be present");
77+
assertEquals("/resolve", resource.getLinks().getFirst("self").map(Link::getHref).orElse(null));
78+
assertFalse(resource.getLinks().getFirst("self").map(Link::isTemplated).orElse(true), "self link should not be templated");
79+
}
80+
}

http/src/main/java/io/micronaut/http/hateoas/AbstractResource.java

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017-2020 original authors
2+
* Copyright 2017-2024 original authors
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,16 +18,17 @@
1818
import com.fasterxml.jackson.annotation.JsonProperty;
1919
import io.micronaut.core.annotation.Internal;
2020
import io.micronaut.core.annotation.Introspected;
21+
import io.micronaut.core.annotation.Nullable;
2122
import io.micronaut.core.annotation.ReflectiveAccess;
2223
import io.micronaut.core.convert.value.ConvertibleValues;
2324
import io.micronaut.core.util.StringUtils;
2425
import io.micronaut.core.value.OptionalMultiValues;
2526
import io.micronaut.http.MediaType;
2627
import io.micronaut.http.annotation.Produces;
2728

28-
import io.micronaut.core.annotation.Nullable;
2929
import java.util.ArrayList;
3030
import java.util.Arrays;
31+
import java.util.Collection;
3132
import java.util.LinkedHashMap;
3233
import java.util.List;
3334
import java.util.Map;
@@ -42,7 +43,8 @@
4243
*/
4344
@Produces(MediaType.APPLICATION_HAL_JSON)
4445
@Introspected
45-
public abstract class AbstractResource<Impl extends AbstractResource> implements Resource {
46+
@SuppressWarnings("java:S119") // Impl is a better name than T
47+
public abstract class AbstractResource<Impl extends AbstractResource<Impl>> implements Resource {
4648

4749
private final Map<CharSequence, List<Link>> linkMap = new LinkedHashMap<>(1);
4850
private final Map<CharSequence, List<Resource>> embeddedMap = new LinkedHashMap<>(1);
@@ -150,6 +152,12 @@ public final void setLinks(Map<String, Object> links) {
150152
if (value instanceof Map) {
151153
Map<String, Object> linkMap = (Map<String, Object>) value;
152154
link(name, linkMap);
155+
} else if (value instanceof Collection<?> collection) {
156+
for (Object o : collection) {
157+
if (o instanceof Map aMap) {
158+
link(name, aMap);
159+
}
160+
}
153161
}
154162
}
155163
}

0 commit comments

Comments
 (0)