From 200458136c0ae3a65b0cbcaba64c96278aa3b177 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 16 Nov 2022 15:56:15 +0100 Subject: [PATCH] Contribute GraphQL resources during AOT phase This commit ensures that the default locations for GraphQL schema files and the GraphiQL UI HTML page are included in the native image by registering resource hints during the AOT phase. Fixes gh-33208 --- .../graphql/GraphQlAutoConfiguration.java | 11 +++++++++++ .../reactive/GraphQlWebFluxAutoConfiguration.java | 13 +++++++++++++ .../servlet/GraphQlWebMvcAutoConfiguration.java | 13 +++++++++++++ .../graphql/GraphQlAutoConfigurationTests.java | 11 +++++++++++ .../GraphQlWebFluxAutoConfigurationTests.java | 9 +++++++++ .../GraphQlWebMvcAutoConfigurationTests.java | 9 +++++++++ 6 files changed, 66 insertions(+) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfiguration.java index 1850e0c364..4562dc8ae7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfiguration.java @@ -29,6 +29,8 @@ import graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -143,4 +145,13 @@ public class GraphQlAutoConfiguration { return controllerConfigurer; } + static class GraphQlResourcesRuntimeHints implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + hints.resources().registerPattern("graphql/*.graphqls").registerPattern("graphql/*.gqls"); + } + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java index 252609b2dc..b32ef4eb7c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java @@ -23,6 +23,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import reactor.core.publisher.Mono; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -37,6 +39,7 @@ import org.springframework.boot.autoconfigure.graphql.GraphQlProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.core.annotation.Order; import org.springframework.core.log.LogMessage; import org.springframework.graphql.ExecutionGraphQlService; @@ -79,6 +82,7 @@ import static org.springframework.web.reactive.function.server.RequestPredicates @ConditionalOnClass({ GraphQL.class, GraphQlHttpHandler.class }) @ConditionalOnBean(ExecutionGraphQlService.class) @EnableConfigurationProperties(GraphQlCorsProperties.class) +@ImportRuntimeHints(GraphQlWebFluxAutoConfiguration.GraphiQlResourceHints.class) public class GraphQlWebFluxAutoConfiguration { private static final RequestPredicate SUPPORTS_MEDIATYPES = accept(MediaType.APPLICATION_GRAPHQL, @@ -175,4 +179,13 @@ public class GraphQlWebFluxAutoConfiguration { } + static class GraphiQlResourceHints implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + hints.resources().registerPattern("graphiql/index.html"); + } + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java index d657ec6006..33470d92f8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java @@ -24,6 +24,8 @@ import jakarta.websocket.server.ServerContainer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -39,6 +41,7 @@ import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.core.annotation.Order; import org.springframework.core.log.LogMessage; import org.springframework.graphql.ExecutionGraphQlService; @@ -80,6 +83,7 @@ import org.springframework.web.socket.server.support.WebSocketHandlerMapping; @ConditionalOnClass({ GraphQL.class, GraphQlHttpHandler.class }) @ConditionalOnBean(ExecutionGraphQlService.class) @EnableConfigurationProperties(GraphQlCorsProperties.class) +@ImportRuntimeHints(GraphQlWebMvcAutoConfiguration.GraphiQlResourceHints.class) public class GraphQlWebMvcAutoConfiguration { private static final Log logger = LogFactory.getLog(GraphQlWebMvcAutoConfiguration.class); @@ -193,4 +197,13 @@ public class GraphQlWebMvcAutoConfiguration { } + static class GraphiQlResourceHints implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + hints.resources().registerPattern("graphiql/index.html"); + } + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfigurationTests.java index 662d442934..06425f5d09 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfigurationTests.java @@ -28,7 +28,10 @@ import graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility; import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration.GraphQlResourcesRuntimeHints; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -182,6 +185,14 @@ class GraphQlAutoConfigurationTests { }); } + @Test + void shouldRegisterHints() { + RuntimeHints hints = new RuntimeHints(); + new GraphQlResourcesRuntimeHints().registerHints(hints, getClass().getClassLoader()); + assertThat(RuntimeHintsPredicates.resource().forResource("graphql/sample/schema.gqls")).accepts(hints); + assertThat(RuntimeHintsPredicates.resource().forResource("graphql/other.graphqls")).accepts(hints); + } + @Configuration(proxyBeanMethods = false) static class CustomGraphQlBuilderConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfigurationTests.java index 0f06e12a99..ffdeac1aba 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfigurationTests.java @@ -23,6 +23,8 @@ import java.util.function.Consumer; import graphql.schema.idl.TypeRuntimeWiring; import org.junit.jupiter.api.Test; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration; import org.springframework.boot.autoconfigure.graphql.GraphQlTestDataFetchers; @@ -161,6 +163,13 @@ class GraphQlWebFluxAutoConfigurationTests { }); } + @Test + void shouldRegisterHints() { + RuntimeHints hints = new RuntimeHints(); + new GraphQlWebFluxAutoConfiguration.GraphiQlResourceHints().registerHints(hints, getClass().getClassLoader()); + assertThat(RuntimeHintsPredicates.resource().forResource("graphiql/index.html")).accepts(hints); + } + private void testWithWebClient(Consumer consumer) { this.contextRunner.run((context) -> { WebTestClient client = WebTestClient.bindToApplicationContext(context).configureClient() diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java index d75f6be455..a3e83069bd 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java @@ -22,6 +22,8 @@ import graphql.schema.idl.TypeRuntimeWiring; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration; import org.springframework.boot.autoconfigure.graphql.GraphQlTestDataFetchers; @@ -168,6 +170,13 @@ class GraphQlWebMvcAutoConfigurationTests { }); } + @Test + void shouldRegisterHints() { + RuntimeHints hints = new RuntimeHints(); + new GraphQlWebMvcAutoConfiguration.GraphiQlResourceHints().registerHints(hints, getClass().getClassLoader()); + assertThat(RuntimeHintsPredicates.resource().forResource("graphiql/index.html")).accepts(hints); + } + private void testWith(MockMvcConsumer mockMvcConsumer) { this.contextRunner.run((context) -> { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context).defaultRequest(