diff --git a/bom/application/pom.xml b/bom/application/pom.xml index dc81559a842d7b..5c60d85458a32c 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -1265,11 +1265,6 @@ quarkus-kafka-client-deployment ${project.version} - - io.quarkus - quarkus-kafka-client-ui - ${project.version} - io.quarkus quarkus-kafka-streams diff --git a/build-parent/pom.xml b/build-parent/pom.xml index e2d6e9c1b05ebe..2a9d7f36089c10 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -134,6 +134,8 @@ 4.6.1 + 0.9.15 + 1.9.1 6.1.2 3.6.1 5.62.2 @@ -309,6 +311,16 @@ bootstrap ${webjar.bootstrap.version} + + org.webjars + bootstrap-multiselect + ${webjar.bootstrap-multiselect.version} + + + org.webjars.npm + bootstrap-icons + ${webjar.bootstrap-icons.version} + org.webjars font-awesome diff --git a/extensions/kafka-client/deployment/pom.xml b/extensions/kafka-client/deployment/pom.xml index 78ae0ae2174231..a7da8dfa0277f1 100644 --- a/extensions/kafka-client/deployment/pom.xml +++ b/extensions/kafka-client/deployment/pom.xml @@ -46,12 +46,8 @@ io.quarkus - quarkus-vertx-http-deployment + quarkus-vertx-http-dev-console-spi - - io.quarkus - quarkus-kafka-client-ui - org.testcontainers testcontainers diff --git a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaBuildTimeConfig.java b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaBuildTimeConfig.java index ab52f4b2dc7a1e..420d8bd85a453c 100644 --- a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaBuildTimeConfig.java +++ b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaBuildTimeConfig.java @@ -1,6 +1,5 @@ package io.quarkus.kafka.client.deployment; -import io.quarkus.runtime.annotations.ConfigDocSection; import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; @@ -30,10 +29,4 @@ public class KafkaBuildTimeConfig { @ConfigItem public KafkaDevServicesBuildTimeConfig devservices; - /** - * Kafka UI configuration - */ - @ConfigItem - @ConfigDocSection - public KafkaBuildTimeUiConfig ui; } diff --git a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaBuildTimeUiConfig.java b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaBuildTimeUiConfig.java deleted file mode 100644 index d8dfd5706a5d97..00000000000000 --- a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaBuildTimeUiConfig.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.quarkus.kafka.client.deployment; - -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; - -@ConfigGroup -public class KafkaBuildTimeUiConfig { - - /** - * The path where Kafka 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 = "kafka-ui") - public String rootPath; - /** - * Whether or not to enable Kafka Dev UI in non-development native mode. - */ - @ConfigItem(name = "handlerpath", defaultValue = "kafka-admin") - public String handlerRootPath; - /** - * Always include the UI. By default, this will only be included in dev and test. - * Setting this to true will also include the UI in Prod - */ - @ConfigItem(defaultValue = "false") - public boolean alwaysInclude; - -} diff --git a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java index 1557b9430add21..c29791d4215695 100644 --- a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java +++ b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java @@ -1,21 +1,14 @@ package io.quarkus.kafka.client.deployment; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Scanner; import java.util.Set; import java.util.function.Consumer; import java.util.logging.Level; import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.security.auth.spi.LoginModule; @@ -67,6 +60,7 @@ import io.quarkus.deployment.Capabilities; import io.quarkus.deployment.Capability; import io.quarkus.deployment.Feature; +import io.quarkus.deployment.IsDevelopment; import io.quarkus.deployment.IsNormal; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; @@ -77,13 +71,11 @@ import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; -import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem; import io.quarkus.deployment.builditem.IndexDependencyBuildItem; import io.quarkus.deployment.builditem.LaunchModeBuildItem; import io.quarkus.deployment.builditem.LogCategoryBuildItem; import io.quarkus.deployment.builditem.RunTimeConfigurationDefaultBuildItem; import io.quarkus.deployment.builditem.RuntimeConfigSetupCompleteBuildItem; -import io.quarkus.deployment.builditem.ShutdownContextBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageProxyDefinitionBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageSecurityProviderBuildItem; @@ -92,6 +84,9 @@ import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.deployment.logging.LogCleanupFilterBuildItem; import io.quarkus.deployment.pkg.NativeConfig; +import io.quarkus.dev.spi.DevModeType; +import io.quarkus.devconsole.spi.DevConsoleRouteBuildItem; +import io.quarkus.devconsole.spi.DevConsoleWebjarBuildItem; import io.quarkus.kafka.client.runtime.*; import io.quarkus.kafka.client.runtime.KafkaRuntimeConfigProducer; import io.quarkus.kafka.client.runtime.ui.KafkaTopicClient; @@ -108,18 +103,7 @@ import io.quarkus.kafka.client.serialization.ObjectMapperDeserializer; import io.quarkus.kafka.client.serialization.ObjectMapperSerializer; import io.quarkus.maven.dependency.GACT; -import io.quarkus.runtime.LaunchMode; -import io.quarkus.runtime.configuration.ConfigurationException; import io.quarkus.smallrye.health.deployment.spi.HealthBuildItem; -import io.quarkus.vertx.http.deployment.BodyHandlerBuildItem; -import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem; -import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; -import io.quarkus.vertx.http.deployment.RouteBuildItem; -import io.quarkus.vertx.http.deployment.webjar.WebJarBuildItem; -import io.quarkus.vertx.http.deployment.webjar.WebJarResourcesFilter; -import io.quarkus.vertx.http.deployment.webjar.WebJarResultsBuildItem; -import io.vertx.core.Handler; -import io.vertx.ext.web.RoutingContext; public class KafkaProcessor { @@ -168,25 +152,11 @@ public class KafkaProcessor { static final DotName PARTITION_ASSIGNER = DotName .createSimple("org.apache.kafka.clients.consumer.internals.PartitionAssignor"); - // For the UI - private static final GACT KAFKA_UI_WEBJAR_ARTIFACT_KEY = new GACT("io.quarkus", "quarkus-kafka-client-ui", null, "jar"); - private static final String KAFKA_UI_WEBJAR_STATIC_RESOURCES_PATH = "META-INF/resources/kafka-ui/"; - private static final String FILE_TO_UPDATE = "config.js"; - private static final String LINE_TO_UPDATE = "export const api = '"; - private static final String LINE_FORMAT = LINE_TO_UPDATE + "%s';"; - private static final String UI_LINE_TO_UPDATE = "export const ui = '"; - private static final String UI_LINE_FORMAT = UI_LINE_TO_UPDATE + "%s';"; - private static final String LOGO_LINE_TO_UPDATE = "export const logo = '"; - private static final String LOGO_LINE_FORMAT = LOGO_LINE_TO_UPDATE + "%s';"; - private static final String UI_LOGO_PATH = "logo.png"; - // UI brandibg - private static final String BRANDING_DIR = "META-INF/branding/"; - private static final String BRANDING_LOGO_GENERAL = BRANDING_DIR + "logo.png"; - private static final String BRANDING_LOGO_MODULE = BRANDING_DIR + "quarkus-kafka-client-ui.png"; - private static final String BRANDING_STYLE_GENERAL = BRANDING_DIR + "style.css"; - private static final String BRANDING_STYLE_MODULE = BRANDING_DIR + "quarkus-kafka-client-ui.css"; - private static final String BRANDING_FAVICON_GENERAL = BRANDING_DIR + "favicon.ico"; - private static final String BRANDING_FAVICON_MODULE = BRANDING_DIR + "quarkus-kafka-client-ui.ico"; + private static final GACT DEVCONSOLE_WEBJAR_ARTIFACT_KEY = new GACT("io.quarkus", + "quarkus-kafka-client-deployment", null, "jar"); + private static final String DEVCONSOLE_WEBJAR_STATIC_RESOURCES_PATH = "dev-static/"; + public static final String KAFKA_ADMIN_PATH = "kafka-admin"; + public static final String KAFKA_RESOURCES_ROOT_PATH = "kafka-ui"; @BuildStep FeatureBuildItem feature() { @@ -535,153 +505,28 @@ public AdditionalBeanBuildItem kafkaClientBeans() { .build(); } - @BuildStep + @BuildStep(onlyIf = IsDevelopment.class) @Record(ExecutionTime.RUNTIME_INIT) public void registerKafkaUiExecHandler( - BuildProducer routeProducer, - KafkaUiRecorder recorder, - LaunchModeBuildItem launchMode, - HttpRootPathBuildItem httpRootPathBuildItem, - NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem, - KafkaBuildTimeConfig buildConfig, - BodyHandlerBuildItem bodyHandlerBuildItem, - ShutdownContextBuildItem shutdownContext) { - - if (shouldIncludeUi(launchMode, buildConfig)) { - String handlerPath = nonApplicationRootPathBuildItem.resolvePath(buildConfig.ui.handlerRootPath); - Handler executionHandler = recorder.kafkaControlHandler(); - HttpRootPathBuildItem.Builder requestBuilder = httpRootPathBuildItem.routeBuilder() - .routeFunction(handlerPath, recorder.routeFunction(bodyHandlerBuildItem.getHandler())) - .handler(executionHandler) - .routeConfigKey("quarkus.kafka-client-ui.root-path") - .displayOnNotFoundPage("Kafka UI Endpoint"); - - routeProducer.produce(requestBuilder.build()); - } - } - - @BuildStep - List uiBrandingFiles() { - return Stream.of(BRANDING_LOGO_GENERAL, - BRANDING_STYLE_GENERAL, - BRANDING_FAVICON_GENERAL, - BRANDING_LOGO_MODULE, - BRANDING_STYLE_MODULE, - BRANDING_FAVICON_MODULE).map(HotDeploymentWatchedFileBuildItem::new) - .collect(Collectors.toList()); + BuildProducer routeProducer, + KafkaUiRecorder recorder) { + routeProducer.produce(DevConsoleRouteBuildItem.builder() + .method("POST") + .handler(recorder.kafkaControlHandler()) + .path(KAFKA_ADMIN_PATH) + .bodyHandlerRequired() + .build()); } - @BuildStep - void getKafkaUiFinalDestination( - HttpRootPathBuildItem httpRootPath, - NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem, - LaunchModeBuildItem launchMode, - KafkaBuildTimeConfig buildConfig, - BuildProducer webJarBuildProducer) { - - if (shouldIncludeUi(launchMode, buildConfig)) { - - if ("/".equals(buildConfig.ui.rootPath)) { - throw new ConfigurationException( - "quarkus.kafka-client-ui.root-path was set to \"/\", this is not allowed as it blocks the application from serving anything else.", - Collections.singleton("quarkus.kafka-client-ui.root-path")); - } - - String devUiPath = nonApplicationRootPathBuildItem.resolvePath("dev"); - String kafkaUiPath = nonApplicationRootPathBuildItem.resolvePath(buildConfig.ui.rootPath); - String kafkaHandlerPath = nonApplicationRootPathBuildItem.resolvePath(buildConfig.ui.handlerRootPath); - webJarBuildProducer.produce( - WebJarBuildItem.builder().artifactKey(KAFKA_UI_WEBJAR_ARTIFACT_KEY) - .root(KAFKA_UI_WEBJAR_STATIC_RESOURCES_PATH) - .filter(new WebJarResourcesFilter() { - @Override - public WebJarResourcesFilter.FilterResult apply(String fileName, InputStream file) - throws IOException { - if (fileName.endsWith(FILE_TO_UPDATE)) { - String content = new String(file.readAllBytes(), StandardCharsets.UTF_8); - content = updateUrl(content, kafkaHandlerPath, - LINE_TO_UPDATE, - LINE_FORMAT); - content = updateUrl(content, kafkaUiPath, - UI_LINE_TO_UPDATE, - UI_LINE_FORMAT); - content = updateUrl(content, - getLogoUrl(launchMode, kafkaUiPath + "/" + UI_LOGO_PATH, - kafkaUiPath + "/" + UI_LOGO_PATH), - LOGO_LINE_TO_UPDATE, - LOGO_LINE_FORMAT); - - return new WebJarResourcesFilter.FilterResult( - new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)), true); - } - - return new WebJarResourcesFilter.FilterResult(file, false); - } - }) - .build()); + @BuildStep(onlyIf = IsDevelopment.class) + public DevConsoleWebjarBuildItem setupWebJar(LaunchModeBuildItem launchModeBuildItem) { + if (launchModeBuildItem.getDevModeType().orElse(null) != DevModeType.LOCAL) { + return null; } + return DevConsoleWebjarBuildItem.builder().artifactKey(DEVCONSOLE_WEBJAR_ARTIFACT_KEY) + .root(DEVCONSOLE_WEBJAR_STATIC_RESOURCES_PATH) + .routeRoot(KAFKA_RESOURCES_ROOT_PATH) + .build(); } - @BuildStep - @Record(ExecutionTime.RUNTIME_INIT) - void registerKafkaUiHandler( - BuildProducer routeProducer, - KafkaUiRecorder recorder, - LaunchModeBuildItem launchMode, - NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem, - KafkaBuildTimeConfig buildConfig, - WebJarResultsBuildItem webJarResultsBuildItem, - ShutdownContextBuildItem shutdownContext) { - - WebJarResultsBuildItem.WebJarResult result = webJarResultsBuildItem.byArtifactKey(KAFKA_UI_WEBJAR_ARTIFACT_KEY); - if (result == null) { - return; - } - - if (shouldIncludeUi(launchMode, buildConfig)) { - String kafkaUiPath = nonApplicationRootPathBuildItem.resolvePath(buildConfig.ui.rootPath); - String finalDestination = result.getFinalDestination(); - - Handler handler = recorder.uiHandler(finalDestination, - kafkaUiPath, result.getWebRootConfigurations(), shutdownContext); - routeProducer.produce(nonApplicationRootPathBuildItem.routeBuilder() - .route(buildConfig.ui.rootPath) - .displayOnNotFoundPage("Kafka UI") - .routeConfigKey("quarkus.kafka-client.ui.root-path") - .handler(handler) - .build()); - - routeProducer.produce(nonApplicationRootPathBuildItem.routeBuilder() - .route(buildConfig.ui.rootPath + "*") - .handler(handler) - .build()); - - } - } - - // In dev mode, when you click on the logo, you should go to Dev UI - private String getLogoUrl(LaunchModeBuildItem launchMode, String devUIValue, String defaultValue) { - if (launchMode.getLaunchMode().equals(LaunchMode.DEVELOPMENT)) { - return devUIValue; - } - return defaultValue; - } - - private String updateUrl(String original, String path, String lineStartsWith, String format) { - try (Scanner scanner = new Scanner(original)) { - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - if (line.trim().startsWith(lineStartsWith)) { - String newLine = String.format(format, path); - return original.replace(line.trim(), newLine); - } - } - } - - return original; - } - - private static boolean shouldIncludeUi(LaunchModeBuildItem launchMode, KafkaBuildTimeConfig config) { - return launchMode.getLaunchMode().isDevOrTest() || config.ui.alwaysInclude; - } } diff --git a/extensions/kafka-client/deployment/src/main/resources/dev-static/js/config.js b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/config.js new file mode 100644 index 00000000000000..130b130828fbed --- /dev/null +++ b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/config.js @@ -0,0 +1,2 @@ +export const api = '/q/dev/io.quarkus.quarkus-kafka-client/kafka-admin'; +export const ui = 'kafka-ui'; \ No newline at end of file diff --git a/extensions/kafka-client/ui/src/main/webapp/kafka_ui.js b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/kafka_ui.js similarity index 73% rename from extensions/kafka-client/ui/src/main/webapp/kafka_ui.js rename to extensions/kafka-client/deployment/src/main/resources/dev-static/js/kafka_ui.js index c07fdfd3782174..36667854c19440 100644 --- a/extensions/kafka-client/ui/src/main/webapp/kafka_ui.js +++ b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/kafka_ui.js @@ -1,10 +1,8 @@ import Navigator from './pages/navigator.js' -import {setLogo} from "./util/logo.js"; const navigator = new Navigator(); $(document).ready( () => { - setLogo(); navigator.navigateToDefaultPage(); } ); diff --git a/extensions/kafka-client/ui/src/main/webapp/pages/accessControlListPage.js b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/accessControlListPage.js similarity index 84% rename from extensions/kafka-client/ui/src/main/webapp/pages/accessControlListPage.js rename to extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/accessControlListPage.js index 2e396ec39874a9..bf10bd05b5e660 100644 --- a/extensions/kafka-client/ui/src/main/webapp/pages/accessControlListPage.js +++ b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/accessControlListPage.js @@ -2,7 +2,7 @@ import {doPost, errorPopUp} from "../web/web.js"; import {createTableItem} from "../util/contentManagement.js"; import {toggleSpinner} from "../util/spinner.js"; -export default class AccessControlListPage{ +export default class AccessControlListPage { constructor(containerId) { this.containerId = containerId; Object.getOwnPropertyNames(AccessControlListPage.prototype).forEach((key) => { @@ -17,15 +17,15 @@ export default class AccessControlListPage{ const req = { action: "getAclInfo" }; + toggleSpinner(this.containerId); doPost(req, (data) => { - let that = this; - setTimeout(function () { - that.updateInfo(data); - toggleSpinner(that.containerId); + setTimeout(() => { + this.updateInfo(data); + toggleSpinner(this.containerId); }, 2000); }, data => { errorPopUp("Error getting Kafka ACL info: ", data); - }); + }); } updateInfo(data) { @@ -33,7 +33,7 @@ export default class AccessControlListPage{ $('#acluster-controller').html(data.broker); $('#acluster-acl').html(data.aclOperations); - const acls = data.entires; + const acls = data.entries; let aclTable = $('#acl-table tbody'); aclTable.empty(); for (let i = 0; i < acls.length; i++) { diff --git a/extensions/kafka-client/ui/src/main/webapp/pages/consumerGroupDetailsPage.js b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/consumerGroupDetailsPage.js similarity index 85% rename from extensions/kafka-client/ui/src/main/webapp/pages/consumerGroupDetailsPage.js rename to extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/consumerGroupDetailsPage.js index cfd39d4d8be2d4..3766588ce9292d 100644 --- a/extensions/kafka-client/ui/src/main/webapp/pages/consumerGroupDetailsPage.js +++ b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/consumerGroupDetailsPage.js @@ -16,7 +16,7 @@ export default class ConsumerGroupDetailsPage { consumerGroupsTable.empty(); for (let i = 0; i < membersData.length; i++) { const d = membersData[i]; - const groupId = "group-" + window.crypto.randomUUID(); + const groupId = "group-" + d.memberId; let tableRow = $(""); let collapseRow; @@ -37,18 +37,17 @@ export default class ConsumerGroupDetailsPage { tableRow.append(createTableItem(d.host)); tableRow.append(createTableItem("" + new Set(d.partitions.map(x => x.partition)).size)); tableRow.append(createTableItem("" + d.partitions.map(x => x.lag).reduce((l, r) => l + r, 0))); - consumerGroupsTable.append(tableRow); if (d.partitions.length > 0) { const content = this.createConsumerGroupCollapseInfo(d); - tableRow - .addClass("pointer") - .click(collapseRow.collapse); - consumerGroupsTable.append( - collapseRow - .getCollapseContent(tableRow.children().length, content) - .addClass("no-hover") - ); + tableRow.addClass("pointer") + tableRow.click(() => collapseRow.collapse()); + consumerGroupsTable.append(tableRow); + consumerGroupsTable.append(collapseRow + .getCollapseContent(tableRow.children().length, content) + .addClass("no-hover")); + } else { + consumerGroupsTable.append(tableRow); } } } diff --git a/extensions/kafka-client/ui/src/main/webapp/pages/consumerGroupPage.js b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/consumerGroupPage.js similarity index 94% rename from extensions/kafka-client/ui/src/main/webapp/pages/consumerGroupPage.js rename to extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/consumerGroupPage.js index 4a8500fab0c0e0..51928af67a7e33 100644 --- a/extensions/kafka-client/ui/src/main/webapp/pages/consumerGroupPage.js +++ b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/consumerGroupPage.js @@ -40,8 +40,7 @@ export default class ConsumerGroupPage { tableRow.append(createTableItem(d.protocol)); tableRow.append(createTableItem(d.members.length)); tableRow.append(createTableItem(d.lag)); - const self = this; - tableRow.click(() => self.navigator.navigateTo(pages.CONSUMER_GROUPS_DETAILS, [d.name, d.members])); + tableRow.click(() => this.navigator.navigateTo(pages.CONSUMER_GROUPS_DETAILS, [d.name, d.members])); consumerGroupsTable.append(tableRow); } } diff --git a/extensions/kafka-client/ui/src/main/webapp/pages/messagesPage.js b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/messagesPage.js similarity index 100% rename from extensions/kafka-client/ui/src/main/webapp/pages/messagesPage.js rename to extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/messagesPage.js diff --git a/extensions/kafka-client/ui/src/main/webapp/pages/navigator.js b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/navigator.js similarity index 100% rename from extensions/kafka-client/ui/src/main/webapp/pages/navigator.js rename to extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/navigator.js diff --git a/extensions/kafka-client/ui/src/main/webapp/pages/nodesPage.js b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/nodesPage.js similarity index 90% rename from extensions/kafka-client/ui/src/main/webapp/pages/nodesPage.js rename to extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/nodesPage.js index ad8675344e22b4..94b2b1e6a270d0 100644 --- a/extensions/kafka-client/ui/src/main/webapp/pages/nodesPage.js +++ b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/nodesPage.js @@ -17,10 +17,9 @@ export default class NodesPage { action: "getInfo" }; doPost(req, (data) => { - let that = this; - setTimeout(function () { - that.updateInfo(data); - toggleSpinner(that.containerId); + setTimeout(() => { + this.updateInfo(data); + toggleSpinner(this.containerId); }, 2000); }, data => { errorPopUp("Error getting Kafka info: ", data); diff --git a/extensions/kafka-client/ui/src/main/webapp/pages/schemaPage.js b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/schemaPage.js similarity index 100% rename from extensions/kafka-client/ui/src/main/webapp/pages/schemaPage.js rename to extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/schemaPage.js diff --git a/extensions/kafka-client/ui/src/main/webapp/pages/topicsPage.js b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/topicsPage.js similarity index 100% rename from extensions/kafka-client/ui/src/main/webapp/pages/topicsPage.js rename to extensions/kafka-client/deployment/src/main/resources/dev-static/js/pages/topicsPage.js diff --git a/extensions/kafka-client/ui/src/main/webapp/util/contentManagement.js b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/util/contentManagement.js similarity index 100% rename from extensions/kafka-client/ui/src/main/webapp/util/contentManagement.js rename to extensions/kafka-client/deployment/src/main/resources/dev-static/js/util/contentManagement.js diff --git a/extensions/kafka-client/ui/src/main/webapp/util/datetimeUtil.js b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/util/datetimeUtil.js similarity index 100% rename from extensions/kafka-client/ui/src/main/webapp/util/datetimeUtil.js rename to extensions/kafka-client/deployment/src/main/resources/dev-static/js/util/datetimeUtil.js diff --git a/extensions/kafka-client/ui/src/main/webapp/util/spinner.js b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/util/spinner.js similarity index 100% rename from extensions/kafka-client/ui/src/main/webapp/util/spinner.js rename to extensions/kafka-client/deployment/src/main/resources/dev-static/js/util/spinner.js diff --git a/extensions/kafka-client/ui/src/main/webapp/web/web.js b/extensions/kafka-client/deployment/src/main/resources/dev-static/js/web/web.js similarity index 100% rename from extensions/kafka-client/ui/src/main/webapp/web/web.js rename to extensions/kafka-client/deployment/src/main/resources/dev-static/js/web/web.js diff --git a/extensions/kafka-client/deployment/src/main/resources/dev-templates/embedded.html b/extensions/kafka-client/deployment/src/main/resources/dev-templates/embedded.html index 312a6c5268e02f..dac4cf81f41501 100644 --- a/extensions/kafka-client/deployment/src/main/resources/dev-templates/embedded.html +++ b/extensions/kafka-client/deployment/src/main/resources/dev-templates/embedded.html @@ -1,3 +1,3 @@ - + Kafka UI diff --git a/extensions/kafka-client/deployment/src/main/resources/dev-templates/kafka-dev-ui.html b/extensions/kafka-client/deployment/src/main/resources/dev-templates/kafka-dev-ui.html new file mode 100644 index 00000000000000..f07f33984441fe --- /dev/null +++ b/extensions/kafka-client/deployment/src/main/resources/dev-templates/kafka-dev-ui.html @@ -0,0 +1,513 @@ +{#include main fluid=true} +{#style} +html { +min-height: 90vh; +min-width: 100vh; +} + +body { +min-height: 90vh; +min-width: 100vh; +} + +.row-holder { +padding: 0; +margin: 0; + +} + +.row:after { +content: ""; +display: table; +clear: both; +} + +.content-holder { +height: auto; +min-height: 90vh; +} + +.link { +background: none; +border: none; +} + +.top-margin { +margin-top: 1em; +} + +.left-margin { +margin-left: 1em; +} + +.left-padding { +padding-left: 1em; +} + +.shown { +display: flex; +height: auto; +min-width: 100%; +} + +.text-shown { +display: inline; +} + +.hidden { +display: none +} + +.nav-item:hover > .nav-row > a { +background-color: #005fff; +color: #e9ecef; +} + +.nav-item:hover > .nav-row > i { +background-color: #005fff; +color: #e9ecef; +} + +#navbar-list > .nav-item:hover { +background-color: #005fff; +color: #e9ecef; +} + +.table-hover:hover { +cursor: pointer; +} + +.multiselect-container > li > a > label { +padding-left: 15px !important; +} + +.page { +min-height: calc(100vh - 135px); +} + +.table-hover:hover { +cursor: pointer; +} + +.pointer { +cursor: pointer; +} + +.no-hover { +background-color: white; +cursor: default; +} + +.no-hover:hover { +background-color: white !important; +cursor: default; +} + +.icon-rotated { +transform: rotate(90deg); +} + +.navbar-brand img { +border-right: 1px solid darkgrey; +padding-right: 10px; +margin-right: 5px; +} + +.navbar-brand { +padding: 0; +margin: 0; +} + +#nav-menu-panel { +padding: 0px; +} + +.float-plus-btn { +position: fixed; +bottom: 60px; +right: 60px; +border-radius: 100%; +height: 50px; +width: 50px; +} + +.breadcrumb-item::before { +float: left; +padding-right: 0.5rem; +color: #007bff; +content: "〉"; +} + +.breadcrumb-item + .breadcrumb-item::before { +float: left; +padding-right: 0.5rem; +color: #007bff; +content: "〉"; +} + +.breadcrumb { +background-color: #343a40; +margin-bottom: 0; +padding: 0 0 0 5px; +} + +.bi-trash-fill:hover { +color: #007bff; +} + +.collapse-content { +max-width: 1200px; +} + +.thead-multiselect { +background-color: #343a40; +color: white; +border: 0px; +font-weight: bold; +} + +.thead-text { +color: white; +} + +#msg-table-holder { +min-width: 100%; +} +{/style} +{#styleref} + + +{/styleref} +{#scriptref} + + +{/scriptref} +{#title}Kafka Dev UI{/title} +{#body} +
+ + + + + + +
+
+
+ + + + + + + + + + + + +
Topic NameIdPartitions countNumber of msg
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + +
Offset + + Timestamp KeyValue
+
+ +
+
+ + +
+
+
+ + + + + + + + + + + + + +
StateIdCoordinatorProtocolMembersLag(Sum)
+
+
+
+
+ + + + + + + + + + + + +
Member IDHostPartitionsLag(Sum)
+
+
+
+
+
+ Kafka cluster id: 
+ Controller node (broker): 
+ ACL operations: 
+
+
+

Cluster nodes

+
+ + + + + + + + + + +
IdHostPort
+
+
+ +
+
+{/body} +{/include} \ No newline at end of file diff --git a/extensions/kafka-client/pom.xml b/extensions/kafka-client/pom.xml index dd7448eb061065..a0e0885d2b1700 100644 --- a/extensions/kafka-client/pom.xml +++ b/extensions/kafka-client/pom.xml @@ -15,7 +15,6 @@ pom - ui deployment runtime diff --git a/extensions/kafka-client/runtime/pom.xml b/extensions/kafka-client/runtime/pom.xml index 0e037445a128e1..1d237acadf2134 100644 --- a/extensions/kafka-client/runtime/pom.xml +++ b/extensions/kafka-client/runtime/pom.xml @@ -61,7 +61,7 @@ io.quarkus - quarkus-vertx-http + quarkus-vertx-http-dev-console-runtime-spi diff --git a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/AbstractHttpRequestHandler.java b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/AbstractHttpRequestHandler.java index bc83093041b33e..ede6bc54f21f84 100644 --- a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/AbstractHttpRequestHandler.java +++ b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/AbstractHttpRequestHandler.java @@ -2,24 +2,15 @@ import io.quarkus.arc.Arc; import io.quarkus.arc.ManagedContext; -import io.quarkus.security.identity.CurrentIdentityAssociation; -import io.quarkus.security.identity.SecurityIdentity; -import io.quarkus.vertx.http.runtime.CurrentVertxRequest; -import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.vertx.core.Handler; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; public abstract class AbstractHttpRequestHandler implements Handler { - private final CurrentIdentityAssociation currentIdentityAssociation; - private final CurrentVertxRequest currentVertxRequest; private final ManagedContext currentManagedContext; private final Handler currentManagedContextTerminationHandler; - public AbstractHttpRequestHandler(CurrentIdentityAssociation currentIdentityAssociation, - CurrentVertxRequest currentVertxRequest) { - this.currentIdentityAssociation = currentIdentityAssociation; - this.currentVertxRequest = currentVertxRequest; + public AbstractHttpRequestHandler() { this.currentManagedContext = Arc.container().requestContext(); this.currentManagedContextTerminationHandler = e -> currentManagedContext.terminate(); } @@ -29,7 +20,7 @@ public AbstractHttpRequestHandler(CurrentIdentityAssociation currentIdentityAsso public void handle(final RoutingContext ctx) { if (currentManagedContext.isActive()) { - handleWithIdentity(ctx); + doHandle(ctx); } else { currentManagedContext.activate(); @@ -39,7 +30,7 @@ public void handle(final RoutingContext ctx) { .closeHandler(currentManagedContextTerminationHandler); try { - handleWithIdentity(ctx); + doHandle(ctx); } catch (Throwable t) { currentManagedContext.terminate(); throw t; @@ -70,20 +61,6 @@ public void doHandle(RoutingContext ctx) { } } - private void handleWithIdentity(final RoutingContext ctx) { - if (currentIdentityAssociation != null) { - QuarkusHttpUser existing = (QuarkusHttpUser) ctx.user(); - if (existing != null) { - SecurityIdentity identity = existing.getSecurityIdentity(); - currentIdentityAssociation.setIdentity(identity); - } else { - currentIdentityAssociation.setIdentity(QuarkusHttpUser.getSecurityIdentity(ctx, null)); - } - } - currentVertxRequest.setCurrent(ctx); - doHandle(ctx); - } - public abstract void handlePost(RoutingContext event); public abstract void handleGet(RoutingContext event); diff --git a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/KafkaUiHandler.java b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/KafkaUiHandler.java index 99d5b9d550813b..8b7d916de765b5 100644 --- a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/KafkaUiHandler.java +++ b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/KafkaUiHandler.java @@ -13,17 +13,11 @@ import io.quarkus.kafka.client.runtime.ui.model.request.KafkaMessageCreateRequest; import io.quarkus.kafka.client.runtime.ui.model.request.KafkaMessagesRequest; import io.quarkus.kafka.client.runtime.ui.model.request.KafkaOffsetRequest; -import io.quarkus.security.identity.CurrentIdentityAssociation; -import io.quarkus.vertx.http.runtime.CurrentVertxRequest; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; public class KafkaUiHandler extends AbstractHttpRequestHandler { - public KafkaUiHandler(CurrentIdentityAssociation currentIdentityAssociation, CurrentVertxRequest currentVertxRequest) { - super(currentIdentityAssociation, currentVertxRequest); - } - @Override public void handlePost(RoutingContext event) { if (event.body() == null) { diff --git a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/KafkaUiRecorder.java b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/KafkaUiRecorder.java index 21d89717ffcbc9..90afe8521cc115 100644 --- a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/KafkaUiRecorder.java +++ b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/KafkaUiRecorder.java @@ -1,18 +1,7 @@ package io.quarkus.kafka.client.runtime.ui; -import java.util.List; -import java.util.function.Consumer; - -import io.quarkus.arc.Arc; -import io.quarkus.arc.InstanceHandle; -import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; -import io.quarkus.security.identity.CurrentIdentityAssociation; -import io.quarkus.vertx.http.runtime.CurrentVertxRequest; -import io.quarkus.vertx.http.runtime.devmode.FileSystemStaticHandler; -import io.quarkus.vertx.http.runtime.webjar.WebJarStaticHandler; import io.vertx.core.Handler; -import io.vertx.ext.web.Route; import io.vertx.ext.web.RoutingContext; /** @@ -22,28 +11,7 @@ public class KafkaUiRecorder { public Handler kafkaControlHandler() { - return new KafkaUiHandler(getCurrentIdentityAssociation(), - Arc.container().instance(CurrentVertxRequest.class).get()); - } - - public Consumer routeFunction(Handler bodyHandler) { - return route -> route.handler(bodyHandler); + return new KafkaUiHandler(); } - public Handler uiHandler(String finalDestination, String uiPath, - List webRootConfigurations, - ShutdownContext shutdownContext) { - WebJarStaticHandler handler = new WebJarStaticHandler(finalDestination, uiPath, webRootConfigurations); - shutdownContext.addShutdownTask(new ShutdownContext.CloseRunnable(handler)); - return handler; - } - - private CurrentIdentityAssociation getCurrentIdentityAssociation() { - InstanceHandle identityAssociations = Arc.container() - .instance(CurrentIdentityAssociation.class); - if (identityAssociations.isAvailable()) { - return identityAssociations.get(); - } - return null; - } } diff --git a/extensions/kafka-client/ui/pom.xml b/extensions/kafka-client/ui/pom.xml deleted file mode 100644 index 31960a9ee918ef..00000000000000 --- a/extensions/kafka-client/ui/pom.xml +++ /dev/null @@ -1,212 +0,0 @@ - - - 4.0.0 - - - io.quarkus - quarkus-kafka-client-parent - 999-SNAPSHOT - - - quarkus-kafka-client-ui - jar - Quarkus - Kafka - Client - UI - - - kafka-ui - 0.9.15 - 1.9.1 - - - - - - org.webjars - bootstrap - provided - - - org.webjars - font-awesome - provided - - - org.webjars - jquery - provided - - - org.webjars - bootstrap-multiselect - ${bootstrap-mutiliselect.version} - - - org.webjars.npm - bootstrap-icons - ${bootstrap-icons.version} - - - - - - - - maven-resources-plugin - - - copy-web - generate-sources - - copy-resources - - - ${project.build.directory}/classes/META-INF/resources/${path.kafkaui} - - - ${basedir}/src/main/webapp - - **/*.* - - - - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - 3.3.0 - - - install-js - generate-sources - - unpack - - - - - - org.webjars - bootstrap - ${webjar.bootstrap.version} - jar - true - ${project.build.directory}/classes/META-INF/resources/${path.kafkaui}/css/ - **/bootstrap.min.css, **/bootstrap.min.css.map - - - - - - org.webjars - bootstrap - ${webjar.bootstrap.version} - jar - true - ${project.build.directory}/classes/META-INF/resources/${path.kafkaui}/js/ - **/bootstrap.bundle.min.js, **/bootstrap.bundle.min.js.map - - - - - - org.webjars - bootstrap-multiselect - ${bootstrap-mutiliselect.version} - jar - true - ${project.build.directory}/classes/META-INF/resources/${path.kafkaui}/js/ - **/bootstrap-multiselect.js - - - - - - org.webjars - bootstrap-multiselect - ${bootstrap-mutiliselect.version} - jar - true - ${project.build.directory}/classes/META-INF/resources/${path.kafkaui}/css/ - **/bootstrap-multiselect.css - - - - - - org.webjars.npm - bootstrap-icons - ${bootstrap-icons.version} - jar - true - ${project.build.directory}/classes/META-INF/resources/${path.kafkaui}/css/ - **/font/bootstrap-icons.css - - - - - - org.webjars.npm - bootstrap-icons - ${bootstrap-icons.version} - jar - true - ${project.build.directory}/classes/META-INF/resources/${path.kafkaui}/css/fonts/ - **/font/fonts/ - - - - - - - - org.webjars - jquery - ${webjar.jquery.version} - jar - true - ${project.build.directory}/classes/META-INF/resources/${path.kafkaui}/js/ - **/jquery.min.js, **/jquery.min.js.map - - - - - - - org.webjars - font-awesome - ${webjar.font-awesome.version} - jar - true - ${project.build.directory}/classes/META-INF/resources/${path.kafkaui}/fontawesome/css - **/css/all.min.css - - - - - - org.webjars - font-awesome - ${webjar.font-awesome.version} - jar - true - ${project.build.directory}/classes/META-INF/resources/${path.kafkaui}/fontawesome/webfonts - **/webfonts/**.* - - - - - - - - - - - - - - - diff --git a/extensions/kafka-client/ui/src/main/webapp/config.js b/extensions/kafka-client/ui/src/main/webapp/config.js deleted file mode 100644 index 1c141c4c1e97c7..00000000000000 --- a/extensions/kafka-client/ui/src/main/webapp/config.js +++ /dev/null @@ -1,4 +0,0 @@ -export const api = '/kafka-admin'; -export const logo = 'quarkus_icon_rgb_reverse.svg'; -export const faviconLogo = 'favicon.ico'; -export const ui = ''; \ No newline at end of file diff --git a/extensions/kafka-client/ui/src/main/webapp/favicon.ico b/extensions/kafka-client/ui/src/main/webapp/favicon.ico deleted file mode 100644 index b4ef4208a6f489..00000000000000 Binary files a/extensions/kafka-client/ui/src/main/webapp/favicon.ico and /dev/null differ diff --git a/extensions/kafka-client/ui/src/main/webapp/index.html b/extensions/kafka-client/ui/src/main/webapp/index.html deleted file mode 100644 index 181056ca3ead08..00000000000000 --- a/extensions/kafka-client/ui/src/main/webapp/index.html +++ /dev/null @@ -1,532 +0,0 @@ - - - - - kafka-devUI - - - - - - - - - - -
- - - - - - -
-
-
- - - - - - - - - - - - -
Topic NameIdPartitions countNumber of msg
-
- -
-
-
-
-
- - - - - - - - - - - - - - -
Offset - - Timestamp KeyValue
-
- -
-
- - -
-
-
- - - - - - - - - - - - - -
StateIdCoordinatorProtocolMembersLag(Sum)
-
-
-
-
- - - - - - - - - - - - -
Member IDHostPartitionsLag(Sum)
-
-
-
-
-
- Kafka cluster id: 
- Controller node (broker): 
- ACL operations: 
-
-
-

Cluster nodes

-
- - - - - - - - - - -
IdHostPort
-
-
- -
-
- - - - - - - - - - diff --git a/extensions/kafka-client/ui/src/main/webapp/logo.png b/extensions/kafka-client/ui/src/main/webapp/logo.png deleted file mode 100644 index 6a1626104eb98f..00000000000000 Binary files a/extensions/kafka-client/ui/src/main/webapp/logo.png and /dev/null differ diff --git a/extensions/kafka-client/ui/src/main/webapp/quarkus_icon_rgb_reverse.svg b/extensions/kafka-client/ui/src/main/webapp/quarkus_icon_rgb_reverse.svg deleted file mode 100644 index 1969e1e886af39..00000000000000 --- a/extensions/kafka-client/ui/src/main/webapp/quarkus_icon_rgb_reverse.svg +++ /dev/null @@ -1 +0,0 @@ -quarkus_icon_rgb_1024px_reverse \ No newline at end of file diff --git a/extensions/kafka-client/ui/src/main/webapp/util/logo.js b/extensions/kafka-client/ui/src/main/webapp/util/logo.js deleted file mode 100644 index 2f40efa91fb37f..00000000000000 --- a/extensions/kafka-client/ui/src/main/webapp/util/logo.js +++ /dev/null @@ -1,8 +0,0 @@ -import {faviconLogo, logo} from "../config.js" - -export function setLogo(){ - $("#navbar-logo") - .attr("src", logo); - $("#favicon") - .attr("href", faviconLogo); -} \ No newline at end of file diff --git a/extensions/vertx-http/deployment/pom.xml b/extensions/vertx-http/deployment/pom.xml index f73b67dd3c39e8..a5154ddd7b9393 100644 --- a/extensions/vertx-http/deployment/pom.xml +++ b/extensions/vertx-http/deployment/pom.xml @@ -52,6 +52,16 @@ bootstrap provided
+ + org.webjars + bootstrap-multiselect + provided + + + org.webjars.npm + bootstrap-icons + provided + org.webjars font-awesome @@ -196,6 +206,55 @@ + + + org.webjars + bootstrap-multiselect + ${webjar.bootstrap-multiselect.version} + jar + true + ${project.build.directory}/classes/dev-static/js/ + **/bootstrap-multiselect.js + + + + + + org.webjars + bootstrap-multiselect + ${webjar.bootstrap-multiselect.version} + jar + true + ${project.build.directory}/classes/dev-static/css/ + **/bootstrap-multiselect.css + + + + + + org.webjars.npm + bootstrap-icons + ${webjar.bootstrap-icons.version} + jar + true + ${project.build.directory}/classes/dev-static/css/ + **/font/bootstrap-icons.css + + + + + + org.webjars.npm + bootstrap-icons + ${webjar.bootstrap-icons.version} + jar + true + ${project.build.directory}/classes/dev-static/css/fonts/ + **/font/fonts/ + + + + org.webjars diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java index 0943aeda594d15..6c34e448daabe5 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java @@ -64,6 +64,7 @@ import io.quarkus.devconsole.spi.DevConsoleRouteBuildItem; import io.quarkus.devconsole.spi.DevConsoleRuntimeTemplateInfoBuildItem; import io.quarkus.devconsole.spi.DevConsoleTemplateInfoBuildItem; +import io.quarkus.devconsole.spi.DevConsoleWebjarBuildItem; import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.maven.dependency.GACT; import io.quarkus.netty.runtime.virtual.VirtualChannel; @@ -88,6 +89,7 @@ import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.TemplateHtmlBuilder; import io.quarkus.utilities.OS; +import io.quarkus.vertx.http.deployment.BodyHandlerBuildItem; import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem; import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; @@ -387,6 +389,46 @@ public WebJarBuildItem setupWebJar( .build(); } + @BuildStep(onlyIf = IsDevelopment.class) + public void setupDevConsoleWebjar( + List devConsoleWebjarBuildItems, + BuildProducer webJarBuildItemBuildProducer, + LaunchModeBuildItem launchModeBuildItem) { + if (launchModeBuildItem.getDevModeType().orElse(null) != DevModeType.LOCAL) { + return; + } + for (DevConsoleWebjarBuildItem devConsoleWebjar : devConsoleWebjarBuildItems) { + webJarBuildItemBuildProducer.produce(WebJarBuildItem.builder() + .artifactKey(devConsoleWebjar.getArtifactKey()) + .root(devConsoleWebjar.getRoot()) + .useDefaultQuarkusBranding(devConsoleWebjar.getUseDefaultQuarkusBranding()) + .onlyCopyNonArtifactFiles(devConsoleWebjar.getOnlyCopyNonArtifactFiles()) + .build()); + } + } + + @Record(ExecutionTime.RUNTIME_INIT) + @BuildStep(onlyIf = IsDevelopment.class) + public void setupDevConsoleRoutes( + List devConsoleWebjarBuildItems, + DevConsoleRecorder recorder, + NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem, + ShutdownContextBuildItem shutdownContext, + BuildProducer routeBuildItemBuildProducer, + WebJarResultsBuildItem webJarResultsBuildItem) { + + for (DevConsoleWebjarBuildItem webjarBuildItem : devConsoleWebjarBuildItems) { + WebJarResultsBuildItem.WebJarResult result = webJarResultsBuildItem.byArtifactKey(webjarBuildItem.getArtifactKey()); + if (result == null) { + continue; + } + routeBuildItemBuildProducer.produce(nonApplicationRootPathBuildItem.routeBuilder() + .route("dev/" + webjarBuildItem.getRouteRoot() + "/*") + .handler(recorder.fileSystemStaticHandler(result.getWebRootConfigurations(), shutdownContext)) + .build()); + } + } + @BuildStep(onlyIf = { IsDevelopment.class }) public DevConsoleTemplateInfoBuildItem config(List serviceDescriptions) { return new DevConsoleTemplateInfoBuildItem("devServices", serviceDescriptions); @@ -404,7 +446,8 @@ public void setupDevConsoleRoutes( ShutdownContextBuildItem shutdownContext, BuildProducer routeBuildItemBuildProducer, WebJarResultsBuildItem webJarResultsBuildItem, - CurateOutcomeBuildItem curateOutcomeBuildItem) { + CurateOutcomeBuildItem curateOutcomeBuildItem, + BodyHandlerBuildItem bodyHandlerBuildItem) { WebJarResultsBuildItem.WebJarResult result = webJarResultsBuildItem.byArtifactKey(DEVCONSOLE_WEBJAR_ARTIFACT_KEY); @@ -432,7 +475,8 @@ public void setupDevConsoleRoutes( NonApplicationRootPathBuildItem.Builder builder = nonApplicationRootPathBuildItem.routeBuilder() .routeFunction( "dev/" + groupAndArtifact.getKey() + "." + groupAndArtifact.getValue() + "/" + i.getPath(), - new RuntimeDevConsoleRoute(i.getMethod())); + new RuntimeDevConsoleRoute(i.getMethod(), + i.isBodyHandlerRequired() ? bodyHandlerBuildItem.getHandler() : null)); if (i.isBlockingHandler()) { builder.blockingRoute(); } diff --git a/extensions/vertx-http/dev-console-spi/src/main/java/io/quarkus/devconsole/spi/DevConsoleWebjarBuildItem.java b/extensions/vertx-http/dev-console-spi/src/main/java/io/quarkus/devconsole/spi/DevConsoleWebjarBuildItem.java new file mode 100644 index 00000000000000..01d16ceea3e3c7 --- /dev/null +++ b/extensions/vertx-http/dev-console-spi/src/main/java/io/quarkus/devconsole/spi/DevConsoleWebjarBuildItem.java @@ -0,0 +1,105 @@ +package io.quarkus.devconsole.spi; + +import io.quarkus.builder.item.MultiBuildItem; +import io.quarkus.maven.dependency.GACT; + +public final class DevConsoleWebjarBuildItem extends MultiBuildItem { + /** + * ArtifactKey pointing to the web jar. Has to be one of the applications dependencies. + */ + private final GACT artifactKey; + + /** + * Root inside the webJar starting from which resources are unpacked. + */ + private final String root; + + /** + * Only copy resources of the webjar which are either user overridden, or contain variables. + */ + private final boolean onlyCopyNonArtifactFiles; + + /** + * Defines whether Quarkus can override resources of the webjar with Quarkus internal files. + */ + private final boolean useDefaultQuarkusBranding; + + /** + * The root of the route to expose resources of the webjar + */ + private final String routeRoot; + + private DevConsoleWebjarBuildItem(Builder builder) { + this.artifactKey = builder.artifactKey; + this.root = builder.root; + this.useDefaultQuarkusBranding = builder.useDefaultQuarkusBranding; + this.onlyCopyNonArtifactFiles = builder.onlyCopyNonArtifactFiles; + this.routeRoot = builder.routeRoot; + } + + public GACT getArtifactKey() { + return artifactKey; + } + + public String getRoot() { + return root; + } + + public boolean getUseDefaultQuarkusBranding() { + return useDefaultQuarkusBranding; + } + + public boolean getOnlyCopyNonArtifactFiles() { + return onlyCopyNonArtifactFiles; + } + + public String getRouteRoot() { + return routeRoot; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private GACT artifactKey; + private String root; + private boolean useDefaultQuarkusBranding = true; + private boolean onlyCopyNonArtifactFiles = true; + private String routeRoot; + + public Builder artifactKey(GACT artifactKey) { + this.artifactKey = artifactKey; + return this; + } + + public Builder root(String root) { + this.root = root; + + if (this.root != null && this.root.startsWith("/")) { + this.root = this.root.substring(1); + } + + return this; + } + + public Builder routeRoot(String route) { + this.routeRoot = route; + return this; + } + + public Builder useDefaultQuarkusBranding(boolean useDefaultQuarkusBranding) { + this.useDefaultQuarkusBranding = useDefaultQuarkusBranding; + return this; + } + + public Builder onlyCopyNonArtifactFiles(boolean onlyCopyNonArtifactFiles) { + this.onlyCopyNonArtifactFiles = onlyCopyNonArtifactFiles; + return this; + } + + public DevConsoleWebjarBuildItem build() { + return new DevConsoleWebjarBuildItem(this); + } + } +} diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/RuntimeDevConsoleRoute.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/RuntimeDevConsoleRoute.java index ff5a05a7056915..d3adff64c5e0ab 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/RuntimeDevConsoleRoute.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/RuntimeDevConsoleRoute.java @@ -2,24 +2,37 @@ import java.util.function.Consumer; +import io.vertx.core.Handler; import io.vertx.core.http.HttpMethod; import io.vertx.ext.web.Route; +import io.vertx.ext.web.RoutingContext; public class RuntimeDevConsoleRoute implements Consumer { private String method; + private Handler bodyHandler; + public RuntimeDevConsoleRoute() { } - public RuntimeDevConsoleRoute(String method) { + public RuntimeDevConsoleRoute(String method, Handler hasBodyHandler) { this.method = method; + this.bodyHandler = hasBodyHandler; } public String getMethod() { return method; } + public Handler getBodyHandler() { + return bodyHandler; + } + + public void setBodyHandler(Handler bodyHandler) { + this.bodyHandler = bodyHandler; + } + public RuntimeDevConsoleRoute setMethod(String method) { this.method = method; return this; @@ -29,5 +42,8 @@ public RuntimeDevConsoleRoute setMethod(String method) { public void accept(Route route) { route.method(HttpMethod.valueOf(method)) .order(-100); + if (bodyHandler != null) { + route.handler(bodyHandler); + } } }