add webflux variant
This commit is contained in:
committed by
Rossen Stoyanchev
parent
ce3a83bc0e
commit
11d2ad74cf
@@ -1,4 +1,6 @@
|
||||
rootProject.name = 'spring-graphql'
|
||||
include 'spring-graphql-boot-starter-webmvc'
|
||||
include 'spring-graphql-boot-starter-webflux'
|
||||
include 'spring-graphql-webmvc'
|
||||
include 'spring-graphql-webflux'
|
||||
|
||||
|
||||
9
spring-graphql-boot-starter-webflux/build.gradle
Normal file
9
spring-graphql-boot-starter-webflux/build.gradle
Normal file
@@ -0,0 +1,9 @@
|
||||
description = "GraphQL Java Spring Boot starter Webmvc"
|
||||
dependencies {
|
||||
implementation "org.springframework.boot:spring-boot-autoconfigure:$springBootVersion"
|
||||
implementation project(':spring-graphql-webflux')
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
|
||||
testCompile "org.springframework.boot:spring-boot-starter-webflux:$springBootVersion"
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.springframework.graphql.reactive;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.graphql.reactive.components.GraphQLController;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication
|
||||
@ComponentScan(basePackageClasses = GraphQLController.class)
|
||||
public class GraphQLEndpointConfiguration {
|
||||
|
||||
@Autowired
|
||||
ApplicationContext applicationContext;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.graphql.reactive.GraphQLEndpointConfiguration
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.springframework.graphql.reactive;
|
||||
|
||||
import graphql.ExecutionInput;
|
||||
import graphql.ExecutionResultImpl;
|
||||
import graphql.GraphQL;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ExtendWith(SpringExtension.class)
|
||||
public class IntegrationTest {
|
||||
|
||||
@Autowired
|
||||
WebTestClient webClient;
|
||||
|
||||
@Autowired
|
||||
GraphQL graphql;
|
||||
|
||||
@Test
|
||||
public void endpointIsAvailable() {
|
||||
String query = "{foo}";
|
||||
|
||||
ExecutionResultImpl executionResult = ExecutionResultImpl.newExecutionResult()
|
||||
.data("bar")
|
||||
.build();
|
||||
CompletableFuture cf = CompletableFuture.completedFuture(executionResult);
|
||||
ArgumentCaptor<ExecutionInput> captor = ArgumentCaptor.forClass(ExecutionInput.class);
|
||||
Mockito.when(graphql.executeAsync(captor.capture())).thenReturn(cf);
|
||||
|
||||
Map<String, String> body = new LinkedHashMap<>();
|
||||
body.put("query", query);
|
||||
|
||||
Map<String, Object> expectedResult = new LinkedHashMap<>();
|
||||
expectedResult.put("data", "bar");
|
||||
|
||||
this.webClient.post().uri("/graphql").body(BodyInserters.fromValue(body)).exchange().expectStatus().isOk()
|
||||
.expectBody(Map.class).isEqualTo(expectedResult);
|
||||
|
||||
assertThat(captor.getValue().getQuery()).isEqualTo(query);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.springframework.graphql.reactive;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@SpringBootApplication
|
||||
public class IntegrationTestConfig {
|
||||
|
||||
|
||||
@Bean
|
||||
public GraphQL graphQL() {
|
||||
return Mockito.mock(GraphQL.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package org.springframework.graphql;
|
||||
package org.springframework.graphql.servlet;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.graphql.components.GraphQLController;
|
||||
import org.springframework.graphql.servlet.components.GraphQLController;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.graphql.GraphQLEndpointConfiguration
|
||||
org.springframework.graphql.servlet.GraphQLEndpointConfiguration
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.graphql;
|
||||
package org.springframework.graphql.servlet;
|
||||
|
||||
import graphql.ExecutionInput;
|
||||
import graphql.ExecutionResultImpl;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.graphql;
|
||||
package org.springframework.graphql.servlet;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import org.mockito.Mockito;
|
||||
16
spring-graphql-webflux/build.gradle
Normal file
16
spring-graphql-webflux/build.gradle
Normal file
@@ -0,0 +1,16 @@
|
||||
description = "GraphQL Java Spring Webmvc integration"
|
||||
|
||||
apply plugin: 'java-library'
|
||||
|
||||
dependencies {
|
||||
implementation "org.springframework:spring-webflux:$springVersion"
|
||||
implementation "org.springframework:spring-context:$springVersion"
|
||||
implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
|
||||
api "com.graphql-java:graphql-java:$graphqlJavaVersion"
|
||||
|
||||
testImplementation("org.assertj:assertj-core:$assertJVersion")
|
||||
testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
|
||||
testImplementation "org.springframework:spring-test:$springVersion"
|
||||
testImplementation group: 'com.jayway.jsonpath', name: 'json-path', version: '2.4.0'
|
||||
testImplementation "org.mockito:mockito-core:2.+"
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.springframework.graphql.reactive;
|
||||
|
||||
import graphql.Assert;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public class GraphQLInvocationData {
|
||||
|
||||
private final String query;
|
||||
private final String operationName;
|
||||
private final Map<String, Object> variables;
|
||||
|
||||
public GraphQLInvocationData(String query, String operationName, Map<String, Object> variables) {
|
||||
this.query = Assert.assertNotNull(query, () -> "query must be provided");
|
||||
this.operationName = operationName;
|
||||
this.variables = variables != null ? variables : Collections.emptyMap();
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public String getOperationName() {
|
||||
return operationName;
|
||||
}
|
||||
|
||||
public Map<String, Object> getVariables() {
|
||||
return variables;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.springframework.graphql.reactive.components;
|
||||
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.graphql.reactive.GraphQLInvocationData;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
|
||||
|
||||
@Component
|
||||
public class GraphQLController {
|
||||
|
||||
@Autowired
|
||||
GraphQLRequestHandler graphQLRequestHandler;
|
||||
|
||||
@Bean
|
||||
public RouterFunction<ServerResponse> routerFunction() {
|
||||
RouterFunction<ServerResponse> route = route()
|
||||
.POST("/graphql", this::graphqlPOST)
|
||||
.build();
|
||||
return route;
|
||||
|
||||
}
|
||||
|
||||
private Mono<ServerResponse> graphqlPOST(ServerRequest serverRequest) {
|
||||
Mono<GraphQLRequestBody> bodyMono = serverRequest.bodyToMono(GraphQLRequestBody.class);
|
||||
return bodyMono.flatMap(body -> {
|
||||
String query = body.getQuery();
|
||||
if (query == null) {
|
||||
query = "";
|
||||
}
|
||||
GraphQLInvocationData invocationData = new GraphQLInvocationData(query, body.getOperationName(), body.getVariables());
|
||||
Mono<Map> resultBodyMono = graphQLRequestHandler.invoke(invocationData, serverRequest.headers());
|
||||
return resultBodyMono.flatMap(resultBody -> ServerResponse.ok().bodyValue(resultBody));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.springframework.graphql.reactive.components;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class GraphQLRequestBody {
|
||||
private String query;
|
||||
private String operationName;
|
||||
private Map<String, Object> variables;
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public void setQuery(String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public String getOperationName() {
|
||||
return operationName;
|
||||
}
|
||||
|
||||
public void setOperationName(String operationName) {
|
||||
this.operationName = operationName;
|
||||
}
|
||||
|
||||
public Map<String, Object> getVariables() {
|
||||
return variables;
|
||||
}
|
||||
|
||||
public void setVariables(Map<String, Object> variables) {
|
||||
this.variables = variables;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.springframework.graphql.reactive.components;
|
||||
|
||||
import graphql.ExecutionInput;
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQL;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.graphql.reactive.GraphQLInvocationData;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class GraphQLRequestHandler {
|
||||
|
||||
@Autowired
|
||||
private GraphQL graphQL;
|
||||
|
||||
public Mono<Map> invoke(GraphQLInvocationData invocationData,
|
||||
ServerRequest.Headers headers) {
|
||||
Assert.notNull(graphQL, "graphQL is not set");
|
||||
ExecutionInput executionInput = ExecutionInput.newExecutionInput()
|
||||
.query(invocationData.getQuery())
|
||||
.operationName(invocationData.getOperationName())
|
||||
.variables(invocationData.getVariables())
|
||||
.build();
|
||||
customizeExecutionInput(executionInput, headers);
|
||||
CompletableFuture<ExecutionInput> customizedExecutionInput = customizeExecutionInput(executionInput, headers);
|
||||
CompletableFuture<ExecutionResult> executionResultCompletableFuture = customizedExecutionInput.thenCompose(graphQL::executeAsync);
|
||||
return handleExecutionResult(executionResultCompletableFuture);
|
||||
}
|
||||
|
||||
protected CompletableFuture<ExecutionInput> customizeExecutionInput(ExecutionInput executionInput,
|
||||
ServerRequest.Headers headers) {
|
||||
return CompletableFuture.completedFuture(executionInput);
|
||||
}
|
||||
|
||||
protected Mono<Map> handleExecutionResult(CompletableFuture<ExecutionResult> executionResultCF) {
|
||||
return Mono.fromCompletionStage(executionResultCF).map(ExecutionResult::toSpecification);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.graphql;
|
||||
package org.springframework.graphql.servlet;
|
||||
|
||||
import graphql.Assert;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.springframework.graphql.components;
|
||||
package org.springframework.graphql.servlet.components;
|
||||
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.graphql.GraphQLInvocationData;
|
||||
import org.springframework.graphql.servlet.GraphQLInvocationData;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.function.RouterFunction;
|
||||
import org.springframework.web.servlet.function.ServerRequest;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.graphql.components;
|
||||
package org.springframework.graphql.servlet.components;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package org.springframework.graphql.components;
|
||||
package org.springframework.graphql.servlet.components;
|
||||
|
||||
import graphql.ExecutionInput;
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQL;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.graphql.GraphQLInvocationData;
|
||||
import org.springframework.graphql.servlet.GraphQLInvocationData;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.servlet.function.ServerRequest;
|
||||
Reference in New Issue
Block a user