From c015dbcb15fb60df35441b411b04110dbd6c4294 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 4 Feb 2025 21:38:48 +0100 Subject: [PATCH] Add GraphQL response media type This commit introduces a static `MediaType` instance for `"application/graphql-response+json"`. Closes gh-1110 --- .../springframework/graphql/MediaTypes.java | 36 +++++++++++++++++++ .../graphql/client/HttpGraphQlTransport.java | 3 +- .../client/HttpSyncGraphQlTransport.java | 3 +- .../server/webflux/GraphQlHttpHandler.java | 3 +- .../webflux/GraphQlRequestPredicates.java | 3 +- .../server/webmvc/GraphQlHttpHandler.java | 3 +- .../webmvc/GraphQlRequestPredicates.java | 5 +-- .../webflux/GraphQlHttpHandlerTests.java | 11 +++--- .../GraphQlRequestPredicatesTests.java | 5 +-- .../webmvc/GraphQlHttpHandlerTests.java | 11 +++--- 10 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 spring-graphql/src/main/java/org/springframework/graphql/MediaTypes.java diff --git a/spring-graphql/src/main/java/org/springframework/graphql/MediaTypes.java b/spring-graphql/src/main/java/org/springframework/graphql/MediaTypes.java new file mode 100644 index 00000000..404f6038 --- /dev/null +++ b/spring-graphql/src/main/java/org/springframework/graphql/MediaTypes.java @@ -0,0 +1,36 @@ +/* + * Copyright 2020-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.graphql; + +import org.springframework.http.MediaType; + +/** + * Constants for well-known GraphQL media types. + * @author Brian Clozel + * @since 1.4.0 + */ +public abstract class MediaTypes { + + /** + * Standard media type for GraphQL responses over the HTTP protocol. + * @see + * GraphQL over HTTP specification + */ + public static final MediaType APPLICATION_GRAPHQL_RESPONSE = + MediaType.parseMediaType("application/graphql-response+json"); + +} 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 611e34f0..59635902 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 @@ -24,6 +24,7 @@ import reactor.core.publisher.Mono; import org.springframework.core.ParameterizedTypeReference; import org.springframework.graphql.GraphQlRequest; import org.springframework.graphql.GraphQlResponse; +import org.springframework.graphql.MediaTypes; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.codec.ServerSentEvent; @@ -75,7 +76,7 @@ final class HttpGraphQlTransport implements GraphQlTransport { public Mono execute(GraphQlRequest request) { return this.webClient.post() .contentType(this.contentType) - .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_GRAPHQL_RESPONSE, APPLICATION_GRAPHQL) + .accept(MediaType.APPLICATION_JSON, MediaTypes.APPLICATION_GRAPHQL_RESPONSE, APPLICATION_GRAPHQL) .bodyValue(request.toMap()) .attributes((attributes) -> { if (request instanceof ClientGraphQlRequest clientRequest) { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/HttpSyncGraphQlTransport.java b/spring-graphql/src/main/java/org/springframework/graphql/client/HttpSyncGraphQlTransport.java index df9d18be..66a965b6 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/HttpSyncGraphQlTransport.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/HttpSyncGraphQlTransport.java @@ -22,6 +22,7 @@ import java.util.Map; import org.springframework.core.ParameterizedTypeReference; import org.springframework.graphql.GraphQlRequest; import org.springframework.graphql.GraphQlResponse; +import org.springframework.graphql.MediaTypes; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.util.Assert; @@ -62,7 +63,7 @@ final class HttpSyncGraphQlTransport implements SyncGraphQlTransport { Map body = this.restClient.post() .contentType(this.contentType) - .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_GRAPHQL_RESPONSE) + .accept(MediaType.APPLICATION_JSON, MediaTypes.APPLICATION_GRAPHQL_RESPONSE) .body(request.toMap()) .retrieve() .body(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 537f3e0d..191928b4 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 @@ -20,6 +20,7 @@ import java.util.List; import reactor.core.publisher.Mono; +import org.springframework.graphql.MediaTypes; import org.springframework.graphql.server.WebGraphQlHandler; import org.springframework.graphql.server.WebGraphQlResponse; import org.springframework.http.MediaType; @@ -40,7 +41,7 @@ public class GraphQlHttpHandler extends AbstractGraphQlHttpHandler { new MediaType("application", "graphql+json"); private static final List SUPPORTED_MEDIA_TYPES = List.of( - MediaType.APPLICATION_GRAPHQL_RESPONSE, MediaType.APPLICATION_JSON, APPLICATION_GRAPHQL); + MediaTypes.APPLICATION_GRAPHQL_RESPONSE, MediaType.APPLICATION_JSON, APPLICATION_GRAPHQL); /** diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicates.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicates.java index 3f80729b..c478152e 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicates.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicates.java @@ -22,6 +22,7 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.graphql.MediaTypes; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; @@ -58,7 +59,7 @@ public final class GraphQlRequestPredicates { */ public static RequestPredicate graphQlHttp(String path) { return new GraphQlHttpRequestPredicate( - path, List.of(MediaType.APPLICATION_JSON, MediaType.APPLICATION_GRAPHQL_RESPONSE)); + path, List.of(MediaType.APPLICATION_JSON, MediaTypes.APPLICATION_GRAPHQL_RESPONSE)); } /** 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 9b35a556..7592d938 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 @@ -23,6 +23,7 @@ import java.util.concurrent.ExecutionException; import reactor.core.publisher.Mono; +import org.springframework.graphql.MediaTypes; import org.springframework.graphql.server.WebGraphQlHandler; import org.springframework.graphql.server.WebGraphQlResponse; import org.springframework.http.MediaType; @@ -45,7 +46,7 @@ public class GraphQlHttpHandler extends AbstractGraphQlHttpHandler { new MediaType("application", "graphql+json"); private static final List SUPPORTED_MEDIA_TYPES = List.of( - MediaType.APPLICATION_GRAPHQL_RESPONSE, MediaType.APPLICATION_JSON, APPLICATION_GRAPHQL); + MediaTypes.APPLICATION_GRAPHQL_RESPONSE, MediaType.APPLICATION_JSON, APPLICATION_GRAPHQL); /** diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicates.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicates.java index a6667e3a..a3190777 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicates.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicates.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.graphql.MediaTypes; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; @@ -58,7 +59,7 @@ public final class GraphQlRequestPredicates { */ public static RequestPredicate graphQlHttp(String path) { return new GraphQlHttpRequestPredicate( - path, List.of(MediaType.APPLICATION_JSON, MediaType.APPLICATION_GRAPHQL_RESPONSE)); + path, List.of(MediaType.APPLICATION_JSON, MediaTypes.APPLICATION_GRAPHQL_RESPONSE)); } /** 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 e8648e8f..ccd88cff 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 @@ -32,6 +32,7 @@ import org.springframework.core.codec.DataBufferEncoder; import org.springframework.core.io.buffer.DefaultDataBuffer; import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.graphql.GraphQlSetup; +import org.springframework.graphql.MediaTypes; import org.springframework.graphql.server.WebGraphQlHandler; import org.springframework.graphql.server.support.SerializableGraphQlRequest; import org.springframework.http.MediaType; @@ -119,12 +120,12 @@ public class GraphQlHttpHandlerTests { void shouldProduceApplicationGraphQl() throws Exception { MockServerHttpRequest httpRequest = MockServerHttpRequest.post("/") .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_GRAPHQL_RESPONSE) + .accept(MediaTypes.APPLICATION_GRAPHQL_RESPONSE) .body(initRequestBody("{greeting}")); MockServerHttpResponse httpResponse = handleRequest(httpRequest, this.greetingHandler); - assertThat(httpResponse.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_GRAPHQL_RESPONSE); + assertThat(httpResponse.getHeaders().getContentType()).isEqualTo(MediaTypes.APPLICATION_GRAPHQL_RESPONSE); } @Test @@ -147,7 +148,7 @@ public class GraphQlHttpHandlerTests { MockServerHttpRequest httpRequest = MockServerHttpRequest.post("/") .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_GRAPHQL_RESPONSE) + .accept(MediaTypes.APPLICATION_GRAPHQL_RESPONSE) .acceptLanguageAsLocales(Locale.FRENCH) .body(initRequestBody("{greeting}")); @@ -165,7 +166,7 @@ public class GraphQlHttpHandlerTests { MockServerHttpRequest httpRequest = MockServerHttpRequest.post("/") .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_GRAPHQL_RESPONSE) + .accept(MediaTypes.APPLICATION_GRAPHQL_RESPONSE) .body(initRequestBody("{showId}")); MockServerHttpResponse httpResponse = handleRequest(httpRequest, handler); @@ -191,7 +192,7 @@ public class GraphQlHttpHandlerTests { MockServerHttpRequest httpRequest = MockServerHttpRequest.post("/") .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_GRAPHQL_RESPONSE) + .accept(MediaTypes.APPLICATION_GRAPHQL_RESPONSE) .body(body); MockServerWebExchange exchange = MockServerWebExchange.from(httpRequest); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicatesTests.java b/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicatesTests.java index 1f20a1f6..a8be2b9b 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicatesTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicatesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import java.util.Collections; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.springframework.graphql.MediaTypes; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; @@ -131,7 +132,7 @@ class GraphQlRequestPredicatesTests { private MockServerWebExchange createMatchingHttpExchange() { MockServerHttpRequest request = MockServerHttpRequest.post("/graphql") .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_GRAPHQL_RESPONSE) + .accept(MediaType.APPLICATION_JSON, MediaTypes.APPLICATION_GRAPHQL_RESPONSE) .build(); return MockServerWebExchange.from(request); } 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 d9792f41..c7962471 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 @@ -34,6 +34,7 @@ import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.graphql.GraphQlSetup; +import org.springframework.graphql.MediaTypes; import org.springframework.graphql.server.WebGraphQlHandler; import org.springframework.graphql.server.support.SerializableGraphQlRequest; import org.springframework.http.MediaType; @@ -91,9 +92,9 @@ public class GraphQlHttpHandlerTests { @Test void shouldProduceApplicationGraphQl() throws Exception { - MockHttpServletRequest request = createServletRequest("{ greeting }", MediaType.APPLICATION_GRAPHQL_RESPONSE_VALUE); + MockHttpServletRequest request = createServletRequest("{ greeting }", MediaTypes.APPLICATION_GRAPHQL_RESPONSE.toString()); MockHttpServletResponse response = handleRequest(request, this.greetingHandler); - assertThat(response.getContentType()).isEqualTo(MediaType.APPLICATION_GRAPHQL_RESPONSE_VALUE); + assertThat(response.getContentType()).isEqualTo(MediaTypes.APPLICATION_GRAPHQL_RESPONSE.toString()); } @Test @@ -110,7 +111,7 @@ public class GraphQlHttpHandlerTests { .toHttpHandler(); MockHttpServletRequest request = createServletRequest( - "{ greeting }", MediaType.APPLICATION_GRAPHQL_RESPONSE_VALUE); + "{ greeting }", MediaTypes.APPLICATION_GRAPHQL_RESPONSE.toString()); LocaleContextHolder.setLocale(Locale.FRENCH); @@ -130,7 +131,7 @@ public class GraphQlHttpHandlerTests { .toHttpHandler(); MockHttpServletRequest request = createServletRequest( - "{ showId }", MediaType.APPLICATION_GRAPHQL_RESPONSE_VALUE); + "{ showId }", MediaTypes.APPLICATION_GRAPHQL_RESPONSE.toString()); MockHttpServletResponse response = handleRequest(request, handler); @@ -145,7 +146,7 @@ public class GraphQlHttpHandlerTests { .queryFetcher("greeting", (env) -> "Hello").toWebGraphQlHandler(); GraphQlHttpHandler handler = new GraphQlHttpHandler(webGraphQlHandler, new MappingJackson2HttpMessageConverter()); - MockHttpServletRequest servletRequest = createServletRequest("{ greeting }", MediaType.APPLICATION_GRAPHQL_RESPONSE_VALUE); + MockHttpServletRequest servletRequest = createServletRequest("{ greeting }", MediaTypes.APPLICATION_GRAPHQL_RESPONSE.toString()); ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); ServerResponse response = handler.handleRequest(request);