Skip to content
This repository was archived by the owner on Dec 19, 2023. It is now read-only.

Commit f4180a9

Browse files
committed
First version of webflux starter
1 parent 3396004 commit f4180a9

File tree

26 files changed

+667
-393
lines changed

26 files changed

+667
-393
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1818
#
1919

20-
version = 5.12.0-SNAPSHOT
20+
version = 6.0.0-SNAPSHOT
2121

2222
PROJECT_GROUP = com.graphql-java-kickstart
2323
PROJECT_NAME = graphql-spring-boot

graphql-kickstart-spring-boot-autoconfigure-tools/src/main/java/graphql/kickstart/tools/boot/GraphQLJavaToolsAutoConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
import com.fasterxml.jackson.databind.ObjectMapper;
1111
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
1212
import com.fasterxml.jackson.module.kotlin.KotlinModule;
13+
import graphql.kickstart.execution.config.GraphQLSchemaProvider;
1314
import graphql.schema.GraphQLScalarType;
1415
import graphql.schema.GraphQLSchema;
1516
import graphql.schema.idl.SchemaDirectiveWiring;
16-
import graphql.servlet.config.GraphQLSchemaProvider;
1717
import java.io.IOException;
1818
import java.util.List;
1919
import org.springframework.beans.factory.annotation.Autowired;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
dependencies {
2+
compile(project(':graphql-kickstart-spring-webflux'))
3+
4+
compile "org.springframework.boot:spring-boot-autoconfigure:$LIB_SPRING_BOOT_VER"
5+
compile "com.graphql-java-kickstart:graphql-java-kickstart:$LIB_GRAPHQL_SERVLET_VER"
6+
compile "org.springframework.boot:spring-boot-starter-webflux:$LIB_SPRING_BOOT_VER"
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package graphql.kickstart.spring.webflux.boot;
2+
3+
import static graphql.kickstart.execution.GraphQLObjectMapper.newBuilder;
4+
5+
import graphql.GraphQL;
6+
import graphql.kickstart.execution.GraphQLInvoker;
7+
import graphql.kickstart.execution.GraphQLObjectMapper;
8+
import graphql.kickstart.execution.config.GraphQLBuilder;
9+
import graphql.kickstart.execution.config.ObjectMapperProvider;
10+
import graphql.kickstart.spring.DefaultGraphQLSpringInvocationInputFactory;
11+
import graphql.kickstart.spring.GraphQLSpringContextBuilder;
12+
import graphql.kickstart.spring.GraphQLSpringInvocationInputFactory;
13+
import graphql.kickstart.spring.GraphQLSpringRootObjectBuilder;
14+
import graphql.kickstart.spring.webflux.GraphQLController;
15+
import graphql.schema.GraphQLSchema;
16+
import lombok.extern.slf4j.Slf4j;
17+
import org.springframework.beans.factory.ObjectProvider;
18+
import org.springframework.beans.factory.annotation.Autowired;
19+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
20+
import org.springframework.context.annotation.Bean;
21+
import org.springframework.context.annotation.ComponentScan;
22+
import org.springframework.context.annotation.Configuration;
23+
24+
@Slf4j
25+
@Configuration
26+
@ComponentScan(basePackageClasses = GraphQLController.class)
27+
public class GraphQLSpringWebfluxAutoConfiguration {
28+
29+
@Bean
30+
@ConditionalOnMissingBean
31+
public GraphQLObjectMapper graphQLObjectMapper(ObjectProvider<ObjectMapperProvider> provider) {
32+
GraphQLObjectMapper.Builder builder = newBuilder();
33+
// builder.withGraphQLErrorHandler(errorHandlerSupplier);
34+
provider.ifAvailable(builder::withObjectMapperProvider);
35+
return builder.build();
36+
}
37+
38+
@Bean
39+
@ConditionalOnMissingBean
40+
public GraphQLSpringInvocationInputFactory graphQLSpringInvocationInputFactory(
41+
@Autowired(required = false) GraphQLSpringContextBuilder contextBuilder,
42+
@Autowired(required = false) GraphQLSpringRootObjectBuilder rootObjectBuilder
43+
) {
44+
return new DefaultGraphQLSpringInvocationInputFactory(contextBuilder, rootObjectBuilder);
45+
}
46+
47+
@Bean
48+
@ConditionalOnMissingBean
49+
public GraphQLInvoker graphQLInvoker(GraphQLSchema schema) {
50+
GraphQL graphQL = new GraphQLBuilder().build(schema);
51+
return new GraphQLInvoker(graphQL);
52+
}
53+
54+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2+
graphql.kickstart.spring.webflux.boot.GraphQLSpringWebfluxAutoConfiguration
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dependencies {
2+
compile(project(':graphql-kickstart-spring-boot-autoconfigure-webflux'))
3+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package graphql.kickstart.spring;
2+
3+
import graphql.kickstart.execution.GraphQLObjectMapper;
4+
import graphql.kickstart.execution.GraphQLRequest;
5+
import java.io.IOException;
6+
import java.util.Collections;
7+
import java.util.Map;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.http.HttpHeaders;
10+
import org.springframework.http.HttpStatus;
11+
import org.springframework.http.MediaType;
12+
import org.springframework.web.bind.annotation.RequestBody;
13+
import org.springframework.web.bind.annotation.RequestHeader;
14+
import org.springframework.web.bind.annotation.RequestMapping;
15+
import org.springframework.web.bind.annotation.RequestMethod;
16+
import org.springframework.web.bind.annotation.RequestParam;
17+
import org.springframework.web.server.ResponseStatusException;
18+
import org.springframework.web.server.ServerWebExchange;
19+
20+
@RequiredArgsConstructor
21+
public abstract class AbstractGraphQLController {
22+
23+
private final GraphQLObjectMapper objectMapper;
24+
25+
@RequestMapping(value = "${graphql.url:graphql}",
26+
method = RequestMethod.POST,
27+
produces = MediaType.APPLICATION_JSON_VALUE)
28+
public Object graphqlPOST(
29+
@RequestHeader(value = HttpHeaders.CONTENT_TYPE, required = false) String contentType,
30+
@RequestParam(value = "query", required = false) String query,
31+
@RequestParam(value = "operationName", required = false) String operationName,
32+
@RequestParam(value = "variables", required = false) String variablesJson,
33+
@RequestBody(required = false) String body,
34+
ServerWebExchange serverWebExchange) throws IOException {
35+
36+
if (body == null) {
37+
body = "";
38+
}
39+
40+
// https://graphql.org/learn/serving-over-http/#post-request
41+
//
42+
// A standard GraphQL POST request should use the application/json content type,
43+
// and include a JSON-encoded body of the following form:
44+
//
45+
// {
46+
// "query": "...",
47+
// "operationName": "...",
48+
// "variables": { "myVariable": "someValue", ... }
49+
// }
50+
51+
if (MediaType.APPLICATION_JSON_VALUE.equals(contentType)) {
52+
GraphQLRequest request = objectMapper.readGraphQLRequest(body);
53+
if (request.getQuery() == null) {
54+
request.setQuery("");
55+
}
56+
return executeRequest(request.getQuery(), request.getOperationName(), request.getVariables(), serverWebExchange);
57+
}
58+
59+
// In addition to the above, we recommend supporting two additional cases:
60+
//
61+
// * If the "query" query string parameter is present (as in the GET example above),
62+
// it should be parsed and handled in the same way as the HTTP GET case.
63+
64+
if (query != null) {
65+
return executeRequest(query, operationName, convertVariablesJson(variablesJson), serverWebExchange);
66+
}
67+
68+
// * If the "application/graphql" Content-Type header is present,
69+
// treat the HTTP POST body contents as the GraphQL query string.
70+
71+
if ("application/graphql".equals(contentType)) {
72+
return executeRequest(body, null, null, serverWebExchange);
73+
}
74+
75+
throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY, "Could not process GraphQL request");
76+
}
77+
78+
@RequestMapping(value = "${graphql.url:graphql}",
79+
method = RequestMethod.GET,
80+
produces = MediaType.APPLICATION_JSON_VALUE)
81+
public Object graphqlGET(
82+
@RequestParam("query") String query,
83+
@RequestParam(value = "operationName", required = false) String operationName,
84+
@RequestParam(value = "variables", required = false) String variablesJson,
85+
ServerWebExchange serverWebExchange) {
86+
87+
// https://graphql.org/learn/serving-over-http/#get-request
88+
//
89+
// When receiving an HTTP GET request, the GraphQL query should be specified in the "query" query string.
90+
// For example, if we wanted to execute the following GraphQL query:
91+
//
92+
// {
93+
// me {
94+
// name
95+
// }
96+
// }
97+
//
98+
// This request could be sent via an HTTP GET like so:
99+
//
100+
// http://myapi/graphql?query={me{name}}
101+
//
102+
// Query variables can be sent as a JSON-encoded string in an additional query parameter called "variables".
103+
// If the query contains several named operations,
104+
// an "operationName" query parameter can be used to control which one should be executed.
105+
106+
return executeRequest(query, operationName, convertVariablesJson(variablesJson), serverWebExchange);
107+
}
108+
109+
private Map<String, Object> convertVariablesJson(String jsonMap) {
110+
if (jsonMap == null) {
111+
return Collections.emptyMap();
112+
}
113+
return objectMapper.deserializeVariables(jsonMap);
114+
}
115+
116+
protected abstract Object executeRequest(
117+
String query,
118+
String operationName,
119+
Map<String, Object> variables,
120+
ServerWebExchange serverWebExchange
121+
);
122+
123+
}

graphql-kickstart-spring-support/src/main/java/graphql/kickstart/spring/DefaultGraphQLSpringInvocationInputFactory.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,30 @@ public class DefaultGraphQLSpringInvocationInputFactory implements GraphQLSpring
1212
private Supplier<GraphQLSpringContextBuilder> contextBuilderSupplier = () -> (DefaultGraphQLSpringContext::new);
1313
private Supplier<GraphQLSpringRootObjectBuilder> rootObjectBuilderSupplier = () -> (serverWebExchange -> new Object());
1414

15+
public DefaultGraphQLSpringInvocationInputFactory(
16+
GraphQLSpringContextBuilder contextBuilder,
17+
GraphQLSpringRootObjectBuilder rootObjectBuilder
18+
) {
19+
if (contextBuilder != null) {
20+
contextBuilderSupplier = () -> contextBuilder;
21+
}
22+
if (rootObjectBuilder != null) {
23+
rootObjectBuilderSupplier = () -> rootObjectBuilder;
24+
}
25+
}
26+
27+
public DefaultGraphQLSpringInvocationInputFactory(
28+
Supplier<GraphQLSpringContextBuilder> contextBuilderSupplier,
29+
Supplier<GraphQLSpringRootObjectBuilder> rootObjectBuilderSupplier
30+
) {
31+
if (contextBuilderSupplier != null) {
32+
this.contextBuilderSupplier = contextBuilderSupplier;
33+
}
34+
if (rootObjectBuilderSupplier != null) {
35+
this.rootObjectBuilderSupplier = rootObjectBuilderSupplier;
36+
}
37+
}
38+
1539
@Override
1640
public GraphQLSingleInvocationInput create(GraphQLRequest graphQLRequest, ServerWebExchange serverWebExchange) {
1741
return new GraphQLSingleInvocationInput(

graphql-kickstart-spring-support/src/main/java/graphql/kickstart/spring/GraphQLSpringContextBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import org.springframework.web.server.ServerWebExchange;
44

5-
interface GraphQLSpringContextBuilder {
5+
public interface GraphQLSpringContextBuilder {
66

77
GraphQLSpringContext build(ServerWebExchange serverWebExchange);
88

graphql-kickstart-spring-support/src/main/java/graphql/kickstart/spring/GraphQLSpringRootObjectBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import org.springframework.web.server.ServerWebExchange;
44

5-
interface GraphQLSpringRootObjectBuilder {
5+
public interface GraphQLSpringRootObjectBuilder {
66

77
Object build(ServerWebExchange serverWebExchange);
88

0 commit comments

Comments
 (0)