From 0b93756865c7543de4d767d1e143267ef8134b09 Mon Sep 17 00:00:00 2001 From: Manyanda Chitimbo Date: Tue, 12 Nov 2019 07:33:21 +0100 Subject: [PATCH 1/4] feat: add a build item to register packages resources for native image --- ...NativeImageResourceDirectoryBuildItem.java | 19 ++++++++++ .../steps/NativeImageAutoFeatureStep.java | 37 +++++++++++++++++++ .../src/main/asciidoc/writing-extensions.adoc | 29 ++++++++------- 3 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageResourceDirectoryBuildItem.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageResourceDirectoryBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageResourceDirectoryBuildItem.java new file mode 100644 index 0000000000000..60847c404ac56 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageResourceDirectoryBuildItem.java @@ -0,0 +1,19 @@ +package io.quarkus.deployment.builditem.nativeimage; + +import io.quarkus.builder.item.MultiBuildItem; + +/** + * A build item that indicates that directory resources should be included in the native image + */ +public final class NativeImageResourceDirectoryBuildItem extends MultiBuildItem { + + private final String path; + + public NativeImageResourceDirectoryBuildItem(String path) { + this.path = path; + } + + public String getPath() { + return path; + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageAutoFeatureStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageAutoFeatureStep.java index 794114ce1b76d..4fd7fc1272246 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageAutoFeatureStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageAutoFeatureStep.java @@ -2,17 +2,26 @@ import static io.quarkus.gizmo.MethodDescriptor.ofMethod; +import java.io.File; +import java.io.IOException; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Enumeration; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; import java.util.stream.Collectors; import org.graalvm.nativeimage.ImageSingletons; @@ -22,10 +31,12 @@ import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.DeploymentClassLoaderBuildItem; import io.quarkus.deployment.builditem.GeneratedNativeImageClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageProxyDefinitionBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBundleBuildItem; +import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceDirectoryBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveFieldBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveMethodBuildItem; @@ -56,6 +67,32 @@ public class NativeImageAutoFeatureStep { static final String DYNAMIC_PROXY_REGISTRY = "com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry"; static final String LOCALIZATION_SUPPORT = "com.oracle.svm.core.jdk.LocalizationSupport"; + @BuildStep + List registerPackageResources( + List nativeImageResourceDirectories, + DeploymentClassLoaderBuildItem classLoader) + throws IOException, URISyntaxException { + List resources = new ArrayList<>(); + + for (NativeImageResourceDirectoryBuildItem nativeImageResourceDirectory : nativeImageResourceDirectories) { + String path = classLoader.getClassLoader().getResource(nativeImageResourceDirectory.getPath()).getPath(); + File resourceFile = Paths.get(new URL(path.substring(0, path.indexOf("!"))).toURI()).toFile(); + try (JarFile jarFile = new JarFile(resourceFile)) { + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String resourceName = entry.getName(); + if (!entry.isDirectory() && resourceName.startsWith(nativeImageResourceDirectory.getPath()) + && !resourceName.endsWith(".class")) { + resources.add(new NativeImageResourceBuildItem(resourceName)); + } + } + } + } + + return resources; + } + @BuildStep void generateFeature(BuildProducer nativeImageClass, List runtimeInitializedClassBuildItems, diff --git a/docs/src/main/asciidoc/writing-extensions.adoc b/docs/src/main/asciidoc/writing-extensions.adoc index 5b7a68781a322..7c368b3c20f57 100755 --- a/docs/src/main/asciidoc/writing-extensions.adoc +++ b/docs/src/main/asciidoc/writing-extensions.adoc @@ -1642,6 +1642,9 @@ executable. Some of these build items are listed below: `io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem`:: Includes static resources into the native executable. +`io.quarkus.deployment.builditem.nativeimage.NativeImageResourceDirectoryBuildItem`:: + Includes directory's static resources into the native executable. + `io.quarkus.deployment.builditem.nativeimage.RuntimeReinitializedClassBuildItem`:: A class that will be reinitialized at runtime by Substrate. This will result in the static initializer running twice. @@ -2099,16 +2102,16 @@ public final class MyExtProcessor { @BuildStep void registerNativeImageReources(BuildProducer services) { String service = "META-INF/services/" + io.quarkus.SomeService.class.getName(); - + // find out all the implementation classes listed in the service files - Set implementations = + Set implementations = ServiceUtil.classNamesNamedIn(Thread.currentThread().getContextClassLoader(), service); - // register every listed implementation class so they can be instantiated + // register every listed implementation class so they can be instantiated // in native-image at run-time services.produce( - new ServiceProviderBuildItem(io.quarkus.SomeService.class.getName(), + new ServiceProviderBuildItem(io.quarkus.SomeService.class.getName(), implementations.toArray(new String[0]))); } } @@ -2130,13 +2133,13 @@ public final class MyExtProcessor { void registerNativeImageReources(BuildProducer resource, BuildProducer reflectionClasses) { String service = "META-INF/services/" + io.quarkus.SomeService.class.getName(); - + // register the service file so it is visible in native-image resource.produce(new NativeImageResourceBuildItem(service)); - - // register every listed implementation class so they can be inspected/instantiated + + // register every listed implementation class so they can be inspected/instantiated // in native-image at run-time - Set implementations = + Set implementations = ServiceUtil.classNamesNamedIn(Thread.currentThread().getContextClassLoader(), service); reflectionClasses.produce( @@ -2148,7 +2151,7 @@ public final class MyExtProcessor { While this is the easiest way to get your services running natively, it's less efficient than scanning the implementation classes at build time and generating code that registers them at static-init time instead of relying on reflection. -You can achieve that by adapting the previous build step to use a static-init recorder instead of registering +You can achieve that by adapting the previous build step to use a static-init recorder instead of registering classes for reflection: [source,java] @@ -2157,19 +2160,19 @@ public final class MyExtProcessor { @BuildStep @Record(ExecutionTime.STATIC_INIT) - void registerNativeImageReources(RecorderContext recorderContext, + void registerNativeImageReources(RecorderContext recorderContext, SomeServiceRecorder recorder) { String service = "META-INF/services/" + io.quarkus.SomeService.class.getName(); - + // read the implementation classes Collection> implementationClasses = new LinkedHashSet<>(); Set implementations = ServiceUtil.classNamesNamedIn(Thread.currentThread().getContextClassLoader(), service); for(String implementation : implementations) { - implementationClasses.add((Class) + implementationClasses.add((Class) recorderContext.classProxy(implementation)); } - + // produce a static-initializer with those classes recorder.configure(implementationClasses); } From c5d04d838276733618b87f1d5d0ee0993501d87f Mon Sep 17 00:00:00 2001 From: Manyanda Chitimbo Date: Tue, 12 Nov 2019 20:04:08 +0100 Subject: [PATCH 2/4] fix(vertx-graphql): make the extension work in native mode fixes #5248 --- ci-templates/stages.yml | 3 ++- .../deployment/VertxGraphqlProcessor.java | 22 ++++++++++++++++++- .../vertx/graphql/it/VertxGraphqlTest.java | 20 ++++++++++++----- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/ci-templates/stages.yml b/ci-templates/stages.yml index e2f47e4bb51f5..0d8abbfcf7c92 100644 --- a/ci-templates/stages.yml +++ b/ci-templates/stages.yml @@ -310,11 +310,12 @@ stages: parameters: poolSettings: ${{parameters.poolSettings}} expectUseVMs: ${{parameters.expectUseVMs}} - timeoutInMinutes: 25 + timeoutInMinutes: 30 modules: - resteasy-jackson - vertx - vertx-http + - vertx-graphql - virtual-http name: http diff --git a/extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlProcessor.java b/extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlProcessor.java index 453a354cd0a52..94e39a0bf8766 100644 --- a/extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlProcessor.java +++ b/extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlProcessor.java @@ -1,11 +1,18 @@ package io.quarkus.vertx.graphql.deployment; +import java.util.Arrays; +import java.util.List; + import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.nativeimage.NativeImagePackageResourceBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.vertx.http.deployment.WebsocketSubProtocolsBuildItem; +import io.vertx.ext.web.handler.graphql.impl.GraphQLBatch; +import io.vertx.ext.web.handler.graphql.impl.GraphQLInputDeserializer; +import io.vertx.ext.web.handler.graphql.impl.GraphQLQuery; class VertxGraphqlProcessor { - @BuildStep FeatureBuildItem feature() { return new FeatureBuildItem(FeatureBuildItem.VERTX_GRAPHQL); @@ -15,4 +22,17 @@ FeatureBuildItem feature() { WebsocketSubProtocolsBuildItem websocketSubProtocols() { return new WebsocketSubProtocolsBuildItem("graphql-ws"); } + + @BuildStep + List registerForReflection() { + return Arrays.asList( + new ReflectiveClassBuildItem(true, true, GraphQLInputDeserializer.class.getName()), + new ReflectiveClassBuildItem(true, true, GraphQLBatch.class.getName()), + new ReflectiveClassBuildItem(true, true, GraphQLQuery.class.getName())); + } + + @BuildStep + NativeImagePackageResourceBuildItem registerNativeImageResources() { + return new NativeImagePackageResourceBuildItem("io/vertx/ext/web/handler/graphiql"); + } } diff --git a/integration-tests/vertx-graphql/src/test/java/io/quarkus/vertx/graphql/it/VertxGraphqlTest.java b/integration-tests/vertx-graphql/src/test/java/io/quarkus/vertx/graphql/it/VertxGraphqlTest.java index a2cdaba2349b7..dc01f4c0d248e 100644 --- a/integration-tests/vertx-graphql/src/test/java/io/quarkus/vertx/graphql/it/VertxGraphqlTest.java +++ b/integration-tests/vertx-graphql/src/test/java/io/quarkus/vertx/graphql/it/VertxGraphqlTest.java @@ -6,10 +6,10 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -import javax.inject.Inject; - import org.eclipse.microprofile.config.ConfigProvider; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import io.quarkus.test.junit.QuarkusTest; @@ -20,14 +20,22 @@ @QuarkusTest class VertxGraphqlTest { - - @Inject - Vertx vertx; - public static int getPortFromConfig() { return ConfigProvider.getConfig().getOptionalValue("quarkus.http.test-port", Integer.class).orElse(8081); } + private static Vertx vertx; + + @BeforeAll + public static void initializeVertx() { + vertx = Vertx.vertx(); + } + + @AfterAll + public static void closeVertx() { + vertx.close(); + } + @Test public void testGraphQlQuery() { given().contentType(ContentType.JSON).body("{ \"query\" : \"{ hello }\" }") From 7fab92439a57e198c6d67fded223d5603e8249a1 Mon Sep 17 00:00:00 2001 From: Manyanda Chitimbo Date: Thu, 14 Nov 2019 12:51:02 +0100 Subject: [PATCH 3/4] feat(vertx-graphql): adds the possibility to control graphql-ui path and resources inclusion --- extensions/vertx-graphql/deployment/pom.xml | 10 +++++ .../deployment/VertxGraphqlConfig.java | 32 ++++++++++++++ .../deployment/VertxGraphqlProcessor.java | 42 +++++++++++++++++-- .../deployment/ErroneousConfigTest.java | 25 +++++++++++ .../ServingUIFromCustomPathTest.java | 23 ++++++++++ .../ServingUIFromDefaultPathTest.java | 21 ++++++++++ .../graphql/runtime/VertxGraphqlRecorder.java | 34 +++++++++++++++ 7 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlConfig.java create mode 100644 extensions/vertx-graphql/deployment/src/test/java/io/quarkus/vertx/graphql/deployment/ErroneousConfigTest.java create mode 100644 extensions/vertx-graphql/deployment/src/test/java/io/quarkus/vertx/graphql/deployment/ServingUIFromCustomPathTest.java create mode 100644 extensions/vertx-graphql/deployment/src/test/java/io/quarkus/vertx/graphql/deployment/ServingUIFromDefaultPathTest.java create mode 100644 extensions/vertx-graphql/runtime/src/main/java/io/quarkus/vertx/graphql/runtime/VertxGraphqlRecorder.java diff --git a/extensions/vertx-graphql/deployment/pom.xml b/extensions/vertx-graphql/deployment/pom.xml index 2bd6495c005c9..0b2217a09f08d 100644 --- a/extensions/vertx-graphql/deployment/pom.xml +++ b/extensions/vertx-graphql/deployment/pom.xml @@ -26,6 +26,16 @@ io.quarkus quarkus-vertx-graphql + + io.quarkus + quarkus-junit5-internal + test + + + io.rest-assured + rest-assured + test + diff --git a/extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlConfig.java b/extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlConfig.java new file mode 100644 index 0000000000000..3e84627bf9783 --- /dev/null +++ b/extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlConfig.java @@ -0,0 +1,32 @@ +package io.quarkus.vertx.graphql.deployment; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigRoot; + +@ConfigRoot +public final class VertxGraphqlConfig { + /** + * GraphQL UI configuration + */ + @ConfigItem + VertxGraphqlUiConfig ui; + + @ConfigGroup + public static class VertxGraphqlUiConfig { + /** + * If GraphQL UI should be included every time. By default this is only included when the application is running + * in dev mode. + */ + @ConfigItem(defaultValue = "false") + boolean alwaysInclude; + + /** + * The path where GraphQL UI is available. + *

+ * The value `/` is not allowed as it blocks the application from serving anything else. + */ + @ConfigItem(defaultValue = "/graphql-ui") + String path; + } +} diff --git a/extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlProcessor.java b/extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlProcessor.java index 94e39a0bf8766..f14127eec0658 100644 --- a/extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlProcessor.java +++ b/extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlProcessor.java @@ -2,17 +2,31 @@ import java.util.Arrays; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.FeatureBuildItem; -import io.quarkus.deployment.builditem.nativeimage.NativeImagePackageResourceBuildItem; +import io.quarkus.deployment.builditem.LaunchModeBuildItem; +import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceDirectoryBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.runtime.configuration.ConfigurationException; +import io.quarkus.vertx.graphql.runtime.VertxGraphqlRecorder; +import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.quarkus.vertx.http.deployment.WebsocketSubProtocolsBuildItem; +import io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem; +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.handler.graphql.impl.GraphQLBatch; import io.vertx.ext.web.handler.graphql.impl.GraphQLInputDeserializer; import io.vertx.ext.web.handler.graphql.impl.GraphQLQuery; class VertxGraphqlProcessor { + private static Pattern TRAILING_SLASH_SUFFIX_REGEX = Pattern.compile("/+$"); + @BuildStep FeatureBuildItem feature() { return new FeatureBuildItem(FeatureBuildItem.VERTX_GRAPHQL); @@ -32,7 +46,29 @@ List registerForReflection() { } @BuildStep - NativeImagePackageResourceBuildItem registerNativeImageResources() { - return new NativeImagePackageResourceBuildItem("io/vertx/ext/web/handler/graphiql"); + @Record(ExecutionTime.STATIC_INIT) + void registerVertxGraphqlUI(VertxGraphqlRecorder recorder, + BuildProducer nativeResourcesProducer, VertxGraphqlConfig config, + LaunchModeBuildItem launchMode, BuildProducer displayableEndpoints, + BuildProducer routes) { + + boolean includeVertxGraphqlUi = launchMode.getLaunchMode().isDevOrTest() || config.ui.alwaysInclude; + if (!includeVertxGraphqlUi) { + return; + } + + Matcher matcher = TRAILING_SLASH_SUFFIX_REGEX.matcher(config.ui.path); + String path = matcher.replaceAll(""); + if (path.isEmpty()) { + throw new ConfigurationException( + "quarkus.vertx-graphql.ui.path was set to \"" + config.ui.path + + "\", this is not allowed as it blocks the application from serving anything else."); + } + + Handler handler = recorder.handler(path); + routes.produce(new RouteBuildItem(path, handler)); + routes.produce(new RouteBuildItem(path + "/*", handler)); + displayableEndpoints.produce(new NotFoundPageDisplayableEndpointBuildItem(path + "/")); + nativeResourcesProducer.produce(new NativeImageResourceDirectoryBuildItem("io/vertx/ext/web/handler/graphiql")); } } diff --git a/extensions/vertx-graphql/deployment/src/test/java/io/quarkus/vertx/graphql/deployment/ErroneousConfigTest.java b/extensions/vertx-graphql/deployment/src/test/java/io/quarkus/vertx/graphql/deployment/ErroneousConfigTest.java new file mode 100644 index 0000000000000..23fa0e18ddbc7 --- /dev/null +++ b/extensions/vertx-graphql/deployment/src/test/java/io/quarkus/vertx/graphql/deployment/ErroneousConfigTest.java @@ -0,0 +1,25 @@ +package io.quarkus.vertx.graphql.deployment; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.runtime.configuration.ConfigurationException; +import io.quarkus.test.QuarkusUnitTest; + +public class ErroneousConfigTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setExpectedException(ConfigurationException.class) + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addAsResource(new StringAsset("quarkus.vertx-graphql.ui.path=/\n"), "application.properties")); + + @Test + public void shouldNotStartApplicationIfPathIsASlash() { + Assertions.fail(); + } +} diff --git a/extensions/vertx-graphql/deployment/src/test/java/io/quarkus/vertx/graphql/deployment/ServingUIFromCustomPathTest.java b/extensions/vertx-graphql/deployment/src/test/java/io/quarkus/vertx/graphql/deployment/ServingUIFromCustomPathTest.java new file mode 100644 index 0000000000000..feddb7f11b575 --- /dev/null +++ b/extensions/vertx-graphql/deployment/src/test/java/io/quarkus/vertx/graphql/deployment/ServingUIFromCustomPathTest.java @@ -0,0 +1,23 @@ +package io.quarkus.vertx.graphql.deployment; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class ServingUIFromCustomPathTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addAsResource(new StringAsset("quarkus.vertx-graphql.ui.path=/custom\n"), "application.properties")); + + @Test + public void shouldServeVertxGraphqlUiFromCustomPath() { + RestAssured.when().get("/custom").then().statusCode(200); + } +} diff --git a/extensions/vertx-graphql/deployment/src/test/java/io/quarkus/vertx/graphql/deployment/ServingUIFromDefaultPathTest.java b/extensions/vertx-graphql/deployment/src/test/java/io/quarkus/vertx/graphql/deployment/ServingUIFromDefaultPathTest.java new file mode 100644 index 0000000000000..4eb9cd8110313 --- /dev/null +++ b/extensions/vertx-graphql/deployment/src/test/java/io/quarkus/vertx/graphql/deployment/ServingUIFromDefaultPathTest.java @@ -0,0 +1,21 @@ +package io.quarkus.vertx.graphql.deployment; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class ServingUIFromDefaultPathTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void shouldServeVertxGraphqlUiFromDefaultPath() { + RestAssured.when().get("/graphql-ui").then().statusCode(200); + } +} diff --git a/extensions/vertx-graphql/runtime/src/main/java/io/quarkus/vertx/graphql/runtime/VertxGraphqlRecorder.java b/extensions/vertx-graphql/runtime/src/main/java/io/quarkus/vertx/graphql/runtime/VertxGraphqlRecorder.java new file mode 100644 index 0000000000000..a720daf837cc3 --- /dev/null +++ b/extensions/vertx-graphql/runtime/src/main/java/io/quarkus/vertx/graphql/runtime/VertxGraphqlRecorder.java @@ -0,0 +1,34 @@ +package io.quarkus.vertx.graphql.runtime; + +import io.quarkus.runtime.annotations.Recorder; +import io.vertx.core.Handler; +import io.vertx.core.http.HttpHeaders; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.graphql.GraphiQLHandler; +import io.vertx.ext.web.handler.graphql.GraphiQLHandlerOptions; + +@Recorder +public class VertxGraphqlRecorder { + public Handler handler(String path) { + + GraphiQLHandlerOptions options = new GraphiQLHandlerOptions(); + options.setEnabled(true); + + Handler handler = GraphiQLHandler.create(options); + + return new Handler() { + @Override + public void handle(RoutingContext event) { + if (event.normalisedPath().length() == path.length()) { + + event.response().setStatusCode(302); + event.response().headers().set(HttpHeaders.LOCATION, path + "/"); + event.response().end(); + return; + } + + handler.handle(event); + } + }; + } +} From 3db09bd29020a207a8aa1cc5b12371dfca4d35fd Mon Sep 17 00:00:00 2001 From: Manyanda Chitimbo Date: Mon, 18 Nov 2019 17:29:43 +0100 Subject: [PATCH 4/4] test: properly close vertx instances --- .../java/io/quarkus/mailer/runtime/MailerImplTest.java | 2 +- .../java/io/quarkus/mailer/runtime/MockMailerImplTest.java | 2 +- .../java/io/quarkus/vertx/graphql/it/VertxGraphqlTest.java | 7 +++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/MailerImplTest.java b/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/MailerImplTest.java index 73f794af8c466..b289520a3ff13 100644 --- a/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/MailerImplTest.java +++ b/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/MailerImplTest.java @@ -48,7 +48,7 @@ static void startWiser() { @AfterAll static void stopWiser() { wiser.stop(); - vertx.close(); + vertx.close().toCompletableFuture().join(); } @BeforeEach diff --git a/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/MockMailerImplTest.java b/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/MockMailerImplTest.java index 1df77c571bf40..e10a92f73a42a 100644 --- a/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/MockMailerImplTest.java +++ b/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/MockMailerImplTest.java @@ -32,7 +32,7 @@ static void start() { @AfterAll static void stop() { - vertx.close(); + vertx.close().toCompletableFuture().join(); } @BeforeEach diff --git a/integration-tests/vertx-graphql/src/test/java/io/quarkus/vertx/graphql/it/VertxGraphqlTest.java b/integration-tests/vertx-graphql/src/test/java/io/quarkus/vertx/graphql/it/VertxGraphqlTest.java index dc01f4c0d248e..4ee948d76791e 100644 --- a/integration-tests/vertx-graphql/src/test/java/io/quarkus/vertx/graphql/it/VertxGraphqlTest.java +++ b/integration-tests/vertx-graphql/src/test/java/io/quarkus/vertx/graphql/it/VertxGraphqlTest.java @@ -4,6 +4,7 @@ import static org.hamcrest.CoreMatchers.is; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.eclipse.microprofile.config.ConfigProvider; @@ -32,8 +33,10 @@ public static void initializeVertx() { } @AfterAll - public static void closeVertx() { - vertx.close(); + public static void closeVertx() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + vertx.close((h) -> latch.countDown()); + latch.await(); } @Test