Skip to content

Commit

Permalink
Fix DevConsole paths;HttpRootPathBuildItem.Builder
Browse files Browse the repository at this point in the history
* Expose configured HTTP paths to the DevUI
* Add Builder to HttpRootPathBuildItem to help create
  RouteBuildItems using resolved paths
* Create devConsoleAppend attribute for resolving static dev
  console resources

Co-authored-by: Stuart Douglas <[email protected]>
  • Loading branch information
ebullient and stuartwdouglas committed Mar 9, 2021
1 parent 33364c8 commit 307696a
Show file tree
Hide file tree
Showing 28 changed files with 462 additions and 140 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,12 @@ public static URI normalizeWithBase(URI base, String segment, boolean trailingSl
URI resolvedUri = base.resolve(segmentUri);
return resolvedUri;
}

public static String relativize(String rootPath, String leafPath) {
if (leafPath.startsWith(rootPath)) {
return leafPath.substring(rootPath.length());
}

return null;
}
}
69 changes: 59 additions & 10 deletions docs/src/main/asciidoc/dev-ui.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ include::./attributes.adoc[]
This guide covers the Quarkus Dev UI for link:building-my-first-extension[extension authors].

Quarkus now ships with a new experimental Dev UI, which is available in dev mode (when you start
quarkus with `mvn quarkus:dev`) at http://localhost:8080/q/dev[/q/dev] and will show you something like
quarkus with `mvn quarkus:dev`) at http://localhost:8080/q/dev[/q/dev] by default. It will show you something like
this:

image::dev-ui-overview.png[alt=Dev UI overview,role="center"]
Expand Down Expand Up @@ -46,16 +46,56 @@ two links with some styling and icons:

[source,html]
----
<a href="/q/openapi" class="badge badge-light">
<i class="fa ..."></i>
<a href="{config:http-path('quarkus.smallrye-openapi.path')}" class="badge badge-light">
<i class="fa fa-map-signs fa-fw"></i>
OpenAPI</a>
<a href="/q/swagger-ui/" class="badge badge-light">
<i class="fa ..."></i>
<br>
<a href="{config:http-path('quarkus.swagger-ui.path')}/" class="badge badge-light">
<i class="fa fa-map-signs fa-fw"></i>
Swagger UI</a>
----

TIP: We use the Font Awesome Free icon set.

Note how the paths are specified: `{config:http-path('quarkus.smallrye-openapi.path')}`. This is a special
directive that the quarkus dev console understands: it will replace that value with the resolved route
named 'quarkus.smallrye-openapi.path'.

The corresponding non-application endpoint is declared using `.routeConfigKey` to associate the route with a name:

[source,java]
----
nonApplicationRootPathBuildItem.routeBuilder()
.route(openApiConfig.path) // <1>
.routeConfigKey("quarkus.smallrye-openapi.path") // <2>
...
.build();
----
<1> The configured path is resolved into a valid route.
<2> The resolved route path is then associated with the key `quarkus.smallrye-openapi.path`.

== Path considerations

Paths are tricky business. Keep the following in mind:

* Assume your UI will be nested under the dev endpoint. Do not provide a way to customize this without a strong reason.
* Never construct your own absolute paths. Adding a suffix to a known, normalized and resolved path is fine.

Configured paths, like the `dev` endpoint used by the console or the SmallRye OpenAPI path shown in the example above,
need to be properly resolved against both `quarkus.http.root-path` and `quarkus.http.non-application-root-path`.
Use `NonApplicationRootPathBuildItem` or `HttpRootPathBuildItem` to construct endpoint routes and identify resolved
path values that can then be used in templates.

The `{devRootAppend}` variable can also be used in templates to construct URLs for static dev console resources, for example:

[source,html]
----
<img src="{devRootAppend}/resources/images/quarkus_icon_rgb_reverse.svg" width="40" height="30" class="d-inline-block align-middle" alt="Quarkus"/>
----

Refer to the link:all-config#quarkus-vertx-http_quarkus.http.non-application-root-path[Quarkus Vertx HTTP configuration reference]
for details on how the non-application root path is configured.

== Template and styling support

Both the `embedded.html` files and any full page you add in `/dev-templates` will be interpreted by
Expand All @@ -75,6 +115,9 @@ A `config:property(name)` expression can be used to output the config value for
The property name can be either a string literal or obtained dynamically by another expression.
For example `{config:property('quarkus.lambda.handler')}` and `{config:property(foo.propertyName)}`.

Reminder: do not use this to retrieve raw configured path values. As shown above, use `{config:http-path(...)}` with
a known route configuration key when working with resource paths.

== Adding full pages

To add full pages for your Dev UI extension such as this one:
Expand Down Expand Up @@ -145,17 +188,19 @@ link:building-my-first-extension#description-of-a-quarkus-extension[`deployment`

== Linking to your full-page templates

Every full-page template lives under the `/q/dev/{groupId}.{artifactId}/` URI (for example
`/q/dev/io.quarkus.quarkus-cache/`), so if you want to link
to them from your `embedded.html` file you can use the `urlbase` template parameter to point to them:
Full-page templates for extensions live under a pre-defined `{devRootAppend}/{groupId}.{artifactId}/` directory
that is referenced using the `urlbase` template parameter. Using configuration defaults, that would resolve to
`/q/dev/io.quarkus.quarkus-cache/`, as an example.

[source,java]
Use the `{urlbase}` template parameter to reference this folder in `embedded.html`:

[source,html]
----
<a href="{urlbase}/caches" class="badge badge-light">// <1>
<i class="fa ..."></i>
Caches <span class="badge badge-light">{info:cacheInfos.size()}</span></a>
----
<1> Use the `urlbase` template parameter to point to where your full-page templates are located
<1> Use the `urlbase` template parameter to reference full-page templates for your extension

== Passing information to your templates

Expand Down Expand Up @@ -274,6 +319,7 @@ This can be done by adding another link:building-my-first-extension#deploying-th
declare the action in your extension's
link:building-my-first-extension#description-of-a-quarkus-extension[`deployment`] module:


[source,java]
----
package io.quarkus.cache.deployment.devconsole;
Expand All @@ -298,11 +344,13 @@ public class DevConsoleProcessor {
<1> Mark the recorder as optional, so it will only be invoked when in dev mode
<2> Declare a `POST {urlbase}/caches` route handled by the given handler


Note: you can see <<action-example,how to invoke this action from your full page>>.

Now all you have to do is implement the recorder in your extension's
link:building-my-first-extension#description-of-a-quarkus-extension[`runtime module`]:


[source,java]
----
package io.quarkus.cache.runtime.devconsole;
Expand Down Expand Up @@ -346,6 +394,7 @@ public class CacheDevConsoleRecorder {
<3> Don't forget to add a message for the user to let them know everything went fine
<4> You can also add error messages


NOTE: Flash messages are handled by the `main` DEV template and will result in nice notifications for your
users:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ void createPrometheusRoute(BuildProducer<RouteBuildItem> routes,
.routeFunction(pConfig.path, recorder.route())
.handler(recorder.getHandler())
.requiresLegacyRedirect()
.displayOnNotFoundPage("Metrics", pConfig.path)
.displayOnNotFoundPage("Metrics")
.build());

// Match paths that begin with the deployment path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
public class SmallRyeGraphQLConfig {

/**
* The rootPath under which queries will be served. Default to /graphql
* The rootPath under which queries will be served. Default to graphql
* By default, this value will be resolved as a path relative to `${quarkus.http.root-path}`.
*/
@ConfigItem(defaultValue = "/graphql")
@ConfigItem(defaultValue = "graphql")
String rootPath;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
*/
public class SmallRyeGraphQLProcessor {
private static final Logger LOG = Logger.getLogger(SmallRyeGraphQLProcessor.class);
private static final String SCHEMA_PATH = "/schema.graphql";
private static final String SCHEMA_PATH = "schema.graphql";

// For Service integration
private static final String SERVICE_NOT_AVAILABLE_WARNING = "The %s property is true, but the %s extension is not present. SmallRye GraphQL %s will be disabled.";
Expand Down Expand Up @@ -181,14 +181,15 @@ void buildExecutionService(
@BuildStep
void buildSchemaEndpoint(
BuildProducer<RouteBuildItem> routeProducer,
HttpRootPathBuildItem httpRootPathBuildItem,
SmallRyeGraphQLInitializedBuildItem graphQLInitializedBuildItem,
SmallRyeGraphQLRecorder recorder,
SmallRyeGraphQLConfig graphQLConfig) {

Handler<RoutingContext> schemaHandler = recorder.schemaHandler(graphQLInitializedBuildItem.getInitialized());

routeProducer.produce(new RouteBuildItem.Builder()
.route(graphQLConfig.rootPath + SCHEMA_PATH)
routeProducer.produce(httpRootPathBuildItem.routeBuilder()
.nestedRoute(graphQLConfig.rootPath, SCHEMA_PATH)
.handler(schemaHandler)
.displayOnNotFoundPage("MicroProfile GraphQL Schema")
.blockingRoute()
Expand All @@ -201,6 +202,7 @@ void buildSchemaEndpoint(
@Consume(BeanContainerBuildItem.class)
void buildExecutionEndpoint(
BuildProducer<RouteBuildItem> routeProducer,
HttpRootPathBuildItem httpRootPathBuildItem,
SmallRyeGraphQLInitializedBuildItem graphQLInitializedBuildItem,
SmallRyeGraphQLRecorder recorder,
ShutdownContextBuildItem shutdownContext,
Expand All @@ -225,9 +227,10 @@ void buildExecutionEndpoint(

Handler<RoutingContext> executionHandler = recorder.executionHandler(graphQLInitializedBuildItem.getInitialized(),
allowGet);
routeProducer.produce(new RouteBuildItem.Builder()
routeProducer.produce(httpRootPathBuildItem.routeBuilder()
.routeFunction(graphQLConfig.rootPath, recorder.routeFunction(bodyHandlerBuildItem.getHandler()))
.handler(executionHandler)
.routeConfigKey("quarkus.smallrye-graphql.root-path")
.displayOnNotFoundPage("MicroProfile GraphQL Endpoint")
.blockingRoute()
.build());
Expand Down Expand Up @@ -528,6 +531,7 @@ void registerGraphQLUiHandler(
smallRyeGraphQLBuildItem.getGraphqlUiPath(), runtimeConfig);
routeProducer.produce(nonApplicationRootPathBuildItem.routeBuilder()
.route(graphQLConfig.ui.rootPath)
.routeConfigKey("quarkus.smallrye-graphql.ui.root-path")
.displayOnNotFoundPage("MicroProfile GraphQL UI")
.handler(handler)
.requiresLegacyRedirect()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class SmallRyeGraphQLUIConfig {
/**
* The path where GraphQL UI is available.
* The value `/` is not allowed as it blocks the application from serving anything else.
* By default, this URL will be resolved as a path relative to `${quarkus.http.non-application-root-path}`.
*/
@ConfigItem(defaultValue = "graphql-ui")
String rootPath;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<a href="{config:property('quarkus.smallrye-graphql.root-path')}/schema.graphql" class="badge badge-light">
<a href="{config:http-path('quarkus.smallrye-graphql.root-path')}/schema.graphql" class="badge badge-light">
<i class="fa fa-project-diagram fa-fw"></i>
GraphQL Schema</a>
<br>
<a href="{frameworkRootPath}{config:property('quarkus.smallrye-graphql.ui.root-path')}/" class="badge badge-light">
<a href="{config:http-path('quarkus.smallrye-graphql.ui.root-path')}/" class="badge badge-light">
<i class="fa fa-project-diagram fa-fw"></i>
GraphQL UI</a>
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,35 @@ public class SmallRyeHealthConfig {

/**
* Root path for health-checking endpoints.
* By default, this value will be resolved as a path relative to `${quarkus.http.non-application-root-path}`.
*/
@ConfigItem(defaultValue = "health")
String rootPath;

/**
* The relative path of the liveness health-checking endpoint.
* By default, this value will be resolved as a path relative to `${quarkus.smallrye-health.rootPath}`.
*/
@ConfigItem(defaultValue = "live")
String livenessPath;

/**
* The relative path of the readiness health-checking endpoint.
* By default, this value will be resolved as a path relative to `${quarkus.smallrye-health.rootPath}`.
*/
@ConfigItem(defaultValue = "ready")
String readinessPath;

/**
* The relative path of the health group endpoint.
* By default, this value will be resolved as a path relative to `${quarkus.smallrye-health.rootPath}`.
*/
@ConfigItem(defaultValue = "group")
String groupPath;

/**
* The relative path of the wellness health-checking endpoint.
* By default, this value will be resolved as a path relative to `${quarkus.smallrye-health.rootPath}`.
*/
@ConfigItem(defaultValue = "well")
String wellnessPath;
Expand Down
Loading

0 comments on commit 307696a

Please sign in to comment.