From 932093ac3483cfa90bb84bf1383f04849194bed1 Mon Sep 17 00:00:00 2001 From: Andreas Marek Date: Sun, 9 Aug 2020 11:59:12 +1000 Subject: [PATCH] refactor to use router functions, upgrade spring and gradle --- build.gradle | 6 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../build.gradle | 4 +- graphql-java-spring-webmvc/build.gradle | 24 ++++---- .../graphql/ExecutionInputCustomizer.java | 20 ------- .../graphql/ExecutionResultHandler.java | 12 ---- .../graphql/GraphQLInvocation.java | 14 ----- .../graphql/GraphQLInvocationData.java | 2 - .../DefaultExecutionInputCustomizer.java | 19 ------- .../DefaultExecutionResultHandler.java | 23 -------- .../components/DefaultGraphQLInvocation.java | 37 ------------- .../graphql/components/GraphQLController.java | 54 ++++++++++-------- .../components/GraphQLRequestBody.java | 3 - .../components/GraphQLRequestHandler.java | 55 +++++++++++++++++++ 14 files changed, 104 insertions(+), 171 deletions(-) delete mode 100644 graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/ExecutionInputCustomizer.java delete mode 100644 graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/ExecutionResultHandler.java delete mode 100644 graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/GraphQLInvocation.java delete mode 100644 graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/DefaultExecutionInputCustomizer.java delete mode 100644 graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/DefaultExecutionResultHandler.java delete mode 100644 graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/DefaultGraphQLInvocation.java create mode 100644 graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/GraphQLRequestHandler.java diff --git a/build.gradle b/build.gradle index 4dd9621d..e4b702d8 100644 --- a/build.gradle +++ b/build.gradle @@ -13,8 +13,8 @@ subprojects { ext { graphqlJavaVersion = "15.0" - springVersion = "5.1.7.RELEASE" - springBootVersion = "2.1.5.RELEASE" + springVersion = "5.2.8.RELEASE" + springBootVersion = "2.3.2.RELEASE" jacksonVersion = "2.9.8" assertJVersion = "3.11.1" } @@ -42,7 +42,7 @@ subprojects { } task myWrapper(type: Wrapper) { - gradleVersion = '4.10.2' + gradleVersion = '6.5.1' distributionUrl = "https://services.gradle.org/distributions/gradle-${gradleVersion}-all.zip" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d76b502e..ac33e994 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/graphql-java-spring-boot-starter-webmvc/build.gradle b/graphql-java-spring-boot-starter-webmvc/build.gradle index c37e06b7..8c2fc114 100644 --- a/graphql-java-spring-boot-starter-webmvc/build.gradle +++ b/graphql-java-spring-boot-starter-webmvc/build.gradle @@ -1,7 +1,7 @@ description = "GraphQL Java Spring Boot starter Webmvc" dependencies { - compile "org.springframework.boot:spring-boot-autoconfigure:$springBootVersion" - compile project(':graphql-java-spring-webmvc') + implementation "org.springframework.boot:spring-boot-autoconfigure:$springBootVersion" + implementation project(':graphql-java-spring-webmvc') testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" testCompile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" diff --git a/graphql-java-spring-webmvc/build.gradle b/graphql-java-spring-webmvc/build.gradle index c9773262..2fe0c08a 100644 --- a/graphql-java-spring-webmvc/build.gradle +++ b/graphql-java-spring-webmvc/build.gradle @@ -1,15 +1,17 @@ description = "GraphQL Java Spring Webmvc integration" -dependencies { - compile "org.springframework:spring-webmvc:$springVersion" - compile "org.springframework:spring-context:$springVersion" - compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion" - compile "com.graphql-java:graphql-java:$graphqlJavaVersion" +apply plugin: 'java-library' - testCompile("org.assertj:assertj-core:$assertJVersion") - testCompile group: 'junit', name: 'junit', version: '4.12' - testCompile "org.springframework:spring-test:$springVersion" - testCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1' - testCompile group: 'com.jayway.jsonpath', name: 'json-path', version: '2.4.0' - testCompile "org.mockito:mockito-core:2.+" +dependencies { + implementation "org.springframework:spring-webmvc:$springVersion" + implementation "org.springframework:spring-context:$springVersion" + implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion" + api "com.graphql-java:graphql-java:$graphqlJavaVersion" + implementation group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1' + + testImplementation("org.assertj:assertj-core:$assertJVersion") + testImplementation group: 'junit', name: 'junit', version: '4.12' + testImplementation "org.springframework:spring-test:$springVersion" + testImplementation group: 'com.jayway.jsonpath', name: 'json-path', version: '2.4.0' + testImplementation "org.mockito:mockito-core:2.+" } diff --git a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/ExecutionInputCustomizer.java b/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/ExecutionInputCustomizer.java deleted file mode 100644 index 6f326d0c..00000000 --- a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/ExecutionInputCustomizer.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.springframework.graphql; - -import graphql.ExecutionInput; -import graphql.PublicApi; -import org.springframework.web.context.request.WebRequest; - -import java.util.concurrent.CompletableFuture; - -/** - * Lets you customize the #ExecutionInput before the query is executed. - * You can for example set a context object or define a root value. - *

- * This is only used if you use the default {@link GraphQLInvocation}. - */ -@PublicApi -public interface ExecutionInputCustomizer { - - CompletableFuture customizeExecutionInput(ExecutionInput executionInput, WebRequest webRequest); - -} diff --git a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/ExecutionResultHandler.java b/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/ExecutionResultHandler.java deleted file mode 100644 index 21d4d1e7..00000000 --- a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/ExecutionResultHandler.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.springframework.graphql; - -import graphql.ExecutionResult; -import graphql.PublicSpi; - -import java.util.concurrent.CompletableFuture; - -@PublicSpi -public interface ExecutionResultHandler { - - Object handleExecutionResult(CompletableFuture executionResultCF); -} diff --git a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/GraphQLInvocation.java b/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/GraphQLInvocation.java deleted file mode 100644 index 3c033bba..00000000 --- a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/GraphQLInvocation.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.springframework.graphql; - -import graphql.ExecutionResult; -import graphql.PublicApi; -import org.springframework.web.context.request.WebRequest; - -import java.util.concurrent.CompletableFuture; - -@PublicApi -public interface GraphQLInvocation { - - CompletableFuture invoke(GraphQLInvocationData invocationData, WebRequest webRequest); - -} diff --git a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/GraphQLInvocationData.java b/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/GraphQLInvocationData.java index bccdf911..42c3e53e 100644 --- a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/GraphQLInvocationData.java +++ b/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/GraphQLInvocationData.java @@ -1,12 +1,10 @@ package org.springframework.graphql; import graphql.Assert; -import graphql.PublicApi; import java.util.Collections; import java.util.Map; -@PublicApi public class GraphQLInvocationData { private final String query; diff --git a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/DefaultExecutionInputCustomizer.java b/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/DefaultExecutionInputCustomizer.java deleted file mode 100644 index 3c85c15e..00000000 --- a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/DefaultExecutionInputCustomizer.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.springframework.graphql.components; - -import graphql.ExecutionInput; -import graphql.Internal; -import org.springframework.graphql.ExecutionInputCustomizer; -import org.springframework.stereotype.Component; -import org.springframework.web.context.request.WebRequest; - -import java.util.concurrent.CompletableFuture; - -@Component -@Internal -public class DefaultExecutionInputCustomizer implements ExecutionInputCustomizer { - - @Override - public CompletableFuture customizeExecutionInput(ExecutionInput executionInput, WebRequest webRequest) { - return CompletableFuture.completedFuture(executionInput); - } -} diff --git a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/DefaultExecutionResultHandler.java b/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/DefaultExecutionResultHandler.java deleted file mode 100644 index bf69c095..00000000 --- a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/DefaultExecutionResultHandler.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.springframework.graphql.components; - -import com.fasterxml.jackson.databind.ObjectMapper; -import graphql.ExecutionResult; -import graphql.Internal; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.graphql.ExecutionResultHandler; -import org.springframework.stereotype.Component; - -import java.util.concurrent.CompletableFuture; - -@Component -@Internal -public class DefaultExecutionResultHandler implements ExecutionResultHandler { - - @Autowired - ObjectMapper objectMapper; - - @Override - public Object handleExecutionResult(CompletableFuture executionResultCF) { - return executionResultCF.thenApply(ExecutionResult::toSpecification); - } -} diff --git a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/DefaultGraphQLInvocation.java b/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/DefaultGraphQLInvocation.java deleted file mode 100644 index a56a23c9..00000000 --- a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/DefaultGraphQLInvocation.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.springframework.graphql.components; - -import graphql.ExecutionInput; -import graphql.ExecutionResult; -import graphql.GraphQL; -import graphql.Internal; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.graphql.ExecutionInputCustomizer; -import org.springframework.graphql.GraphQLInvocation; -import org.springframework.graphql.GraphQLInvocationData; -import org.springframework.stereotype.Component; -import org.springframework.web.context.request.WebRequest; - -import java.util.concurrent.CompletableFuture; - -@Component -@Internal -public class DefaultGraphQLInvocation implements GraphQLInvocation { - - @Autowired - GraphQL graphQL; - - @Autowired - ExecutionInputCustomizer executionInputCustomizer; - - @Override - public CompletableFuture invoke(GraphQLInvocationData invocationData, WebRequest webRequest) { - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .query(invocationData.getQuery()) - .operationName(invocationData.getOperationName()) - .variables(invocationData.getVariables()) - .build(); - CompletableFuture customizedExecutionInput = executionInputCustomizer.customizeExecutionInput(executionInput, webRequest); - return customizedExecutionInput.thenCompose(graphQL::executeAsync); - } - -} diff --git a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/GraphQLController.java b/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/GraphQLController.java index 1f24fc3a..914bea96 100644 --- a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/GraphQLController.java +++ b/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/GraphQLController.java @@ -1,43 +1,49 @@ package org.springframework.graphql.components; -import graphql.ExecutionResult; -import graphql.Internal; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.graphql.ExecutionResultHandler; -import org.springframework.graphql.GraphQLInvocation; +import org.springframework.context.annotation.Bean; import org.springframework.graphql.GraphQLInvocationData; -import org.springframework.http.MediaType; -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.RestController; -import org.springframework.web.context.request.WebRequest; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.function.RouterFunction; +import org.springframework.web.servlet.function.ServerRequest; +import org.springframework.web.servlet.function.ServerResponse; -import java.util.concurrent.CompletableFuture; +import java.io.IOException; -@RestController -@Internal +import static org.springframework.web.servlet.function.RouterFunctions.route; + +@Component public class GraphQLController { @Autowired - GraphQLInvocation graphQLInvocation; + GraphQLRequestHandler graphQLRequestHandler; - @Autowired - ExecutionResultHandler executionResultHandler; + @Bean + public RouterFunction routerFunction() { + RouterFunction route = route() + .POST("/graphql", this::graphqlPOST) + .build(); + return route; - @RequestMapping(value = "${graphql.url:graphql}", - method = RequestMethod.POST, - consumes = MediaType.APPLICATION_JSON_VALUE, - produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Object graphqlPOST(@RequestBody GraphQLRequestBody body, - WebRequest webRequest) { + } + + private ServerResponse graphqlPOST(ServerRequest serverRequest) { + GraphQLRequestBody body = null; + try { + body = serverRequest.body(GraphQLRequestBody.class); + } catch (javax.servlet.ServletException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } String query = body.getQuery(); if (query == null) { query = ""; } - CompletableFuture executionResult = graphQLInvocation.invoke(new GraphQLInvocationData(query, body.getOperationName(), body.getVariables()), webRequest); - return executionResultHandler.handleExecutionResult(executionResult); + GraphQLInvocationData invocationData = new GraphQLInvocationData(query, body.getOperationName(), body.getVariables()); + Object resultBody = graphQLRequestHandler.invoke(invocationData, serverRequest.headers()); + return ServerResponse.ok().body(resultBody); } } diff --git a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/GraphQLRequestBody.java b/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/GraphQLRequestBody.java index 10697e30..2cb1e6e3 100644 --- a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/GraphQLRequestBody.java +++ b/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/GraphQLRequestBody.java @@ -1,10 +1,7 @@ package org.springframework.graphql.components; -import graphql.Internal; - import java.util.Map; -@Internal public class GraphQLRequestBody { private String query; private String operationName; diff --git a/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/GraphQLRequestHandler.java b/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/GraphQLRequestHandler.java new file mode 100644 index 00000000..dd01bd50 --- /dev/null +++ b/graphql-java-spring-webmvc/src/main/java/org/springframework/graphql/components/GraphQLRequestHandler.java @@ -0,0 +1,55 @@ +package org.springframework.graphql.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.stereotype.Component; +import org.springframework.util.Assert; +import org.springframework.web.servlet.function.ServerRequest; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +@Component +public class GraphQLRequestHandler { + + @Autowired + private GraphQL graphQL; + + + public Object 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 customizedExecutionInput = customizeExecutionInput(executionInput, headers); + CompletableFuture executionResultCompletableFuture = customizedExecutionInput.thenCompose(graphQL::executeAsync); + return handleExecutionResult(executionResultCompletableFuture); + } + + protected CompletableFuture customizeExecutionInput(ExecutionInput executionInput, + ServerRequest.Headers headers) { + return CompletableFuture.completedFuture(executionInput); + } + + protected Object handleExecutionResult(CompletableFuture executionResultCF) { + if (executionResultCF.isDone()) { + return toSpecification(executionResultCF); + } + return executionResultCF.thenApply(ExecutionResult::toSpecification); + } + + private Map toSpecification(CompletableFuture executionResultCF) { + try { + return executionResultCF.get().toSpecification(); + } catch (Exception e) { + throw new RuntimeException("Should not happen", e); + } + } +}