From 573e89987f66f9ebe92f3aec686030358bd1fbac Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Mon, 5 Dec 2022 14:47:51 +0100 Subject: [PATCH] Adapt to GraphQL over HTTP spec changes As of graphql/graphql-over-http#215, the official media type for GraphQL HTTP responses is now `"application/graphql-response+json"` instead of `"application/graphql+json"`. The latter is now deprecated and support will be removed in the future. This commit now favors the new media type. HTTP Clients are still supposed to send requests with the `"application/json"` content type. Closes gh-563 --- .../graphql/client/HttpGraphQlTransport.java | 7 ++++++- .../graphql/server/webflux/GraphQlHttpHandler.java | 7 ++++++- .../graphql/server/webmvc/GraphQlHttpHandler.java | 7 ++++++- .../graphql/client/WebGraphQlClientBuilderTests.java | 5 +++-- .../graphql/server/webflux/GraphQlHttpHandlerTests.java | 9 +++++---- .../graphql/server/webmvc/GraphQlHttpHandlerTests.java | 8 ++++---- 6 files changed, 30 insertions(+), 13 deletions(-) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/HttpGraphQlTransport.java b/spring-graphql/src/main/java/org/springframework/graphql/client/HttpGraphQlTransport.java index c82f8bbc..98f340d9 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/HttpGraphQlTransport.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/HttpGraphQlTransport.java @@ -44,6 +44,10 @@ final class HttpGraphQlTransport implements GraphQlTransport { private static final ParameterizedTypeReference> MAP_TYPE = new ParameterizedTypeReference>() {}; + // To be removed in favor of Framework's MediaType.APPLICATION_GRAPHQL_RESPONSE + private static final MediaType APPLICATION_GRAPHQL_RESPONSE = + new MediaType("application", "graphql-response+json"); + private final WebClient webClient; @@ -65,10 +69,11 @@ final class HttpGraphQlTransport implements GraphQlTransport { @Override + @SuppressWarnings("removal") public Mono execute(GraphQlRequest request) { return this.webClient.post() .contentType(this.contentType) - .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_GRAPHQL) + .accept(MediaType.APPLICATION_JSON, APPLICATION_GRAPHQL_RESPONSE, MediaType.APPLICATION_GRAPHQL) .bodyValue(request.toMap()) .retrieve() .bodyToMono(MAP_TYPE) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlHttpHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlHttpHandler.java index e3bccd29..1f0bf793 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlHttpHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlHttpHandler.java @@ -43,11 +43,16 @@ public class GraphQlHttpHandler { private static final Log logger = LogFactory.getLog(GraphQlHttpHandler.class); + // To be removed in favor of Framework's MediaType.APPLICATION_GRAPHQL_RESPONSE + private static final MediaType APPLICATION_GRAPHQL_RESPONSE = + new MediaType("application", "graphql-response+json"); + private static final ParameterizedTypeReference> MAP_PARAMETERIZED_TYPE_REF = new ParameterizedTypeReference>() {}; + @SuppressWarnings("removal") private static final List SUPPORTED_MEDIA_TYPES = - Arrays.asList(MediaType.APPLICATION_GRAPHQL, MediaType.APPLICATION_JSON); + Arrays.asList(APPLICATION_GRAPHQL_RESPONSE, MediaType.APPLICATION_JSON, MediaType.APPLICATION_GRAPHQL); private final WebGraphQlHandler graphQlHandler; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandler.java index 1189854f..52046f60 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandler.java @@ -55,8 +55,13 @@ public class GraphQlHttpHandler { private static final ParameterizedTypeReference> MAP_PARAMETERIZED_TYPE_REF = new ParameterizedTypeReference>() {}; + // To be removed in favor of Framework's MediaType.APPLICATION_GRAPHQL_RESPONSE + private static final MediaType APPLICATION_GRAPHQL_RESPONSE = + new MediaType("application", "graphql-response+json"); + + @SuppressWarnings("removal") private static final List SUPPORTED_MEDIA_TYPES = - Arrays.asList(MediaType.APPLICATION_GRAPHQL, MediaType.APPLICATION_JSON); + Arrays.asList(APPLICATION_GRAPHQL_RESPONSE, MediaType.APPLICATION_JSON, MediaType.APPLICATION_GRAPHQL); private final IdGenerator idGenerator = new AlternativeJdkIdGenerator(); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/client/WebGraphQlClientBuilderTests.java b/spring-graphql/src/test/java/org/springframework/graphql/client/WebGraphQlClientBuilderTests.java index 447f3532..444d5873 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/client/WebGraphQlClientBuilderTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/client/WebGraphQlClientBuilderTests.java @@ -189,13 +189,14 @@ public class WebGraphQlClientBuilderTests { @Test void contentTypeOverride() { + MediaType testMediaType = new MediaType("application", "graphql-request+json"); HttpBuilderSetup setup = new HttpBuilderSetup(); - setup.initBuilder().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_GRAPHQL_VALUE).build() + setup.initBuilder().header(HttpHeaders.CONTENT_TYPE, "application/graphql-request+json").build() .document(DOCUMENT).execute().block(TIMEOUT); WebGraphQlRequest request = setup.getActualRequest(); - assertThat(request.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_GRAPHQL); + assertThat(request.getHeaders().getContentType()).isEqualTo(testMediaType); } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlHttpHandlerTests.java b/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlHttpHandlerTests.java index 83484b66..4dd3c52b 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlHttpHandlerTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlHttpHandlerTests.java @@ -64,12 +64,12 @@ public class GraphQlHttpHandlerTests { @Test void shouldProduceApplicationGraphQl() { MockServerHttpRequest httpRequest = MockServerHttpRequest.post("/") - .contentType(MediaType.APPLICATION_GRAPHQL).accept(MediaType.APPLICATION_GRAPHQL).build(); + .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_GRAPHQL_RESPONSE).build(); MockServerHttpResponse httpResponse = handleRequest( httpRequest, this.greetingHandler, Collections.singletonMap("query", "{greeting}")); - assertThat(httpResponse.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_GRAPHQL); + assertThat(httpResponse.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_GRAPHQL_RESPONSE); } @Test @@ -90,7 +90,8 @@ public class GraphQlHttpHandlerTests { .toHttpHandlerWebFlux(); MockServerHttpRequest httpRequest = MockServerHttpRequest.post("/") - .contentType(MediaType.APPLICATION_GRAPHQL).accept(MediaType.APPLICATION_GRAPHQL).acceptLanguageAsLocales(Locale.FRENCH).build(); + .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_GRAPHQL_RESPONSE) + .acceptLanguageAsLocales(Locale.FRENCH).build(); MockServerHttpResponse httpResponse = handleRequest( httpRequest, handler, Collections.singletonMap("query", "{greeting}")); @@ -106,7 +107,7 @@ public class GraphQlHttpHandlerTests { .toHttpHandlerWebFlux(); MockServerHttpRequest httpRequest = MockServerHttpRequest.post("/") - .contentType(MediaType.APPLICATION_GRAPHQL).accept(MediaType.APPLICATION_GRAPHQL).build(); + .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_GRAPHQL_RESPONSE).build(); MockServerHttpResponse httpResponse = handleRequest( httpRequest, handler, Collections.singletonMap("query", "{showId}")); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandlerTests.java b/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandlerTests.java index 1a3e5713..19ee887e 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandlerTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandlerTests.java @@ -64,9 +64,9 @@ public class GraphQlHttpHandlerTests { @Test void shouldProduceApplicationGraphQl() throws Exception { - MockHttpServletRequest servletRequest = createServletRequest("{\"query\":\"{ greeting }\"}", MediaType.APPLICATION_GRAPHQL_VALUE); + MockHttpServletRequest servletRequest = createServletRequest("{\"query\":\"{ greeting }\"}", MediaType.APPLICATION_GRAPHQL_RESPONSE_VALUE); MockHttpServletResponse servletResponse = handleRequest(servletRequest, this.greetingHandler); - assertThat(servletResponse.getContentType()).isEqualTo(MediaType.APPLICATION_GRAPHQL_VALUE); + assertThat(servletResponse.getContentType()).isEqualTo(MediaType.APPLICATION_GRAPHQL_RESPONSE_VALUE); } @Test @@ -81,7 +81,7 @@ public class GraphQlHttpHandlerTests { GraphQlHttpHandler handler = GraphQlSetup.schemaContent("type Query { greeting: String }") .queryFetcher("greeting", (env) -> "Hello in " + env.getLocale()) .toHttpHandler(); - MockHttpServletRequest servletRequest = createServletRequest("{\"query\":\"{ greeting }\"}", MediaType.APPLICATION_GRAPHQL_VALUE); + MockHttpServletRequest servletRequest = createServletRequest("{\"query\":\"{ greeting }\"}", MediaType.APPLICATION_GRAPHQL_RESPONSE_VALUE); LocaleContextHolder.setLocale(Locale.FRENCH); try { @@ -101,7 +101,7 @@ public class GraphQlHttpHandlerTests { .queryFetcher("showId", (env) -> env.getExecutionId().toString()) .toHttpHandler(); - MockHttpServletRequest servletRequest = createServletRequest("{\"query\":\"{ showId }\"}", MediaType.APPLICATION_GRAPHQL_VALUE); + MockHttpServletRequest servletRequest = createServletRequest("{\"query\":\"{ showId }\"}", MediaType.APPLICATION_GRAPHQL_RESPONSE_VALUE); MockHttpServletResponse servletResponse = handleRequest(servletRequest, handler); DocumentContext document = JsonPath.parse(servletResponse.getContentAsString());