From 162038932f1890da28ffd5f7c25210dda9e92460 Mon Sep 17 00:00:00 2001 From: Caio Guedes Date: Fri, 10 Nov 2023 11:23:20 +0100 Subject: [PATCH] regression: support JSON Merge Patch (#10089) --- .../http/netty/body/NettyJsonHandler.java | 2 + .../java/io/micronaut/http/MediaType.java | 13 ++++++ .../io/micronaut/http/MediaTypeSpec.groovy | 1 + .../json/body/JsonMessageHandler.java | 6 ++- .../json/codec/JsonMediaTypeCodec.java | 1 + .../docs/jsonpatch/JsonMergePatchTest.java | 45 +++++++++++++++++++ 6 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 test-suite/src/test/java/io/micronaut/docs/jsonpatch/JsonMergePatchTest.java diff --git a/http-netty/src/main/java/io/micronaut/http/netty/body/NettyJsonHandler.java b/http-netty/src/main/java/io/micronaut/http/netty/body/NettyJsonHandler.java index 5973a67dade..e139d06a880 100644 --- a/http-netty/src/main/java/io/micronaut/http/netty/body/NettyJsonHandler.java +++ b/http-netty/src/main/java/io/micronaut/http/netty/body/NettyJsonHandler.java @@ -58,6 +58,7 @@ MediaType.APPLICATION_JSON_GITHUB, MediaType.APPLICATION_JSON_FEED, MediaType.APPLICATION_JSON_PATCH, + MediaType.APPLICATION_JSON_MERGE_PATCH, MediaType.APPLICATION_JSON_PROBLEM }) @Consumes({ @@ -67,6 +68,7 @@ MediaType.APPLICATION_JSON_GITHUB, MediaType.APPLICATION_JSON_FEED, MediaType.APPLICATION_JSON_PATCH, + MediaType.APPLICATION_JSON_MERGE_PATCH, MediaType.APPLICATION_JSON_PROBLEM }) @BootstrapContextCompatible diff --git a/http/src/main/java/io/micronaut/http/MediaType.java b/http/src/main/java/io/micronaut/http/MediaType.java index c086e6fdf95..c50d1b72193 100644 --- a/http/src/main/java/io/micronaut/http/MediaType.java +++ b/http/src/main/java/io/micronaut/http/MediaType.java @@ -193,6 +193,17 @@ public class MediaType implements CharSequence { */ public static final MediaType APPLICATION_JSON_PATCH_TYPE = new MediaType(MediaType.APPLICATION_JSON_PATCH); + /** + * @see JSON Merge Patch + * JSON Merge Patch: application/merge-patch+json + */ + public static final String APPLICATION_JSON_MERGE_PATCH = "application/merge-patch+json"; + + /** + * JSON Merge Patch: application/merge-patch+json + */ + public static final MediaType APPLICATION_JSON_MERGE_PATCH_TYPE = new MediaType(MediaType.APPLICATION_JSON_MERGE_PATCH); + /** * JSON Feed: application/problem+json. */ @@ -584,6 +595,8 @@ public static MediaType of(String mediaType) { return APPLICATION_JSON_GITHUB_TYPE; case APPLICATION_JSON_PATCH: return APPLICATION_JSON_PATCH_TYPE; + case APPLICATION_JSON_MERGE_PATCH: + return APPLICATION_JSON_MERGE_PATCH_TYPE; case APPLICATION_JSON_PROBLEM: return APPLICATION_JSON_PROBLEM_TYPE; case APPLICATION_YAML: diff --git a/http/src/test/groovy/io/micronaut/http/MediaTypeSpec.groovy b/http/src/test/groovy/io/micronaut/http/MediaTypeSpec.groovy index e38acd0f534..c4ee8eda5bd 100644 --- a/http/src/test/groovy/io/micronaut/http/MediaTypeSpec.groovy +++ b/http/src/test/groovy/io/micronaut/http/MediaTypeSpec.groovy @@ -34,6 +34,7 @@ class MediaTypeSpec extends Specification { MediaType.of('application/feed+json') == MediaType.APPLICATION_JSON_FEED_TYPE MediaType.of('application/problem+json') == MediaType.APPLICATION_JSON_PROBLEM_TYPE MediaType.of('application/json-patch+json') == MediaType.APPLICATION_JSON_PATCH_TYPE + MediaType.of('application/merge-patch+json') == MediaType.APPLICATION_JSON_MERGE_PATCH_TYPE } void "test media type"() { diff --git a/json-core/src/main/java/io/micronaut/json/body/JsonMessageHandler.java b/json-core/src/main/java/io/micronaut/json/body/JsonMessageHandler.java index c7ca0841f2a..47d8331e02f 100644 --- a/json-core/src/main/java/io/micronaut/json/body/JsonMessageHandler.java +++ b/json-core/src/main/java/io/micronaut/json/body/JsonMessageHandler.java @@ -54,7 +54,8 @@ MediaType.APPLICATION_JSON_GITHUB, MediaType.APPLICATION_JSON_FEED, MediaType.APPLICATION_JSON_PROBLEM, - MediaType.APPLICATION_JSON_PATCH + MediaType.APPLICATION_JSON_PATCH, + MediaType.APPLICATION_JSON_MERGE_PATCH }) @Consumes({ MediaType.APPLICATION_JSON, @@ -63,7 +64,8 @@ MediaType.APPLICATION_JSON_GITHUB, MediaType.APPLICATION_JSON_FEED, MediaType.APPLICATION_JSON_PROBLEM, - MediaType.APPLICATION_JSON_PATCH + MediaType.APPLICATION_JSON_PATCH, + MediaType.APPLICATION_JSON_MERGE_PATCH }) @BootstrapContextCompatible public final class JsonMessageHandler implements MessageBodyHandler { diff --git a/json-core/src/main/java/io/micronaut/json/codec/JsonMediaTypeCodec.java b/json-core/src/main/java/io/micronaut/json/codec/JsonMediaTypeCodec.java index a84deb164d3..cc3294ef0b8 100644 --- a/json-core/src/main/java/io/micronaut/json/codec/JsonMediaTypeCodec.java +++ b/json-core/src/main/java/io/micronaut/json/codec/JsonMediaTypeCodec.java @@ -50,6 +50,7 @@ public class JsonMediaTypeCodec extends MapperMediaTypeCodec { MediaType.APPLICATION_JSON_GITHUB_TYPE, MediaType.APPLICATION_JSON_FEED_TYPE, MediaType.APPLICATION_JSON_PATCH_TYPE, + MediaType.APPLICATION_JSON_MERGE_PATCH_TYPE, MediaType.APPLICATION_JSON_PROBLEM_TYPE ); diff --git a/test-suite/src/test/java/io/micronaut/docs/jsonpatch/JsonMergePatchTest.java b/test-suite/src/test/java/io/micronaut/docs/jsonpatch/JsonMergePatchTest.java new file mode 100644 index 00000000000..1d798a1bfc0 --- /dev/null +++ b/test-suite/src/test/java/io/micronaut/docs/jsonpatch/JsonMergePatchTest.java @@ -0,0 +1,45 @@ +package io.micronaut.docs.jsonpatch; + +import io.micronaut.context.annotation.Property; +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.annotation.Introspected; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.*; +import io.micronaut.http.client.HttpClient; +import io.micronaut.http.client.annotation.Client; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +@Property(name = "spec.name", value = "JsonMergePatchTest") +@MicronautTest +public class JsonMergePatchTest { + @Test + void acceptMergePatchJsonContentType(@Client("/") HttpClient client) { + String expected, body = expected = "{\"f\":\"foo\",\"b\":\"bar\"}"; + String response = assertDoesNotThrow(() -> client.toBlocking().retrieve( + HttpRequest.PATCH("/json-merge-patch", body) + .contentType("application/merge-patch+json") + .accept("application/merge-patch+json") + )); + assertNotNull(response); + assertEquals(expected, response); + } + + @Requires(property = "spec.name", value = "JsonMergePatchTest") + @Controller("/json-merge-patch") + static class JsonMergePatchController { + @Consumes(MediaType.APPLICATION_JSON_MERGE_PATCH) + @Produces(MediaType.APPLICATION_JSON_MERGE_PATCH) + @Patch + MergePatch mergePatch(@Body MergePatch body) { + return body; + } + } + + @Introspected + record MergePatch(String f, String b) { + } +}