Skip to content
This repository was archived by the owner on Oct 25, 2021. It is now read-only.

Commit 1b9f656

Browse files
committed
add concept of GraphQLInvocation
test
1 parent ae56681 commit 1b9f656

File tree

9 files changed

+259
-24
lines changed

9 files changed

+259
-24
lines changed

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# THIS IS STILL WIP AND NOT RELEASED YET
12
# GraphQL Java Spring
23

34

@@ -9,10 +10,18 @@ While the GraphQL Specification itself doesn't specify any transport protocol th
910
Goals / Design:
1011

1112
- Just HTTP JSON: the current focus is on HTTP execution via JSON.
12-
- Minimal Dependencies: the only dependencies are GraphQL Java and Spring projects.
13+
- Minimal Dependencies: the only dependencies are GraphQL Java and Spring projects (including Jackson for JSON handling).
1314
- No additional abstraction layer on top of GraphQL Java: GraphQL Java is meant to be used directly.
1415

1516

17+
## Supported HTTP Requests
18+
19+
As outlined in https://graphql.org/learn/serving-over-http this project supports:
20+
21+
1. GET request with `query`, `operationName` and `variables` parameters. The variable parameters are json encoded
22+
2. POST request with body `application/json` and keys `query` (string), `operationName` (string) and `variables` (map).
23+
24+
Both produce `application/json`.
1625

1726
## Spring Boot Starter
1827

@@ -39,3 +48,9 @@ or to your `pom.xml`
3948
</dependency>
4049

4150
```
51+
52+
## Spring Web Artifact
53+
54+
We also provide a artifact for non Boot Application.
55+
56+

graphql-java-spring-web/build.gradle

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1+
def springVersion = "5.1.2.RELEASE"
2+
def jacksonVersion = "2.9.6"
3+
def graphqlJavaVersion = "11.0"
14

25
dependencies {
3-
compile "org.springframework:spring-web:5.1.2.RELEASE"
4-
compile "com.graphql-java:graphql-java:11.0"
6+
compile "org.springframework:spring-webmvc:$springVersion"
7+
compile "org.springframework:spring-context:$springVersion"
8+
compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
9+
compile "com.graphql-java:graphql-java:$graphqlJavaVersion"
10+
compile "javax.servlet:javax.servlet-api:4.0.1"
11+
512
testCompile group: 'junit', name: 'junit', version: '4.12'
13+
testCompile "org.springframework:spring-test:$springVersion"
14+
testCompile "org.mockito:mockito-core:2.+"
615
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package graphql.spring.controller;
2+
3+
import graphql.ExecutionInput;
4+
import graphql.ExecutionResult;
5+
import graphql.GraphQL;
6+
import graphql.Internal;
7+
import org.springframework.beans.factory.annotation.Autowired;
8+
import org.springframework.stereotype.Component;
9+
import org.springframework.web.context.request.WebRequest;
10+
11+
import java.util.Map;
12+
import java.util.concurrent.CompletableFuture;
13+
14+
@Component
15+
@Internal
16+
public class DefaultGraphQLInvocation implements GraphQLInvocation {
17+
18+
@Autowired
19+
private GraphQL graphQL;
20+
21+
@Override
22+
public CompletableFuture<Map<String, Object>> invoke(GraphQLInvocationData invocationData, WebRequest webRequest) {
23+
ExecutionInput executionInput = ExecutionInput.newExecutionInput()
24+
.query(invocationData.getQuery())
25+
.operationName(invocationData.getOperationName())
26+
.variables(invocationData.getVariables())
27+
.build();
28+
return graphQL.executeAsync(executionInput).thenApply(ExecutionResult::toSpecification);
29+
}
30+
31+
}
Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,61 @@
11
package graphql.spring.controller;
22

33

4-
import graphql.ExecutionInput;
5-
import graphql.GraphQL;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import graphql.Internal;
66
import org.springframework.beans.factory.annotation.Autowired;
77
import org.springframework.http.MediaType;
88
import org.springframework.web.bind.annotation.RequestBody;
99
import org.springframework.web.bind.annotation.RequestMapping;
1010
import org.springframework.web.bind.annotation.RequestMethod;
11+
import org.springframework.web.bind.annotation.RequestParam;
1112
import org.springframework.web.bind.annotation.RestController;
13+
import org.springframework.web.context.request.WebRequest;
1214

13-
import java.util.LinkedHashMap;
15+
import java.io.IOException;
1416
import java.util.Map;
1517

1618
@RestController
19+
@Internal
1720
public class GraphQLController {
1821

1922
@Autowired
20-
private GraphQL graphql;
23+
private GraphQLInvocation graphQLInvocation;
2124

22-
23-
@RequestMapping(value = "${graphql.url:graphql}", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
24-
public Map<String, Object> graphql(@RequestBody Map<String, Object> body) {
25-
String query = (String) body.get("query");
25+
@Autowired
26+
private ObjectMapper objectMapper;
27+
28+
@RequestMapping(value = "${graphql.url:graphql}",
29+
method = RequestMethod.POST,
30+
consumes = MediaType.APPLICATION_JSON_VALUE,
31+
produces = MediaType.APPLICATION_JSON_VALUE)
32+
public Object graphqlPOST(@RequestBody GraphQLRequestBody body, WebRequest webRequest) {
33+
String query = body.getQuery();
2634
if (query == null) {
2735
query = "";
2836
}
29-
String operationName = (String) body.get("operationName");
30-
Map<String, Object> variables = (Map<String, Object>) body.get("variables");
31-
if (variables == null) {
32-
variables = new LinkedHashMap<>();
33-
}
34-
return executeGraphqlQuery(query, operationName, variables);
37+
return graphQLInvocation.invoke(new GraphQLInvocationData(query, body.getOperationName(), body.getVariables()), webRequest);
38+
}
39+
40+
@RequestMapping(value = "${graphql.url:graphql}",
41+
method = RequestMethod.GET,
42+
produces = MediaType.APPLICATION_JSON_VALUE)
43+
public Object graphqlGET(
44+
@RequestParam("query") String query,
45+
@RequestParam(value = "operationName", required = false) String operationName,
46+
@RequestParam(value = "variables", required = false) String variablesJson,
47+
WebRequest webRequest) {
48+
return graphQLInvocation.invoke(new GraphQLInvocationData(query, operationName, convertVariablesJson(variablesJson)), webRequest);
3549
}
3650

37-
private Map<String, Object> executeGraphqlQuery(String query, String operationName, Map<String, Object> variables) {
38-
ExecutionInput executionInput = ExecutionInput.newExecutionInput()
39-
.query(query)
40-
.operationName(operationName)
41-
.variables(variables)
42-
.build();
43-
return this.graphql.execute(executionInput).toSpecification();
51+
private Map<String, Object> convertVariablesJson(String jsonMap) {
52+
try {
53+
return objectMapper.readValue(jsonMap, Map.class);
54+
} catch (IOException e) {
55+
throw new RuntimeException("Could not convert variables GET parameter: expected a JSON map", e);
56+
}
57+
4458
}
4559

60+
4661
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package graphql.spring.controller;
2+
3+
import graphql.PublicApi;
4+
import org.springframework.web.context.request.WebRequest;
5+
6+
@PublicApi
7+
public interface GraphQLInvocation {
8+
9+
Object invoke(GraphQLInvocationData invocationData, WebRequest webRequest);
10+
11+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package graphql.spring.controller;
2+
3+
import graphql.Assert;
4+
import graphql.PublicApi;
5+
6+
import java.util.Collections;
7+
import java.util.Map;
8+
9+
@PublicApi
10+
public class GraphQLInvocationData {
11+
12+
private final String query;
13+
private final String operationName;
14+
private final Map<String, Object> variables;
15+
16+
public GraphQLInvocationData(String query, String operationName, Map<String, Object> variables) {
17+
this.query = Assert.assertNotNull(query, "query must be provided");
18+
this.operationName = operationName;
19+
this.variables = variables != null ? variables : Collections.emptyMap();
20+
}
21+
22+
public String getQuery() {
23+
return query;
24+
}
25+
26+
public String getOperationName() {
27+
return operationName;
28+
}
29+
30+
public Map<String, Object> getVariables() {
31+
return variables;
32+
}
33+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package graphql.spring.controller;
2+
3+
import graphql.Internal;
4+
5+
import java.util.Map;
6+
7+
@Internal
8+
public class GraphQLRequestBody {
9+
private String query;
10+
private String operationName;
11+
private Map<String, Object> variables;
12+
13+
public String getQuery() {
14+
return query;
15+
}
16+
17+
public void setQuery(String query) {
18+
this.query = query;
19+
}
20+
21+
public String getOperationName() {
22+
return operationName;
23+
}
24+
25+
public void setOperationName(String operationName) {
26+
this.operationName = operationName;
27+
}
28+
29+
public Map<String, Object> getVariables() {
30+
return variables;
31+
}
32+
33+
public void setVariables(Map<String, Object> variables) {
34+
this.variables = variables;
35+
}
36+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package graphql.spring.controller;
2+
3+
import org.junit.Before;
4+
import org.junit.Test;
5+
import org.junit.runner.RunWith;
6+
import org.springframework.beans.factory.annotation.Autowired;
7+
import org.springframework.test.context.ContextConfiguration;
8+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
9+
import org.springframework.test.context.web.WebAppConfiguration;
10+
import org.springframework.test.web.servlet.MockMvc;
11+
import org.springframework.test.web.servlet.MvcResult;
12+
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
13+
import org.springframework.web.context.WebApplicationContext;
14+
15+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
16+
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
17+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
18+
19+
@RunWith(SpringJUnit4ClassRunner.class)
20+
@ContextConfiguration(classes = {TestAppConfig.class})
21+
@WebAppConfiguration
22+
public class GraphQLControllerTest {
23+
24+
private MockMvc mockMvc;
25+
26+
@Autowired
27+
private WebApplicationContext wac;
28+
29+
@Before
30+
public void setup() throws Exception {
31+
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
32+
}
33+
34+
35+
@Test
36+
public void testVariableJson() throws Exception {
37+
String query = "{foo}";
38+
String variablesJson = "{\"key\":\"value\"}";
39+
MvcResult mvcResult = this.mockMvc.perform(get("/graphql")
40+
.param("query", query)
41+
.param("variables", variablesJson))
42+
.andDo(print()).andExpect(status().isOk())
43+
.andReturn();
44+
45+
}
46+
47+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package graphql.spring.controller;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import graphql.ExecutionInput;
5+
import graphql.ExecutionResultImpl;
6+
import graphql.GraphQL;
7+
import org.mockito.ArgumentMatchers;
8+
import org.mockito.Mockito;
9+
import org.springframework.context.annotation.Bean;
10+
import org.springframework.context.annotation.ComponentScan;
11+
import org.springframework.context.annotation.Configuration;
12+
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
13+
14+
import java.util.concurrent.CompletableFuture;
15+
16+
@Configuration
17+
@EnableWebMvc
18+
@ComponentScan(basePackageClasses = GraphQLController.class)
19+
public class TestAppConfig {
20+
21+
22+
@Bean
23+
public GraphQL graphQL() {
24+
GraphQL graphql = Mockito.mock(GraphQL.class);
25+
ExecutionResultImpl executionResult = ExecutionResultImpl.newExecutionResult()
26+
.data("foo")
27+
.build();
28+
CompletableFuture cf = CompletableFuture.completedFuture(executionResult);
29+
Mockito.when(graphql.executeAsync(ArgumentMatchers.<ExecutionInput>any())).thenReturn(cf);
30+
return graphql;
31+
}
32+
33+
@Bean
34+
public ObjectMapper objectMapper() {
35+
return new ObjectMapper();
36+
}
37+
38+
}

0 commit comments

Comments
 (0)