mirror of
https://github.com/jlengrand/quarkus.git
synced 2026-03-10 08:41:22 +00:00
feat(vertx-graphql): adds the possibility to control graphql-ui path and resources inclusion
This commit is contained in:
committed by
Guillaume Smet
parent
c5d04d8382
commit
7fab92439a
@@ -26,6 +26,16 @@
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-vertx-graphql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-junit5-internal</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* The value `/` is not allowed as it blocks the application from serving anything else.
|
||||
*/
|
||||
@ConfigItem(defaultValue = "/graphql-ui")
|
||||
String path;
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,31 @@ package io.quarkus.vertx.graphql.deployment;
|
||||
|
||||
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 @@ class VertxGraphqlProcessor {
|
||||
}
|
||||
|
||||
@BuildStep
|
||||
NativeImagePackageResourceBuildItem registerNativeImageResources() {
|
||||
return new NativeImagePackageResourceBuildItem("io/vertx/ext/web/handler/graphiql");
|
||||
@Record(ExecutionTime.STATIC_INIT)
|
||||
void registerVertxGraphqlUI(VertxGraphqlRecorder recorder,
|
||||
BuildProducer<NativeImageResourceDirectoryBuildItem> nativeResourcesProducer, VertxGraphqlConfig config,
|
||||
LaunchModeBuildItem launchMode, BuildProducer<NotFoundPageDisplayableEndpointBuildItem> displayableEndpoints,
|
||||
BuildProducer<RouteBuildItem> 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<RoutingContext> 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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<RoutingContext> handler(String path) {
|
||||
|
||||
GraphiQLHandlerOptions options = new GraphiQLHandlerOptions();
|
||||
options.setEnabled(true);
|
||||
|
||||
Handler<RoutingContext> handler = GraphiQLHandler.create(options);
|
||||
|
||||
return new Handler<RoutingContext>() {
|
||||
@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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user