Skip to content

Commit

Permalink
Merge pull request #34662 from phillip-kruger/uis-management-option
Browse files Browse the repository at this point in the history
Add OpenAPI to management interface if enabled, with option to exclude
  • Loading branch information
phillip-kruger authored Jul 11, 2023
2 parents 80826ec + 9d633e4 commit 9166c1c
Show file tree
Hide file tree
Showing 14 changed files with 173 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,16 @@ CardPageBuildItem createCard(NonApplicationRootPathBuildItem nonApplicationRootP
CardPageBuildItem cardPageBuildItem = new CardPageBuildItem();

// Generated GraphQL Schema
String schemaPath = "/" + graphQLConfig.rootPath + "/schema.graphql";
PageBuilder schemaPage = Page.externalPageBuilder("GraphQL Schema")
.icon("font-awesome-solid:diagram-project")
.url("/" + graphQLConfig.rootPath + "/schema.graphql");
.url(schemaPath, schemaPath);

// GraphiQL UI
String uiPath = nonApplicationRootPathBuildItem.resolvePath(graphQLConfig.ui.rootPath);
PageBuilder uiPage = Page.externalPageBuilder("GraphQL UI")
.icon("font-awesome-solid:table-columns")
.staticLabel("<a style='color: var(--lumo-contrast-80pct);' href='" + uiPath
+ "' target='_blank'><vaadin-icon class='icon' icon='font-awesome-solid:up-right-from-square'></vaadin-icon></a>")
.url(uiPath + "/index.html?embed=true");
.url(uiPath + "/index.html?embed=true", uiPath);

// Learn
PageBuilder learnLink = Page.externalPageBuilder("Learn more about GraphQL")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ public class SmallRyeHealthConfig {
@ConfigItem
Optional<String> defaultHealthGroup;

/**
* If management interface is turned on the health endpoints and ui will be published under the management interface. This
* allows you to exclude Health from management by setting the value to false
*/
@ConfigItem(name = "management.enabled", defaultValue = "true")
public boolean managementEnabled;

/**
* SmallRye Health UI configuration
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,19 @@ CardPageBuildItem create(NonApplicationRootPathBuildItem nonApplicationRootPathB
SmallRyeHealthRecorder unused) {
CardPageBuildItem pageBuildItem = new CardPageBuildItem();

var path = nonApplicationRootPathBuildItem.resolveManagementPath(config.rootPath,
managementInterfaceBuildTimeConfig, launchModeBuildItem);
String path = nonApplicationRootPathBuildItem.resolveManagementPath(config.rootPath,
managementInterfaceBuildTimeConfig, launchModeBuildItem, config.managementEnabled);

pageBuildItem.addPage(Page.externalPageBuilder("Health")
.icon("font-awesome-solid:heart-circle-bolt")
.url(path)
.url(path, path)
.isJsonContent());

String uipath = nonApplicationRootPathBuildItem.resolveManagementPath(config.ui.rootPath,
managementInterfaceBuildTimeConfig, launchModeBuildItem, config.managementEnabled);
pageBuildItem.addPage(Page.externalPageBuilder("Health UI")
.icon("font-awesome-solid:stethoscope")
.url(nonApplicationRootPathBuildItem.resolvePath(config.ui.rootPath))
.url(uipath, uipath)
.isHtmlContent());

return pageBuildItem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ public void defineHealthRoutes(BuildProducer<RouteBuildItem> routes,

// Register the health handler
routes.produce(nonApplicationRootPathBuildItem.routeBuilder()
.management()
.management("quarkus.smallrye-health.management.enabled")
.route(healthConfig.rootPath)
.routeConfigKey("quarkus.smallrye-health.root-path")
.handler(new SmallRyeHealthHandler())
Expand All @@ -218,7 +218,7 @@ public void defineHealthRoutes(BuildProducer<RouteBuildItem> routes,

// Register the liveness handler
routes.produce(nonApplicationRootPathBuildItem.routeBuilder()
.management()
.management("quarkus.smallrye-health.management.enabled")
.nestedRoute(healthConfig.rootPath, healthConfig.livenessPath)
.handler(new SmallRyeLivenessHandler())
.displayOnNotFoundPage()
Expand All @@ -227,7 +227,7 @@ public void defineHealthRoutes(BuildProducer<RouteBuildItem> routes,

// Register the readiness handler
routes.produce(nonApplicationRootPathBuildItem.routeBuilder()
.management()
.management("quarkus.smallrye-health.management.enabled")
.nestedRoute(healthConfig.rootPath, healthConfig.readinessPath)
.handler(new SmallRyeReadinessHandler())
.displayOnNotFoundPage()
Expand All @@ -249,7 +249,7 @@ public void defineHealthRoutes(BuildProducer<RouteBuildItem> routes,

// Register the health group handlers
routes.produce(nonApplicationRootPathBuildItem.routeBuilder()
.management()
.management("quarkus.smallrye-health.management.enabled")
.nestedRoute(healthConfig.rootPath, healthConfig.groupPath)
.handler(new SmallRyeHealthGroupHandler())
.displayOnNotFoundPage()
Expand All @@ -258,7 +258,7 @@ public void defineHealthRoutes(BuildProducer<RouteBuildItem> routes,

SmallRyeIndividualHealthGroupHandler handler = new SmallRyeIndividualHealthGroupHandler();
routes.produce(nonApplicationRootPathBuildItem.routeBuilder()
.management()
.management("quarkus.smallrye-health.management.enabled")
.nestedRoute(healthConfig.rootPath, healthConfig.groupPath + "/*")
.handler(handler)
.displayOnNotFoundPage()
Expand All @@ -267,7 +267,7 @@ public void defineHealthRoutes(BuildProducer<RouteBuildItem> routes,

// Register the wellness handler
routes.produce(nonApplicationRootPathBuildItem.routeBuilder()
.management()
.management("quarkus.smallrye-health.management.enabled")
.nestedRoute(healthConfig.rootPath, healthConfig.wellnessPath)
.handler(new SmallRyeWellnessHandler())
.displayOnNotFoundPage()
Expand All @@ -276,7 +276,7 @@ public void defineHealthRoutes(BuildProducer<RouteBuildItem> routes,

// Register the startup handler
routes.produce(nonApplicationRootPathBuildItem.routeBuilder()
.management()
.management("quarkus.smallrye-health.management.enabled")
.nestedRoute(healthConfig.rootPath, healthConfig.startupPath)
.handler(new SmallRyeStartupHandler())
.displayOnNotFoundPage()
Expand Down Expand Up @@ -443,15 +443,17 @@ void registerHealthUiHandler(

Handler<RoutingContext> handler = recorder.uiHandler(result.getFinalDestination(),
healthUiPath, result.getWebRootConfigurations(), runtimeConfig, shutdownContext);
// The health ui is not a management route.

routeProducer.produce(nonApplicationRootPathBuildItem.routeBuilder()
.management("quarkus.smallrye-health.management.enabled")
.route(healthConfig.ui.rootPath)
.displayOnNotFoundPage("Health UI")
.routeConfigKey("quarkus.smallrye-health.ui.root-path")
.handler(handler)
.build());

routeProducer.produce(nonApplicationRootPathBuildItem.routeBuilder()
.management("quarkus.smallrye-health.management.enabled")
.route(healthConfig.ui.rootPath + "*")
.handler(handler)
.build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ public final class SmallRyeOpenApiConfig {
@ConfigItem(defaultValue = "false")
public boolean ignoreStaticDocument;

/**
* If management interface is turned on the openapi schema document will be published under the management interface. This
* allows you to exclude OpenAPI from management by setting the value to false
*/
@ConfigItem(name = "management.enabled", defaultValue = "true")
public boolean managementEnabled;

/**
* A list of local directories that should be scanned for yaml and/or json files to be included in the static model.
* Example: `META-INF/openapi/`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.regex.Matcher;
Expand Down Expand Up @@ -70,6 +71,7 @@
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.RunTimeConfigBuilderBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.builditem.SystemPropertyBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem;
Expand Down Expand Up @@ -105,6 +107,8 @@
import io.quarkus.vertx.http.deployment.RouteBuildItem;
import io.quarkus.vertx.http.deployment.SecurityInformationBuildItem;
import io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem;
import io.quarkus.vertx.http.runtime.management.ManagementInterfaceBuildTimeConfig;
import io.quarkus.vertx.http.runtime.management.ManagementInterfaceConfiguration;
import io.smallrye.openapi.api.OpenApiConfig;
import io.smallrye.openapi.api.OpenApiConfigImpl;
import io.smallrye.openapi.api.OpenApiDocument;
Expand Down Expand Up @@ -235,12 +239,15 @@ void registerAutoSecurityFilter(BuildProducer<SyntheticBeanBuildItem> syntheticB
void handler(LaunchModeBuildItem launch,
BuildProducer<NotFoundPageDisplayableEndpointBuildItem> displayableEndpoints,
BuildProducer<RouteBuildItem> routes,
BuildProducer<SystemPropertyBuildItem> systemProperties,
OpenApiRecorder recorder,
NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem,
OpenApiRuntimeConfig openApiRuntimeConfig,
ShutdownContextBuildItem shutdownContext,
SmallRyeOpenApiConfig openApiConfig,
List<FilterBuildItem> filterBuildItems) {
List<FilterBuildItem> filterBuildItems,
ManagementInterfaceBuildTimeConfig managementInterfaceBuildTimeConfig,
ManagementInterfaceConfiguration managementInterfaceConfiguration) {
/*
* <em>Ugly Hack</em>
* In dev mode, we pass a classloader to load the up to date OpenAPI document.
Expand Down Expand Up @@ -271,6 +278,7 @@ void handler(LaunchModeBuildItem launch,
}

routes.produce(nonApplicationRootPathBuildItem.routeBuilder()
.management("quarkus.smallrye-openapi.management.enabled")
.routeFunction(openApiConfig.path, corsFilter)
.routeConfigKey("quarkus.smallrye-openapi.path")
.handler(handler)
Expand All @@ -279,19 +287,59 @@ void handler(LaunchModeBuildItem launch,
.build());

routes.produce(nonApplicationRootPathBuildItem.routeBuilder()
.management("quarkus.smallrye-openapi.management.enabled")
.routeFunction(openApiConfig.path + ".json", corsFilter)
.handler(handler)
.build());

routes.produce(nonApplicationRootPathBuildItem.routeBuilder()
.management("quarkus.smallrye-openapi.management.enabled")
.routeFunction(openApiConfig.path + ".yaml", corsFilter)
.handler(handler)
.build());

routes.produce(nonApplicationRootPathBuildItem.routeBuilder()
.management("quarkus.smallrye-openapi.management.enabled")
.routeFunction(openApiConfig.path + ".yml", corsFilter)
.handler(handler)
.build());

// If management is enabled and swagger-ui is part of management, we need to add CORS so that swagger can hit the endpoint
if (isManagement(managementInterfaceBuildTimeConfig, openApiConfig, launch)) {
Config c = ConfigProvider.getConfig();

// quarkus.http.cors=true
// quarkus.http.cors.origins
Optional<Boolean> maybeCors = c.getOptionalValue("quarkus.http.cors", Boolean.class);
if (!maybeCors.isPresent() || !maybeCors.get().booleanValue()) {
// We need to set quarkus.http.cors=true
systemProperties.produce(new SystemPropertyBuildItem("quarkus.http.cors", "true"));
}

String managementUrl = getManagementRoot(launch, nonApplicationRootPathBuildItem, openApiConfig,
managementInterfaceBuildTimeConfig, managementInterfaceConfiguration);

List<String> origins = c.getOptionalValues("quarkus.http.cors.origins", String.class).orElse(new ArrayList<>());
if (!origins.contains(managementUrl)) {
// We need to set quarkus.http.cors.origins
origins.add(managementUrl);
String originConfigValue = String.join(",", origins);
systemProperties.produce(new SystemPropertyBuildItem("quarkus.http.cors.origins", originConfigValue));
}

}
}

private String getManagementRoot(LaunchModeBuildItem launch,
NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem,
SmallRyeOpenApiConfig openApiConfig,
ManagementInterfaceBuildTimeConfig managementInterfaceBuildTimeConfig,
ManagementInterfaceConfiguration managementInterfaceConfiguration) {
String managementRoot = nonApplicationRootPathBuildItem.resolveManagementPath("/",
managementInterfaceBuildTimeConfig, launch, openApiConfig.managementEnabled);

return managementRoot.split(managementInterfaceBuildTimeConfig.rootPath)[0];

}

@BuildStep
Expand Down Expand Up @@ -340,7 +388,9 @@ public boolean accepts(DotName className) {
void addAutoFilters(BuildProducer<AddToOpenAPIDefinitionBuildItem> addToOpenAPIDefinitionProducer,
List<SecurityInformationBuildItem> securityInformationBuildItems,
OpenApiFilteredIndexViewBuildItem apiFilteredIndexViewBuildItem,
SmallRyeOpenApiConfig config) {
SmallRyeOpenApiConfig config,
LaunchModeBuildItem launchModeBuildItem,
ManagementInterfaceBuildTimeConfig managementInterfaceBuildTimeConfig) {

// Add a security scheme from config
if (config.securityScheme.isPresent()) {
Expand Down Expand Up @@ -371,12 +421,24 @@ void addAutoFilters(BuildProducer<AddToOpenAPIDefinitionBuildItem> addToOpenAPID
}

// Add Auto Server based on the current server details
OASFilter autoServerFilter = getAutoServerFilter(config, false);
OASFilter autoServerFilter = getAutoServerFilter(config, false, "Auto generated value");
if (autoServerFilter != null) {
addToOpenAPIDefinitionProducer.produce(new AddToOpenAPIDefinitionBuildItem(autoServerFilter));
} else if (isManagement(managementInterfaceBuildTimeConfig, config, launchModeBuildItem)) { // Add server if management is enabled
OASFilter serverFilter = getAutoServerFilter(config, true, "Auto-added by management interface");
if (serverFilter != null) {
addToOpenAPIDefinitionProducer.produce(new AddToOpenAPIDefinitionBuildItem(serverFilter));
}
}
}

private boolean isManagement(ManagementInterfaceBuildTimeConfig managementInterfaceBuildTimeConfig,
SmallRyeOpenApiConfig smallRyeOpenApiConfig,
LaunchModeBuildItem launchModeBuildItem) {
return managementInterfaceBuildTimeConfig.enabled && smallRyeOpenApiConfig.managementEnabled
&& launchModeBuildItem.getLaunchMode().equals(LaunchMode.DEVELOPMENT);
}

private OASFilter getAutoSecurityFilter(List<SecurityInformationBuildItem> securityInformationBuildItems,
SmallRyeOpenApiConfig config) {

Expand Down Expand Up @@ -467,7 +529,7 @@ private OASFilter getAutoTagFilter(OpenApiFilteredIndexViewBuildItem apiFiltered
return null;
}

private OASFilter getAutoServerFilter(SmallRyeOpenApiConfig config, boolean defaultFlag) {
private OASFilter getAutoServerFilter(SmallRyeOpenApiConfig config, boolean defaultFlag, String description) {
if (config.autoAddServer.orElse(defaultFlag)) {
Config c = ConfigProvider.getConfig();

Expand All @@ -483,7 +545,7 @@ private OASFilter getAutoServerFilter(SmallRyeOpenApiConfig config, boolean defa
port = c.getOptionalValue("quarkus.http.ssl-port", Integer.class).orElse(8443);
}

return new AutoServerFilter(scheme, host, port);
return new AutoServerFilter(scheme, host, port, description);
}
return null;
}
Expand Down Expand Up @@ -1040,7 +1102,7 @@ private OpenApiDocument storeDocument(OutputTargetBuildItem out,
}

// By default, also add the auto generated server
OASFilter autoServerFilter = getAutoServerFilter(smallRyeOpenApiConfig, true);
OASFilter autoServerFilter = getAutoServerFilter(smallRyeOpenApiConfig, true, "Auto generated value");
if (autoServerFilter != null) {
document.filter(autoServerFilter);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,44 @@

import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.devui.spi.page.CardPageBuildItem;
import io.quarkus.devui.spi.page.Page;
import io.quarkus.smallrye.openapi.common.deployment.SmallRyeOpenApiConfig;
import io.quarkus.swaggerui.deployment.SwaggerUiConfig;
import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem;
import io.quarkus.vertx.http.runtime.management.ManagementInterfaceBuildTimeConfig;

public class OpenApiDevUIProcessor {

@BuildStep(onlyIf = IsDevelopment.class)
public CardPageBuildItem pages(NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem,
SwaggerUiConfig swaggerUiConfig, SmallRyeOpenApiConfig openApiConfig) {
ManagementInterfaceBuildTimeConfig managementInterfaceBuildTimeConfig,
LaunchModeBuildItem launchModeBuildItem,
SwaggerUiConfig swaggerUiConfig,
SmallRyeOpenApiConfig openApiConfig) {

String uiPath = nonApplicationRootPathBuildItem.resolvePath(swaggerUiConfig.path);
String schemaPath = nonApplicationRootPathBuildItem.resolvePath(openApiConfig.path);
String uiPath = nonApplicationRootPathBuildItem.resolveManagementPath(swaggerUiConfig.path,
managementInterfaceBuildTimeConfig, launchModeBuildItem, openApiConfig.managementEnabled);

String schemaPath = nonApplicationRootPathBuildItem.resolveManagementPath(openApiConfig.path,
managementInterfaceBuildTimeConfig, launchModeBuildItem, openApiConfig.managementEnabled);

CardPageBuildItem cardPageBuildItem = new CardPageBuildItem();

cardPageBuildItem.addPage(Page.externalPageBuilder("Schema yaml")
.url(nonApplicationRootPathBuildItem.resolvePath(schemaPath))
.url(schemaPath, schemaPath)
.isYamlContent()
.icon("font-awesome-solid:file-lines"));

String jsonSchema = schemaPath + "?format=json";
cardPageBuildItem.addPage(Page.externalPageBuilder("Schema json")
.url(nonApplicationRootPathBuildItem.resolvePath(schemaPath) + "?format=json")
.url(jsonSchema, jsonSchema)
.isJsonContent()
.icon("font-awesome-solid:file-code"));

cardPageBuildItem.addPage(Page.externalPageBuilder("Swagger UI")
.url(uiPath + "/index.html?embed=true")
.staticLabel("<a style='color: var(--lumo-contrast-80pct);' href='" + uiPath
+ "' target='_blank'><vaadin-icon class='icon' icon='font-awesome-solid:up-right-from-square'></vaadin-icon></a>")
.url(uiPath + "/index.html?embed=true", uiPath)
.isHtmlContent()
.icon("font-awesome-solid:signs-post"));

Expand Down
Loading

0 comments on commit 9166c1c

Please sign in to comment.