diff --git a/graphql-spqr-spring-boot-autoconfigure/src/main/java/io/leangen/graphql/spqr/spring/web/GraphQLController.java b/graphql-spqr-spring-boot-autoconfigure/src/main/java/io/leangen/graphql/spqr/spring/web/GraphQLController.java index 5e5d6e1..b7159a9 100644 --- a/graphql-spqr-spring-boot-autoconfigure/src/main/java/io/leangen/graphql/spqr/spring/web/GraphQLController.java +++ b/graphql-spqr-spring-boot-autoconfigure/src/main/java/io/leangen/graphql/spqr/spring/web/GraphQLController.java @@ -1,30 +1,36 @@ package io.leangen.graphql.spqr.spring.web; +import java.util.Collections; +import java.util.Map; + import graphql.GraphQL; +import io.leangen.geantyref.GenericTypeReflector; +import io.leangen.graphql.execution.GlobalEnvironment; +import io.leangen.graphql.generator.mapping.ConverterRegistry; +import io.leangen.graphql.metadata.strategy.type.DefaultTypeInfoGenerator; +import io.leangen.graphql.metadata.strategy.value.ValueMapper; import io.leangen.graphql.spqr.spring.web.dto.ExecutorParams; import io.leangen.graphql.spqr.spring.web.dto.GraphQLRequest; import io.leangen.graphql.spqr.spring.web.dto.TransportType; +import io.leangen.graphql.util.Defaults; import io.leangen.graphql.util.Utils; import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import java.util.Map; +import org.springframework.web.bind.annotation.*; @RestController public abstract class GraphQLController { protected final GraphQL graphQL; protected final GraphQLExecutor executor; + private final ValueMapper valueMapper; public GraphQLController(GraphQL graphQL, GraphQLExecutor executor) { this.graphQL = graphQL; this.executor = executor; + this.valueMapper = Defaults.valueMapperFactory(new DefaultTypeInfoGenerator()).getValueMapper( + Collections.emptyMap(), + new GlobalEnvironment(null, null, null, new ConverterRegistry(Collections.emptyList(), Collections.emptyList()), null, null, null, null) + ); } @PostMapping( @@ -33,9 +39,15 @@ public GraphQLController(GraphQL graphQL, GraphQLExecutor executor) { produces = MediaType.APPLICATION_JSON_VALUE ) public Object executeJsonPost(@RequestBody GraphQLRequest requestBody, - GraphQLRequest requestParams, + @RequestParam(value = "query", required = false) String requestQuery, + @RequestParam(value = "operationName", required = false) String requestOperationName, + @RequestParam(value = "variables", required = false) String variablesJsonString, R request) { - return jsonPost(requestBody, requestParams, request, TransportType.HTTP); + String id = requestBody.getId(); + String query = requestQuery == null ? requestBody.getQuery() : requestQuery; + String operationName = requestOperationName == null ? requestBody.getOperationName() : requestOperationName; + Map variables = parseJsonString(variablesJsonString); + return jsonPost(requestBody, new GraphQLRequest(id, query, operationName, variables), request, TransportType.HTTP); } @PostMapping( @@ -71,8 +83,7 @@ public Object executeGraphQLPost(@RequestBody String queryBody, return executor.execute(graphQL, params); } - @RequestMapping( - method = RequestMethod.POST, + @PostMapping( value = "${graphql.spqr.http.endpoint:/graphql}", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE, "application/x-www-form-urlencoded;charset=UTF-8"}, produces = MediaType.APPLICATION_JSON_VALUE @@ -98,7 +109,14 @@ public Object executeFormPost(@RequestParam Map queryParams, produces = MediaType.APPLICATION_JSON_VALUE, headers = { "Connection!=Upgrade", "Connection!=keep-alive, Upgrade" } ) - public Object executeGet(GraphQLRequest graphQLRequest, R request) { + @ResponseBody + public Object executeGet(@RequestParam(value = "query") String requestQuery, + @RequestParam(value = "operationName", required = false) String requestOperationName, + @RequestParam(value = "variables", required = false) String variablesJsonString, + R request) + { + Map variables = parseJsonString(variablesJsonString); + GraphQLRequest graphQLRequest = new GraphQLRequest(null, requestQuery, requestOperationName, variables); return get(graphQLRequest, request, TransportType.HTTP); } @@ -114,4 +132,8 @@ public Object executeGetEventStream(GraphQLRequest graphQLRequest, R request) { private Object get(GraphQLRequest graphQLRequest, R request, TransportType transportType) { return executor.execute(graphQL, new ExecutorParams<>(graphQLRequest, request, transportType)); } + + private Map parseJsonString(String json) { + return valueMapper.fromString(json, GenericTypeReflector.annotate(Map.class)); + } } diff --git a/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/test/ResolverBuilder_TestConfig.java b/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/test/ResolverBuilder_TestConfig.java index 977957e..f2b3060 100644 --- a/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/test/ResolverBuilder_TestConfig.java +++ b/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/test/ResolverBuilder_TestConfig.java @@ -1,5 +1,11 @@ package io.leangen.graphql.spqr.spring.test; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + import io.leangen.graphql.ExtensionProvider; import io.leangen.graphql.GeneratorConfiguration; import io.leangen.graphql.annotations.GraphQLArgument; @@ -19,12 +25,6 @@ import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Component; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - @Configuration public class ResolverBuilder_TestConfig { //------------------------------------------------------------------------------------------ @@ -49,11 +49,16 @@ protected boolean isQuery(Method method, ResolverBuilderParams params) { @Component("annotatedOperationSourceBean") @GraphQLApi - private static class AnnotatedOperationSourceBean { + public static class AnnotatedOperationSourceBean { @GraphQLQuery(name = "greetingFromAnnotatedSource_wiredAsComponent") public String getGreeting() { return "Hello world !"; } + + @GraphQLQuery(name = "echo") + public String echo(@GraphQLArgument(name = "content") String content) { + return content; + } } @Bean diff --git a/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/test/ResolverBuilder_TestReactiveConfig.java b/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/test/ResolverBuilder_TestReactiveConfig.java index b8bc72b..5a7f7fd 100644 --- a/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/test/ResolverBuilder_TestReactiveConfig.java +++ b/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/test/ResolverBuilder_TestReactiveConfig.java @@ -1,5 +1,6 @@ package io.leangen.graphql.spqr.spring.test; +import io.leangen.graphql.annotations.GraphQLArgument; import io.leangen.graphql.annotations.GraphQLQuery; import io.leangen.graphql.spqr.spring.annotations.GraphQLApi; import org.springframework.context.annotation.Configuration; @@ -22,5 +23,10 @@ public Mono getGreetingMono(){ public Flux getGreetingFlux(){ return Flux.fromArray(new String[]{"First Hello world !","Second Hello world !"}); } + + @GraphQLQuery(name = "echo") + public Mono echo(@GraphQLArgument(name = "content") String content) { + return Mono.just(content); + } } } diff --git a/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/web/GraphQLControllerTest.java b/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/web/GraphQLControllerTest.java index cfb8c74..1e42645 100644 --- a/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/web/GraphQLControllerTest.java +++ b/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/web/GraphQLControllerTest.java @@ -1,5 +1,8 @@ package io.leangen.graphql.spqr.spring.web; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + import io.leangen.graphql.spqr.spring.autoconfigure.BaseAutoConfiguration; import io.leangen.graphql.spqr.spring.autoconfigure.MvcAutoConfiguration; import io.leangen.graphql.spqr.spring.autoconfigure.SpringDataAutoConfiguration; @@ -15,9 +18,6 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; - import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalToCompressingWhiteSpace; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -119,6 +119,29 @@ public void defaultControllerTest_POST_applicationJson_overridingQueryParams() t .andExpect(content().string(containsString("Hello world"))); } + @Test + public void defaultControllerTest_GET_with_variables() throws Exception { + mockMvc.perform( + get("/" + apiContext) + .param("query", "query Echo($contentInput: String){ echo(content: $contentInput)}") + .param("variables", "{\"contentInput\": \"Hello world\"}") + ) + .andExpect(status().isOk()) + .andExpect(content().json("{\"data\":{\"echo\":\"Hello world\"}}", true)); + } + + @Test + public void defaultControllerTest_POST_with_variables() throws Exception { + mockMvc.perform( + post("/" + apiContext) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("{\"query\":\"{INVALID_QUERY}\",\"variables\":{\"contentInput\": \"Hello world2\"},\"operationName\":null}") + .param("query", "query Echo($contentInput: String){ echo(content: $contentInput)}") + .param("variables", "{\"contentInput\": \"Hello world1\"}")) + .andExpect(status().isOk()) + .andExpect(content().json("{\"data\":{\"echo\":\"Hello world1\"}}", true)); + } + @Test public void defaultControllerTest_POST_formUrlEncoded_INVALID() throws Exception { mockMvc.perform( diff --git a/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/web/reactive/GraphQLReactiveControllerTest.java b/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/web/reactive/GraphQLReactiveControllerTest.java index 5369c75..2e722a1 100644 --- a/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/web/reactive/GraphQLReactiveControllerTest.java +++ b/graphql-spqr-spring-boot-autoconfigure/src/test/java/io/leangen/graphql/spqr/spring/web/reactive/GraphQLReactiveControllerTest.java @@ -1,5 +1,7 @@ package io.leangen.graphql.spqr.spring.web.reactive; +import java.net.URI; + import io.leangen.graphql.spqr.spring.autoconfigure.BaseAutoConfiguration; import io.leangen.graphql.spqr.spring.autoconfigure.ReactiveAutoConfiguration; import io.leangen.graphql.spqr.spring.test.ResolverBuilder_TestReactiveConfig; @@ -18,10 +20,8 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.web.reactive.function.BodyInserters; -import java.net.URI; - -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; @RunWith(SpringRunner.class) @WebFluxTest @@ -74,4 +74,28 @@ public void defaultControllerTest_POST_flux() { assertThat("", c.getResponseBody(), containsString("Second Hello world !")); }); } + + @Test + public void defaultControllerTest_GET_with_variables() { + webTestClient.get().uri("/" + apiContext + "?query={query}&variables={variables}", + "query Echo($contentInput: String){ echo(content: $contentInput)}", + "{\"contentInput\": \"Hello world\"}") + .exchange() + .expectStatus().isOk() + .expectBody(String.class) + .consumeWith(c -> assertThat("", c.getResponseBody(), containsString("Hello world"))); + } + + @Test + public void defaultControllerTest_POST_with_variables() { + webTestClient.post().uri("/" + apiContext + "?query={query}&variables={variables}", + "query Echo($contentInput: String){ echo(content: $contentInput)}", + "{\"contentInput\": \"Hello world1\"}") + .contentType(MediaType.APPLICATION_JSON) + .body(BodyInserters.fromValue("{\"query\":\"{INVALID_QUERY}\",\"variables\":{\"contentInput\": \"Hello world2\"},\"operationName\":null}")) + .exchange() + .expectStatus().isOk() + .expectBody(String.class) + .consumeWith(c -> assertThat("", c.getResponseBody(), containsString("Hello world1"))); + } }