Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow disabling the Swagger/GraphQL/Health/OpenAPI UIs at Runtime #13016

Merged
merged 1 commit into from
Nov 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.quarkus.smallrye.graphql.deployment;

import io.quarkus.builder.item.SimpleBuildItem;

final class SmallRyeGraphQLBuildItem extends SimpleBuildItem {

private final String graphqlUiFinalDestination;
private final String graphqlUiPath;

public SmallRyeGraphQLBuildItem(String graphqlUiFinalDestination, String graphqlUiPath) {
this.graphqlUiFinalDestination = graphqlUiFinalDestination;
this.graphqlUiPath = graphqlUiPath;
}

public String getGraphqlUiFinalDestination() {
return graphqlUiFinalDestination;
}

public String getGraphqlUiPath() {
return graphqlUiPath;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.metrics.MetricsFactory;
import io.quarkus.smallrye.graphql.runtime.SmallRyeGraphQLRecorder;
import io.quarkus.smallrye.graphql.runtime.SmallRyeGraphQLRuntimeConfig;
import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem;
import io.quarkus.vertx.http.deployment.RequireBodyHandlerBuildItem;
import io.quarkus.vertx.http.deployment.RouteBuildItem;
Expand Down Expand Up @@ -92,8 +93,6 @@ public class SmallRyeGraphQLProcessor {
private static final String LINE_TO_UPDATE = "const api = '";
private static final String LINE_FORMAT = LINE_TO_UPDATE + "%s';";

SmallRyeGraphQLConfig quarkusConfig;

@BuildStep
void feature(BuildProducer<FeatureBuildItem> featureProducer) {
featureProducer.produce(new FeatureBuildItem(Feature.SMALLRYE_GRAPHQL));
Expand Down Expand Up @@ -145,11 +144,12 @@ void buildExecutionService(
BuildProducer<ReflectiveHierarchyBuildItem> reflectiveHierarchyProducer,
SmallRyeGraphQLRecorder recorder,
BeanContainerBuildItem beanContainer,
CombinedIndexBuildItem combinedIndex) {
CombinedIndexBuildItem combinedIndex,
SmallRyeGraphQLConfig graphQLConfig) {

IndexView index = combinedIndex.getIndex();

Schema schema = SchemaBuilder.build(index, quarkusConfig.autoNameStrategy);
Schema schema = SchemaBuilder.build(index, graphQLConfig.autoNameStrategy);

recorder.createExecutionService(beanContainer.getValue(), schema);

Expand All @@ -166,14 +166,27 @@ void requireBody(BuildProducer<RequireBodyHandlerBuildItem> requireBodyHandlerPr
requireBodyHandlerProducer.produce(new RequireBodyHandlerBuildItem());
}

@Record(ExecutionTime.RUNTIME_INIT)
@BuildStep
void buildSchemaEndpoint(
BuildProducer<RouteBuildItem> routeProducer,
SmallRyeGraphQLRecorder recorder,
SmallRyeGraphQLConfig graphQLConfig) {

Handler<RoutingContext> schemaHandler = recorder.schemaHandler();
routeProducer.produce(
new RouteBuildItem(graphQLConfig.rootPath + SCHEMA_PATH, schemaHandler, HandlerType.BLOCKING));
}

@Record(ExecutionTime.STATIC_INIT)
@BuildStep
void buildEndpoints(
void buildExecutionEndpoint(
BuildProducer<RouteBuildItem> routeProducer,
BuildProducer<NotFoundPageDisplayableEndpointBuildItem> notFoundPageDisplayableEndpointProducer,
LaunchModeBuildItem launchMode,
SmallRyeGraphQLRecorder recorder,
ShutdownContextBuildItem shutdownContext,
LaunchModeBuildItem launchMode,
SmallRyeGraphQLConfig graphQLConfig,
BeanContainerBuildItem beanContainerBuildItem // don't remove this - makes sure beanContainer is initialized
) {

Expand All @@ -192,19 +205,16 @@ void buildEndpoints(
// add graphql endpoint for not found display in dev or test mode
if (launchMode.getLaunchMode().isDevOrTest()) {
notFoundPageDisplayableEndpointProducer
.produce(new NotFoundPageDisplayableEndpointBuildItem(quarkusConfig.rootPath));
.produce(new NotFoundPageDisplayableEndpointBuildItem(graphQLConfig.rootPath));
notFoundPageDisplayableEndpointProducer
.produce(new NotFoundPageDisplayableEndpointBuildItem(quarkusConfig.rootPath + SCHEMA_PATH));
.produce(new NotFoundPageDisplayableEndpointBuildItem(graphQLConfig.rootPath + SCHEMA_PATH));
}

Boolean allowGet = ConfigProvider.getConfig().getOptionalValue(ConfigKey.ALLOW_GET, boolean.class).orElse(false);

Handler<RoutingContext> executionHandler = recorder.executionHandler(allowGet);
routeProducer.produce(new RouteBuildItem(quarkusConfig.rootPath, executionHandler, HandlerType.BLOCKING));
routeProducer.produce(new RouteBuildItem(graphQLConfig.rootPath, executionHandler, HandlerType.BLOCKING));

Handler<RoutingContext> schemaHandler = recorder.schemaHandler();
routeProducer.produce(
new RouteBuildItem(quarkusConfig.rootPath + SCHEMA_PATH, schemaHandler, HandlerType.BLOCKING));
}

private String[] getSchemaJavaClasses(Schema schema) {
Expand Down Expand Up @@ -318,11 +328,12 @@ private Set<String> getAllReferenceClasses(Reference reference) {
@BuildStep
void activateMetrics(Capabilities capabilities,
Optional<MetricsCapabilityBuildItem> metricsCapability,
SmallRyeGraphQLConfig graphQLConfig,
BuildProducer<SystemPropertyBuildItem> systemProperties,
BuildProducer<UnremovableBeanBuildItem> unremovableBeans) {

boolean activate = shouldActivateService(capabilities,
quarkusConfig.metricsEnabled,
graphQLConfig.metricsEnabled,
metricsCapability.isPresent(),
"quarkus-smallrye-metrics",
"metrics",
Expand All @@ -339,10 +350,11 @@ void activateMetrics(Capabilities capabilities,

@BuildStep
void activateTracing(Capabilities capabilities,
SmallRyeGraphQLConfig graphQLConfig,
BuildProducer<SystemPropertyBuildItem> systemProperties) {

boolean activate = shouldActivateService(capabilities,
quarkusConfig.tracingEnabled,
graphQLConfig.tracingEnabled,
"quarkus-smallrye-opentracing",
Capability.OPENTRACING,
"quarkus.smallrye-graphql.tracing.enabled");
Expand All @@ -355,10 +367,11 @@ void activateTracing(Capabilities capabilities,

@BuildStep
void activateValidation(Capabilities capabilities,
SmallRyeGraphQLConfig graphQLConfig,
BuildProducer<SystemPropertyBuildItem> systemProperties) {

boolean activate = shouldActivateService(capabilities,
quarkusConfig.validationEnabled,
graphQLConfig.validationEnabled,
"quarkus-hibernate-validator",
Capability.HIBERNATE_VALIDATOR,
"quarkus.smallrye-graphql.validation.enabled");
Expand All @@ -370,8 +383,8 @@ void activateValidation(Capabilities capabilities,
}

@BuildStep
void activateEventing(BuildProducer<SystemPropertyBuildItem> systemProperties) {
if (quarkusConfig.eventsEnabled) {
void activateEventing(SmallRyeGraphQLConfig graphQLConfig, BuildProducer<SystemPropertyBuildItem> systemProperties) {
if (graphQLConfig.eventsEnabled) {
systemProperties.produce(new SystemPropertyBuildItem(ConfigKey.ENABLE_EVENTS, TRUE));
} else {
systemProperties.produce(new SystemPropertyBuildItem(ConfigKey.ENABLE_EVENTS, FALSE));
Expand Down Expand Up @@ -412,63 +425,80 @@ private boolean shouldActivateService(Capabilities capabilities,
// UI Related

@BuildStep
@Record(ExecutionTime.STATIC_INIT)
void registerGraphQLUiServletExtension(
BuildProducer<RouteBuildItem> routeProducer,
void getGraphqlUiFinalDestination(
BuildProducer<GeneratedResourceBuildItem> generatedResourceProducer,
BuildProducer<NativeImageResourceBuildItem> nativeImageResourceProducer,
BuildProducer<NotFoundPageDisplayableEndpointBuildItem> notFoundPageDisplayableEndpointProducer,
SmallRyeGraphQLRecorder recorder,
LaunchModeBuildItem launchMode,
BuildProducer<SmallRyeGraphQLBuildItem> smallRyeGraphQLBuildProducer,
HttpRootPathBuildItem httpRootPath,
CurateOutcomeBuildItem curateOutcomeBuildItem) throws Exception {
CurateOutcomeBuildItem curateOutcomeBuildItem,
LaunchModeBuildItem launchMode,
SmallRyeGraphQLConfig graphQLConfig) throws Exception {

if (!quarkusConfig.ui.enable) {
return;
}
if ("/".equals(quarkusConfig.ui.rootPath)) {
throw new ConfigurationError(
"quarkus.smallrye-graphql.root-path-ui was set to \"/\", this is not allowed as it blocks the application from serving anything else.");
}
if (shouldInclude(launchMode, graphQLConfig)) {

if ("/".equals(graphQLConfig.ui.rootPath)) {
throw new ConfigurationError(
"quarkus.smallrye-graphql.root-path-ui was set to \"/\", this is not allowed as it blocks the application from serving anything else.");
}

String graphQLPath = httpRootPath.adjustPath(quarkusConfig.rootPath);
String graphQLPath = httpRootPath.adjustPath(graphQLConfig.rootPath);

AppArtifact artifact = WebJarUtil.getAppArtifact(curateOutcomeBuildItem, GRAPHQL_UI_WEBJAR_GROUP_ID,
GRAPHQL_UI_WEBJAR_ARTIFACT_ID);
if (launchMode.getLaunchMode().isDevOrTest()) {
Path tempPath = WebJarUtil.devOrTest(curateOutcomeBuildItem, launchMode, artifact, GRAPHQL_UI_WEBJAR_PREFIX);
WebJarUtil.updateUrl(tempPath.resolve(FILE_TO_UPDATE), graphQLPath, LINE_TO_UPDATE, LINE_FORMAT);
AppArtifact artifact = WebJarUtil.getAppArtifact(curateOutcomeBuildItem, GRAPHQL_UI_WEBJAR_GROUP_ID,
GRAPHQL_UI_WEBJAR_ARTIFACT_ID);
if (launchMode.getLaunchMode().isDevOrTest()) {
Path tempPath = WebJarUtil.devOrTest(curateOutcomeBuildItem, launchMode, artifact, GRAPHQL_UI_WEBJAR_PREFIX);
WebJarUtil.updateUrl(tempPath.resolve(FILE_TO_UPDATE), graphQLPath, LINE_TO_UPDATE, LINE_FORMAT);

Handler<RoutingContext> handler = recorder.uiHandler(tempPath.toAbsolutePath().toString(),
httpRootPath.adjustPath(quarkusConfig.ui.rootPath));
routeProducer.produce(new RouteBuildItem(quarkusConfig.ui.rootPath, handler));
routeProducer.produce(new RouteBuildItem(quarkusConfig.ui.rootPath + "/*", handler));
notFoundPageDisplayableEndpointProducer
.produce(new NotFoundPageDisplayableEndpointBuildItem(quarkusConfig.ui.rootPath + "/"));
smallRyeGraphQLBuildProducer.produce(new SmallRyeGraphQLBuildItem(tempPath.toAbsolutePath().toString(),
httpRootPath.adjustPath(graphQLConfig.ui.rootPath)));
notFoundPageDisplayableEndpointProducer
.produce(new NotFoundPageDisplayableEndpointBuildItem(graphQLConfig.ui.rootPath + "/"));

} else if (quarkusConfig.ui.alwaysInclude) {
} else {
Map<String, byte[]> files = WebJarUtil.production(curateOutcomeBuildItem, artifact, GRAPHQL_UI_WEBJAR_PREFIX);

Map<String, byte[]> files = WebJarUtil.production(curateOutcomeBuildItem, artifact, GRAPHQL_UI_WEBJAR_PREFIX);
for (Map.Entry<String, byte[]> file : files.entrySet()) {

for (Map.Entry<String, byte[]> file : files.entrySet()) {
String fileName = file.getKey();
byte[] content = file.getValue();
if (fileName.endsWith(FILE_TO_UPDATE)) {
content = WebJarUtil
.updateUrl(new String(content, StandardCharsets.UTF_8), graphQLPath, LINE_TO_UPDATE,
LINE_FORMAT)
.getBytes(StandardCharsets.UTF_8);
}
fileName = GRAPHQL_UI_FINAL_DESTINATION + "/" + fileName;

String fileName = file.getKey();
byte[] content = file.getValue();
if (fileName.endsWith(FILE_TO_UPDATE)) {
content = WebJarUtil
.updateUrl(new String(content, StandardCharsets.UTF_8), graphQLPath, LINE_TO_UPDATE, LINE_FORMAT)
.getBytes(StandardCharsets.UTF_8);
generatedResourceProducer.produce(new GeneratedResourceBuildItem(fileName, content));
nativeImageResourceProducer.produce(new NativeImageResourceBuildItem(fileName));
}
fileName = GRAPHQL_UI_FINAL_DESTINATION + "/" + fileName;

generatedResourceProducer.produce(new GeneratedResourceBuildItem(fileName, content));
nativeImageResourceProducer.produce(new NativeImageResourceBuildItem(fileName));
smallRyeGraphQLBuildProducer.produce(new SmallRyeGraphQLBuildItem(GRAPHQL_UI_FINAL_DESTINATION,
httpRootPath.adjustPath(graphQLConfig.ui.rootPath)));
}
}
}

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void registerGraphQLUiHandler(
BuildProducer<RouteBuildItem> routeProducer,
SmallRyeGraphQLRecorder recorder,
SmallRyeGraphQLRuntimeConfig runtimeConfig,
SmallRyeGraphQLBuildItem smallRyeGraphQLBuildItem,
LaunchModeBuildItem launchMode,
SmallRyeGraphQLConfig graphQLConfig) throws Exception {

Handler<RoutingContext> handler = recorder
.uiHandler(GRAPHQL_UI_FINAL_DESTINATION, httpRootPath.adjustPath(quarkusConfig.ui.rootPath));
routeProducer.produce(new RouteBuildItem(quarkusConfig.ui.rootPath, handler));
routeProducer.produce(new RouteBuildItem(quarkusConfig.ui.rootPath + "/*", handler));
if (shouldInclude(launchMode, graphQLConfig)) {
Handler<RoutingContext> handler = recorder.uiHandler(smallRyeGraphQLBuildItem.getGraphqlUiFinalDestination(),
smallRyeGraphQLBuildItem.getGraphqlUiPath(), runtimeConfig);
routeProducer.produce(new RouteBuildItem(graphQLConfig.ui.rootPath, handler));
routeProducer.produce(new RouteBuildItem(graphQLConfig.ui.rootPath + "/*", handler));
}
}

private static boolean shouldInclude(LaunchModeBuildItem launchMode, SmallRyeGraphQLConfig graphQLConfig) {
return launchMode.getLaunchMode().isDevOrTest() || graphQLConfig.ui.alwaysInclude;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,4 @@ public class SmallRyeGraphQLUIConfig {
*/
@ConfigItem(defaultValue = "false")
boolean alwaysInclude;

/**
* If GraphQL UI should be enabled. By default, GraphQL UI is enabled.
*/
@ConfigItem(defaultValue = "true")
boolean enable;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.quarkus.smallrye.graphql.runtime;

import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;

/**
* Handling static when disabled
*/
public class SmallRyeGraphQLNotFoundHandler implements Handler<RoutingContext> {

@Override
public void handle(RoutingContext event) {
event.response().setStatusCode(404);
event.response().end();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
import io.smallrye.graphql.cdi.producer.GraphQLProducer;
import io.smallrye.graphql.schema.model.Schema;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpHeaders;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.StaticHandler;

@Recorder
public class SmallRyeGraphQLRecorder {
Expand Down Expand Up @@ -43,29 +41,14 @@ public Handler<RoutingContext> schemaHandler() {
return new SmallRyeGraphQLSchemaHandler();
}

public Handler<RoutingContext> uiHandler(String graphqlUiFinalDestination, String graphqlUiPath) {
public Handler<RoutingContext> uiHandler(String graphqlUiFinalDestination,
String graphqlUiPath, SmallRyeGraphQLRuntimeConfig runtimeConfig) {

StaticHandler staticHandler = StaticHandler.create().setAllowRootFileSystemAccess(true)
.setWebRoot(graphqlUiFinalDestination)
.setDefaultContentEncoding("UTF-8");

return new Handler<RoutingContext>() {
@Override
public void handle(RoutingContext event) {
if (event.normalisedPath().length() == graphqlUiPath.length()) {

event.response().setStatusCode(302);
event.response().headers().set(HttpHeaders.LOCATION, graphqlUiPath + "/");
event.response().end();
return;
} else if (event.normalisedPath().length() == graphqlUiPath.length() + 1) {
event.reroute(graphqlUiPath + "/index.html");
return;
}

staticHandler.handle(event);
}
};
if (runtimeConfig.enable) {
return new SmallRyeGraphQLStaticHandler(graphqlUiFinalDestination, graphqlUiPath);
} else {
return new SmallRyeGraphQLNotFoundHandler();
}
}

public void setupClDevMode(ShutdownContext shutdownContext) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.quarkus.smallrye.graphql.runtime;

import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;

@ConfigRoot(name = "smallrye-graphql", phase = ConfigPhase.RUN_TIME)
public class SmallRyeGraphQLRuntimeConfig {

/**
* If GraphQL UI should be enabled. By default, GraphQL UI is enabled if it is included (see {@code always-include}).
*/
@ConfigItem(name = "ui.enable", defaultValue = "true")
boolean enable;

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ public class SmallRyeGraphQLSchemaHandler implements Handler<RoutingContext> {

@Override
public void handle(RoutingContext event) {
HttpServerRequest request = event.request();
HttpServerResponse response = event.response();

GraphQLSchema graphQLSchema = CDI.current().select(GraphQLSchema.class).get();
SchemaPrinter schemaPrinter = CDI.current().select(SchemaPrinter.class).get();

String schemaString = schemaPrinter.print(graphQLSchema);

HttpServerRequest request = event.request();
HttpServerResponse response = event.response();
if (request.method().equals(HttpMethod.OPTIONS)) {
response.headers().set(HttpHeaders.ALLOW, ALLOWED_METHODS);
} else if (request.method().equals(HttpMethod.GET)) {
Expand Down
Loading