Skip to content

Commit

Permalink
Merge pull request quarkusio#5235 from machi1990/fix/5150
Browse files Browse the repository at this point in the history
feat: list openapi, swaggerui, metrics and health endpoints in not found page
  • Loading branch information
gwenneg authored Nov 8, 2019
2 parents 52099eb + b8b193c commit 9d892c0
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

Expand All @@ -21,6 +22,8 @@
import io.quarkus.resteasy.runtime.UnauthorizedExceptionMapper;
import io.quarkus.resteasy.server.common.deployment.ResteasyDeploymentBuildItem;
import io.quarkus.undertow.deployment.StaticResourceFilesBuildItem;
import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem;
import io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem;

public class ResteasyBuiltinsProcessor {

Expand Down Expand Up @@ -54,8 +57,8 @@ void setupExceptionMapper(BuildProducer<ResteasyJaxrsProviderBuildItem> provider
providers.produce(new ResteasyJaxrsProviderBuildItem(NotFoundExceptionMapper.class.getName()));
}

@BuildStep(onlyIf = IsDevelopment.class)
@Record(STATIC_INIT)
@BuildStep(onlyIf = IsDevelopment.class)
void addStaticResourcesExceptionMapper(StaticResourceFilesBuildItem paths, ExceptionMapperRecorder recorder) {
//limit to 1000 to not have to many files to display
Set<String> staticResources = paths.files.stream().filter(this::isHtmlFileName).limit(1000).collect(Collectors.toSet());
Expand All @@ -69,4 +72,17 @@ private boolean isHtmlFileName(String fileName) {
return fileName.endsWith(".html") || fileName.endsWith(".htm");
}

@Record(STATIC_INIT)
@BuildStep(onlyIf = IsDevelopment.class)
void addAdditionalEndpointsExceptionMapper(List<NotFoundPageDisplayableEndpointBuildItem> displayableEndpoints,
ExceptionMapperRecorder recorder, HttpRootPathBuildItem httpRoot) {
List<String> endpoints = displayableEndpoints
.stream()
.map(displayableAdditionalBuildItem -> httpRoot.adjustPath(displayableAdditionalBuildItem.getEndpoint())
.substring(1))
.sorted()
.collect(Collectors.toList());

recorder.setAdditionalEndpoints(endpoints);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public void staticInit(ResteasyStandaloneRecorder recorder,
deploymentRootPath = deployment.getRootPath();
if (rootPath.endsWith("/")) {
if (deploymentRootPath.startsWith("/")) {
rootPath += deploymentRootPath.substring(1, deploymentRootPath.length());
rootPath += deploymentRootPath.substring(1);
} else {
rootPath += deploymentRootPath;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ public void setStaticResource(Set<String> resources) {
NotFoundExceptionMapper.staticResources(resources);
}

public void setAdditionalEndpoints(List<String> additionalEndpoints) {
NotFoundExceptionMapper.setAdditionalEndpoints(additionalEndpoints);
}

public void setServlets(Map<String, List<String>> servletToMapping) {
NotFoundExceptionMapper.servlets(servletToMapping);
}

/**
* Uses to register the paths of classes that are not annotated with JAX-RS annotations (like Spring Controllers for
* example)
*
*
* @param nonJaxRsClassNameToMethodPaths A map that contains the class name as a key and a map that
* contains the method name to path as a value
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public class NotFoundExceptionMapper implements ExceptionMapper<NotFoundExceptio

private volatile static List<String> servletMappings = Collections.EMPTY_LIST;
private volatile static List<String> staticResources = Collections.EMPTY_LIST;
private volatile static List<String> additionalEndpoints = Collections.EMPTY_LIST;
private volatile static Map<String, NonJaxRsClassMappings> nonJaxRsClassNameToMethodPaths = Collections.EMPTY_MAP;

@Context
Expand Down Expand Up @@ -241,6 +242,14 @@ private Response respond(List<ResourceDescription> descriptions) {
sb.resourcesEnd();
}

if (!additionalEndpoints.isEmpty()) {
sb.resourcesStart("Additional endpoints");
for (String additionalEndpoint : additionalEndpoints) {
sb.staticResourcePath(additionalEndpoint);
}
sb.resourcesEnd();
}

return Response.status(Status.NOT_FOUND).entity(sb.toString()).type(MediaType.TEXT_HTML_TYPE).build();
}
return Response.status(Status.NOT_FOUND).build();
Expand All @@ -266,4 +275,8 @@ public static void staticResources(Set<String> knownFiles) {
public static void nonJaxRsClassNameToMethodPaths(Map<String, NonJaxRsClassMappings> nonJaxRsPaths) {
NotFoundExceptionMapper.nonJaxRsClassNameToMethodPaths = nonJaxRsPaths;
}

public static void setAdditionalEndpoints(List<String> additionalEndpoints) {
NotFoundExceptionMapper.additionalEndpoints = additionalEndpoints;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.recording.RecorderContext;
import io.quarkus.deployment.util.ServiceUtil;
import io.quarkus.kubernetes.spi.KubernetesHealthLivenessPathBuildItem;
Expand All @@ -29,6 +30,7 @@
import io.quarkus.smallrye.health.runtime.SmallRyeLivenessHandler;
import io.quarkus.smallrye.health.runtime.SmallRyeReadinessHandler;
import io.quarkus.vertx.http.deployment.RouteBuildItem;
import io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem;
import io.quarkus.vertx.http.runtime.HandlerType;
import io.smallrye.health.SmallRyeHealthReporter;

Expand Down Expand Up @@ -87,7 +89,9 @@ void build(SmallRyeHealthRecorder recorder, RecorderContext recorderContext,
BuildProducer<FeatureBuildItem> feature,
BuildProducer<RouteBuildItem> routes,
BuildProducer<AdditionalBeanBuildItem> additionalBean,
BuildProducer<BeanDefiningAnnotationBuildItem> beanDefiningAnnotation) throws IOException {
BuildProducer<BeanDefiningAnnotationBuildItem> beanDefiningAnnotation,
BuildProducer<NotFoundPageDisplayableEndpointBuildItem> displayableEndpoints,
LaunchModeBuildItem launchModeBuildItem) throws IOException {

feature.produce(new FeatureBuildItem(FeatureBuildItem.SMALLRYE_HEALTH));

Expand All @@ -100,6 +104,14 @@ void build(SmallRyeHealthRecorder recorder, RecorderContext recorderContext,
new RouteBuildItem(health.rootPath + health.readinessPath, new SmallRyeReadinessHandler(),
HandlerType.BLOCKING));

// add health endpoints to not found page
if (launchModeBuildItem.getLaunchMode().isDevOrTest()) {
displayableEndpoints.produce(new NotFoundPageDisplayableEndpointBuildItem(health.rootPath));
displayableEndpoints.produce(new NotFoundPageDisplayableEndpointBuildItem(health.rootPath + health.livenessPath));
displayableEndpoints
.produce(new NotFoundPageDisplayableEndpointBuildItem(health.rootPath + health.readinessPath));
}

// Make ArC discover the beans marked with the @Health qualifier
beanDefiningAnnotation.produce(new BeanDefiningAnnotationBuildItem(HEALTH));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.logging.LogCleanupFilterBuildItem;
Expand All @@ -63,6 +64,7 @@
import io.quarkus.smallrye.metrics.runtime.SmallRyeMetricsRecorder;
import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem;
import io.quarkus.vertx.http.deployment.RouteBuildItem;
import io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem;
import io.quarkus.vertx.http.runtime.HandlerType;
import io.smallrye.metrics.MetricProducer;
import io.smallrye.metrics.MetricRegistries;
Expand Down Expand Up @@ -96,9 +98,16 @@ static final class SmallRyeMetricsConfig {
@Record(STATIC_INIT)
void createRoute(BuildProducer<RouteBuildItem> routes,
SmallRyeMetricsRecorder recorder,
HttpRootPathBuildItem httpRoot) {
HttpRootPathBuildItem httpRoot,
BuildProducer<NotFoundPageDisplayableEndpointBuildItem> displayableEndpoints,
LaunchModeBuildItem launchModeBuildItem) {
Function<Router, Route> route = recorder.route(metrics.path + (metrics.path.endsWith("/") ? "*" : "/*"));
Function<Router, Route> slash = recorder.route(metrics.path);

// add metrics endpoint for not found display in dev or test mode
if (launchModeBuildItem.getLaunchMode().isDevOrTest()) {
displayableEndpoints.produce(new NotFoundPageDisplayableEndpointBuildItem(metrics.path));
}
routes.produce(new RouteBuildItem(route, recorder.handler(httpRoot.adjustPath(metrics.path)), HandlerType.BLOCKING));
routes.produce(new RouteBuildItem(slash, recorder.handler(httpRoot.adjustPath(metrics.path)), HandlerType.BLOCKING));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import io.quarkus.smallrye.openapi.runtime.OpenApiDocumentProducer;
import io.quarkus.smallrye.openapi.runtime.OpenApiHandler;
import io.quarkus.vertx.http.deployment.RouteBuildItem;
import io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem;
import io.quarkus.vertx.http.runtime.HandlerType;
import io.smallrye.openapi.api.OpenApiConfig;
import io.smallrye.openapi.api.OpenApiConfigImpl;
Expand Down Expand Up @@ -101,7 +102,8 @@ List<HotDeploymentWatchedFileBuildItem> configFiles() {
}

@BuildStep
RouteBuildItem handler(DeploymentClassLoaderBuildItem deploymentClassLoaderBuildItem, LaunchModeBuildItem launch) {
RouteBuildItem handler(DeploymentClassLoaderBuildItem deploymentClassLoaderBuildItem, LaunchModeBuildItem launch,
BuildProducer<NotFoundPageDisplayableEndpointBuildItem> displayableEndpoints) {
/*
* <em>Ugly Hack</em>
* In dev mode, we pass a classloader to load the up to date OpenAPI document.
Expand All @@ -115,6 +117,7 @@ RouteBuildItem handler(DeploymentClassLoaderBuildItem deploymentClassLoaderBuild
*/
if (launch.getLaunchMode() == LaunchMode.DEVELOPMENT) {
OpenApiHandler.classLoader = deploymentClassLoaderBuildItem.getClassLoader();
displayableEndpoints.produce(new NotFoundPageDisplayableEndpointBuildItem(openapi.path));
} else {
OpenApiHandler.classLoader = null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.quarkus.smallrye.openapi.test.hotreload;

import static org.hamcrest.Matchers.containsString;

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.QuarkusDevModeTest;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;

public class DisplayOpenAPiEndpointInNotFoundExceptionPageTest {
private static final String OPEN_API_PATH = "/openapi-path";
private static final String SWAGGER_UI_PATH = "/swagger-path";

@RegisterExtension
static final QuarkusDevModeTest test = new QuarkusDevModeTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(MyResource.class)
.addAsResource(new StringAsset(
"quarkus.smallrye-openapi.path=" + OPEN_API_PATH + "\nquarkus.swagger-ui.path=" + SWAGGER_UI_PATH),
"application.properties"));

@Test
public void shouldDisplayOpenApiAndSwaggerUiEndpointsInNotFoundPage() {
RestAssured
.given()
.accept(ContentType.HTML)
.when()
.get("/open")
.then()
.statusCode(404)
.body(containsString(OPEN_API_PATH))
.body(containsString(SWAGGER_UI_PATH));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import io.quarkus.swaggerui.runtime.SwaggerUiRecorder;
import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem;
import io.quarkus.vertx.http.deployment.RouteBuildItem;
import io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;

Expand Down Expand Up @@ -79,7 +80,8 @@ public void registerSwaggerUiServletExtension(SwaggerUiRecorder recorder,
BuildProducer<GeneratedResourceBuildItem> generatedResources,
BuildProducer<NativeImageResourceBuildItem> nativeImageResourceBuildItemBuildProducer,
LiveReloadBuildItem liveReloadBuildItem,
HttpRootPathBuildItem httpRootPathBuildItem) throws Exception {
HttpRootPathBuildItem httpRootPathBuildItem,
BuildProducer<NotFoundPageDisplayableEndpointBuildItem> displayableEndpoints) throws Exception {

if ("/".equals(swaggerUiConfig.path)) {
throw new ConfigurationError(
Expand Down Expand Up @@ -119,6 +121,7 @@ public void registerSwaggerUiServletExtension(SwaggerUiRecorder recorder,
Handler<RoutingContext> handler = recorder.handler(cached.cachedDirectory, swaggerUiConfig.path);
routes.produce(new RouteBuildItem(swaggerUiConfig.path, handler));
routes.produce(new RouteBuildItem(swaggerUiConfig.path + "/*", handler));
displayableEndpoints.produce(new NotFoundPageDisplayableEndpointBuildItem(swaggerUiConfig.path + "/"));
} else if (swaggerUiConfig.alwaysInclude) {
ResolvedArtifact artifact = getSwaggerUiArtifact();
//we are including in a production artifact
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.quarkus.vertx.http.deployment.devmode;

import io.quarkus.builder.item.MultiBuildItem;

final public class NotFoundPageDisplayableEndpointBuildItem extends MultiBuildItem {
private final String endpoint;

public NotFoundPageDisplayableEndpointBuildItem(String endpoint) {
this.endpoint = endpoint;
}

public String getEndpoint() {
return endpoint;
}
}

0 comments on commit 9d892c0

Please sign in to comment.