Skip to content

Commit

Permalink
improve query param handling fixes leangen#58
Browse files Browse the repository at this point in the history
  • Loading branch information
Andy2003 committed Aug 15, 2023
1 parent 8751777 commit 2b8848f
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -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<R> {

protected final GraphQL graphQL;
protected final GraphQLExecutor<R> executor;
private final ValueMapper valueMapper;

public GraphQLController(GraphQL graphQL, GraphQLExecutor<R> 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(
Expand All @@ -33,9 +39,15 @@ public GraphQLController(GraphQL graphQL, GraphQLExecutor<R> 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<String, Object> variables = parseJsonString(variablesJsonString);
return jsonPost(requestBody, new GraphQLRequest(id, query, operationName, variables), request, TransportType.HTTP);
}

@PostMapping(
Expand Down Expand Up @@ -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
Expand All @@ -98,7 +109,14 @@ public Object executeFormPost(@RequestParam Map<String, String> 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<String, Object> variables = parseJsonString(variablesJsonString);
GraphQLRequest graphQLRequest = new GraphQLRequest(null, requestQuery, requestOperationName, variables);
return get(graphQLRequest, request, TransportType.HTTP);
}

Expand All @@ -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<String, Object> parseJsonString(String json) {
return valueMapper.fromString(json, GenericTypeReflector.annotate(Map.class));
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 {
//------------------------------------------------------------------------------------------
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -22,5 +23,10 @@ public Mono<String> getGreetingMono(){
public Flux<String> getGreetingFlux(){
return Flux.fromArray(new String[]{"First Hello world !","Second Hello world !"});
}

@GraphQLQuery(name = "echo")
public Mono<String> echo(@GraphQLArgument(name = "content") String content) {
return Mono.just(content);
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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")));
}
}

0 comments on commit 2b8848f

Please sign in to comment.