Skip to content

Commit

Permalink
Kafka dev ui as a dev console route
Browse files Browse the repository at this point in the history
minor fixes for UI
  • Loading branch information
ozangunalp committed Sep 12, 2022
1 parent a570b8c commit 7753320
Show file tree
Hide file tree
Showing 38 changed files with 807 additions and 1,079 deletions.
5 changes: 0 additions & 5 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1265,11 +1265,6 @@
<artifactId>quarkus-kafka-client-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kafka-client-ui</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kafka-streams</artifactId>
Expand Down
12 changes: 12 additions & 0 deletions build-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@

<!-- Webjars used by the Dev UI -->
<webjar.bootstrap.version>4.6.1</webjar.bootstrap.version>
<webjar.bootstrap-multiselect.version>0.9.15</webjar.bootstrap-multiselect.version>
<webjar.bootstrap-icons.version>1.9.1</webjar.bootstrap-icons.version>
<webjar.font-awesome.version>6.1.2</webjar.font-awesome.version>
<webjar.jquery.version>3.6.1</webjar.jquery.version>
<webjar.codemirror.version>5.62.2</webjar.codemirror.version>
Expand Down Expand Up @@ -309,6 +311,16 @@
<artifactId>bootstrap</artifactId>
<version>${webjar.bootstrap.version}</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap-multiselect</artifactId>
<version>${webjar.bootstrap-multiselect.version}</version>
</dependency>
<dependency>
<groupId>org.webjars.npm</groupId>
<artifactId>bootstrap-icons</artifactId>
<version>${webjar.bootstrap-icons.version}</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>font-awesome</artifactId>
Expand Down
6 changes: 1 addition & 5 deletions extensions/kafka-client/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,8 @@
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-http-deployment</artifactId>
<artifactId>quarkus-vertx-http-dev-console-spi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kafka-client-ui</artifactId>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -30,10 +29,4 @@ public class KafkaBuildTimeConfig {
@ConfigItem
public KafkaDevServicesBuildTimeConfig devservices;

/**
* Kafka UI configuration
*/
@ConfigItem
@ConfigDocSection
public KafkaBuildTimeUiConfig ui;
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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 {

Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -535,153 +505,28 @@ public AdditionalBeanBuildItem kafkaClientBeans() {
.build();
}

@BuildStep
@BuildStep(onlyIf = IsDevelopment.class)
@Record(ExecutionTime.RUNTIME_INIT)
public void registerKafkaUiExecHandler(
BuildProducer<RouteBuildItem> 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<RoutingContext> 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<HotDeploymentWatchedFileBuildItem> 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<DevConsoleRouteBuildItem> 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<WebJarBuildItem> 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<RouteBuildItem> 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<RoutingContext> 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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const api = '/q/dev/io.quarkus.quarkus-kafka-client/kafka-admin';
export const ui = 'kafka-ui';
Original file line number Diff line number Diff line change
@@ -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();
}
);
Expand Down
Loading

0 comments on commit 7753320

Please sign in to comment.