Skip to content

Commit

Permalink
Merge pull request quarkusio#31553 from cescoffier/devui-config
Browse files Browse the repository at this point in the history
Initial version of the config editor page
  • Loading branch information
phillip-kruger authored Mar 3, 2023
2 parents 2767f39 + 8e636e5 commit 9579d9d
Show file tree
Hide file tree
Showing 7 changed files with 508 additions and 115 deletions.
6 changes: 6 additions & 0 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3331,6 +3331,12 @@
<version>${vaadin.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mvnpm.at.vaadin</groupId>
<artifactId>integer-field</artifactId>
<version>${vaadin.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mvnpm.at.vaadin</groupId>
<artifactId>field-base</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.quarkus.devui.deployment;

import static io.quarkus.vertx.http.deployment.devmode.console.ConfigEditorProcessor.cleanUpAsciiDocIfNecessary;
import static io.quarkus.vertx.http.deployment.devmode.console.ConfigEditorProcessor.isSetByDevServices;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -13,7 +16,10 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
Expand All @@ -27,6 +33,8 @@
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.ConfigDescriptionBuildItem;
import io.quarkus.deployment.builditem.DevServicesLauncherConfigResultBuildItem;
import io.quarkus.deployment.dev.devservices.DevServiceDescriptionBuildItem;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.deployment.util.IoUtil;
Expand All @@ -41,6 +49,7 @@
import io.quarkus.devui.spi.page.Page;
import io.quarkus.devui.spi.page.PageBuilder;
import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem;
import io.quarkus.vertx.http.runtime.devmode.ConfigDescription;

/**
* This creates static content that is used in dev UI. For example the index.html and any other data (json) available on build
Expand Down Expand Up @@ -283,7 +292,9 @@ void createBuildTimeData(BuildProducer<BuildTimeConstBuildItem> buildTimeConstPr
BuildProducer<ThemeVarsBuildItem> themeVarsProducer,
ExtensionsBuildItem extensionsBuildItem,
List<MenuPageBuildItem> menuPageBuildItems,
List<DevServiceDescriptionBuildItem> DevServiceDescriptions) {
List<DevServiceDescriptionBuildItem> devServiceDescriptions,
List<ConfigDescriptionBuildItem> configDescriptionBuildItems,
Optional<DevServicesLauncherConfigResultBuildItem> devServicesLauncherConfig) {

BuildTimeConstBuildItem internalBuildTimeData = new BuildTimeConstBuildItem(AbstractDevUIBuildItem.DEV_UI);

Expand All @@ -292,6 +303,101 @@ void createBuildTimeData(BuildProducer<BuildTimeConstBuildItem> buildTimeConstPr
Map<String, String> dark = new HashMap<>();
Map<String, String> light = new HashMap<>();

computeColors(themes, dark, light);

internalBuildTimeData.addBuildTimeData("themes", themes);

// Extensions
Map<ExtensionGroup, List<Extension>> response = Map.of(
ExtensionGroup.active, extensionsBuildItem.getActiveExtensions(),
ExtensionGroup.inactive, extensionsBuildItem.getInactiveExtensions());

internalBuildTimeData.addBuildTimeData("extensions", response);

// Sections Menu
Page extensions = Page.webComponentPageBuilder().internal()
.title("Extensions")
.icon("font-awesome-solid:puzzle-piece")
.componentLink("qwc-extensions.js").build();

Page configuration = Page.webComponentPageBuilder().internal()
.title("Configuration")
.icon("font-awesome-solid:sliders")
.componentLink("qwc-configuration.js").build();

internalBuildTimeData.addBuildTimeData("allConfiguration",
getAllConfig(configDescriptionBuildItems, devServicesLauncherConfig));

Page continuousTesting = Page.webComponentPageBuilder().internal()
.title("Continuous Testing")
.icon("font-awesome-solid:flask-vial")
.componentLink("qwc-continuous-testing.js").build();

internalBuildTimeData.addBuildTimeData("continuousTesting", "TODO: Continuous Testing");

Page devServices = Page.webComponentPageBuilder().internal()
.title("Dev services")
.icon("font-awesome-solid:wand-magic-sparkles")
.componentLink("qwc-dev-services.js").build();

internalBuildTimeData.addBuildTimeData("devServices", devServiceDescriptions);

Page buildSteps = Page.webComponentPageBuilder().internal()
.title("Build steps")
.icon("font-awesome-solid:hammer")
.componentLink("qwc-build-steps.js").build();

internalBuildTimeData.addBuildTimeData("buildSteps", "TODO: Build Steps");

// Add default menu items
@SuppressWarnings("unchecked")
List<Page> sectionMenu = new ArrayList(List.of(extensions, configuration, continuousTesting, devServices, buildSteps));

// Add any Menus from extensions
for (Extension e : extensionsBuildItem.getSectionMenuExtensions()) {
List<Page> pagesFromExtension = e.getMenuPages();
sectionMenu.addAll(pagesFromExtension);
}

internalBuildTimeData.addBuildTimeData("menuItems", sectionMenu);

// Add the Footer tabs
Page serverLog = Page.webComponentPageBuilder().internal()
.title("Server")
.icon("font-awesome-solid:server")
.componentLink("qwc-server-log.js").build();

Page devUiLog = Page.webComponentPageBuilder().internal()
.title("Dev UI")
.icon("font-awesome-solid:satellite-dish")
.componentLink("qwc-jsonrpc-messages.js").build();

@SuppressWarnings("unchecked")
List<Page> footerTabs = new ArrayList(List.of(serverLog, devUiLog));

// Add any Footer tabs from extensions
for (Extension e : extensionsBuildItem.getFooterTabsExtensions()) {
List<Page> tabsFromExtension = e.getFooterPages();
footerTabs.addAll(tabsFromExtension);
}

internalBuildTimeData.addBuildTimeData("footerTabs", footerTabs);

// Add version info
Map<String, String> applicationInfo = new HashMap<>();
applicationInfo.put("quarkusVersion", Version.getVersion());
applicationInfo.put("applicationName", config.getOptionalValue("quarkus.application.name", String.class).orElse(""));
applicationInfo.put("applicationVersion",
config.getOptionalValue("quarkus.application.version", String.class).orElse(""));
internalBuildTimeData.addBuildTimeData("applicationInfo", applicationInfo);

buildTimeConstProducer.produce(internalBuildTimeData);

themeVarsProducer.produce(new ThemeVarsBuildItem(light.keySet(), QUARKUS_BLUE.toString()));
}

private static void computeColors(Map<String, Map<String, String>> themes, Map<String, String> dark,
Map<String, String> light) {
// Quarkus logo colors
light.put("--quarkus-blue", QUARKUS_BLUE.toString());
dark.put("--quarkus-blue", QUARKUS_BLUE.toString());
Expand Down Expand Up @@ -382,95 +488,22 @@ void createBuildTimeData(BuildProducer<BuildTimeConstBuildItem> buildTimeConstPr

themes.put("dark", dark);
themes.put("light", light);
}

internalBuildTimeData.addBuildTimeData("themes", themes);

// Extensions
Map<ExtensionGroup, List<Extension>> response = Map.of(
ExtensionGroup.active, extensionsBuildItem.getActiveExtensions(),
ExtensionGroup.inactive, extensionsBuildItem.getInactiveExtensions());

internalBuildTimeData.addBuildTimeData("extensions", response);

// Sections Menu
Page extensions = Page.webComponentPageBuilder().internal()
.title("Extensions")
.icon("font-awesome-solid:puzzle-piece")
.componentLink("qwc-extensions.js").build();

Page configuration = Page.webComponentPageBuilder().internal()
.title("Configuration")
.icon("font-awesome-solid:sliders")
.componentLink("qwc-configuration.js").build();

internalBuildTimeData.addBuildTimeData("allConfiguration", "TODO: Configuration");

Page continuousTesting = Page.webComponentPageBuilder().internal()
.title("Continuous Testing")
.icon("font-awesome-solid:flask-vial")
.componentLink("qwc-continuous-testing.js").build();

internalBuildTimeData.addBuildTimeData("continuousTesting", "TODO: Continuous Testing");

Page devServices = Page.webComponentPageBuilder().internal()
.title("Dev services")
.icon("font-awesome-solid:wand-magic-sparkles")
.componentLink("qwc-dev-services.js").build();

internalBuildTimeData.addBuildTimeData("devServices", DevServiceDescriptions);

Page buildSteps = Page.webComponentPageBuilder().internal()
.title("Build steps")
.icon("font-awesome-solid:hammer")
.componentLink("qwc-build-steps.js").build();

internalBuildTimeData.addBuildTimeData("buildSteps", "TODO: Build Steps");

// Add default menu items
@SuppressWarnings("unchecked")
List<Page> sectionMenu = new ArrayList(List.of(extensions, configuration, continuousTesting, devServices, buildSteps));

// Add any Menus from extensions
for (Extension e : extensionsBuildItem.getSectionMenuExtensions()) {
List<Page> pagesFromExtension = e.getMenuPages();
sectionMenu.addAll(pagesFromExtension);
private List<ConfigDescription> getAllConfig(List<ConfigDescriptionBuildItem> configDescriptionBuildItems,
Optional<DevServicesLauncherConfigResultBuildItem> devServicesLauncherConfig) {
List<ConfigDescription> configDescriptions = new ArrayList<>();
for (ConfigDescriptionBuildItem item : configDescriptionBuildItems) {
configDescriptions.add(
new ConfigDescription(item.getPropertyName(),
formatJavadoc(cleanUpAsciiDocIfNecessary(item.getDocs())),
item.getDefaultValue(),
isSetByDevServices(devServicesLauncherConfig, item.getPropertyName()),
item.getValueTypeName(),
item.getAllowedValues(),
item.getConfigPhase().name()));
}

internalBuildTimeData.addBuildTimeData("menuItems", sectionMenu);

// Add the Footer tabs
Page serverLog = Page.webComponentPageBuilder().internal()
.title("Server")
.icon("font-awesome-solid:server")
.componentLink("qwc-server-log.js").build();

Page devUiLog = Page.webComponentPageBuilder().internal()
.title("Dev UI")
.icon("font-awesome-solid:satellite-dish")
.componentLink("qwc-jsonrpc-messages.js").build();

@SuppressWarnings("unchecked")
List<Page> footerTabs = new ArrayList(List.of(serverLog, devUiLog));

// Add any Footer tabs from extensions
for (Extension e : extensionsBuildItem.getFooterTabsExtensions()) {
List<Page> tabsFromExtension = e.getFooterPages();
footerTabs.addAll(tabsFromExtension);
}

internalBuildTimeData.addBuildTimeData("footerTabs", footerTabs);

// Add version info
Map<String, String> applicationInfo = new HashMap<>();
applicationInfo.put("quarkusVersion", Version.getVersion());
applicationInfo.put("applicationName", config.getOptionalValue("quarkus.application.name", String.class).orElse(""));
applicationInfo.put("applicationVersion",
config.getOptionalValue("quarkus.application.version", String.class).orElse(""));
internalBuildTimeData.addBuildTimeData("applicationInfo", applicationInfo);

buildTimeConstProducer.produce(internalBuildTimeData);

themeVarsProducer.produce(new ThemeVarsBuildItem(light.keySet(), QUARKUS_BLUE.toString()));
return configDescriptions;
}

private static final Color QUARKUS_BLUE = Color.from(211, 63, 54);
Expand Down Expand Up @@ -531,4 +564,20 @@ static Color from(int hue, int saturation, int lightness, double alpha) {
return new Color(hue, saturation, lightness, alpha);
}
}

private static final Pattern codePattern = Pattern.compile("(\\{@code )([^}]+)(\\})");
private static final Pattern linkPattern = Pattern.compile("(\\{@link )([^}]+)(\\})");

static String formatJavadoc(String val) {
if (val == null) {
return val;
}
// Replace {@code} and {@link}
val = codePattern.matcher(val).replaceAll("<code>$2</code>");
val = linkPattern.matcher(val).replaceAll("<code>$2</code>");
// Add br before @see and @deprecated
val = val.lines().filter(s -> !s.startsWith("@see")).collect(Collectors.joining("\n"));
val = val.replace("@deprecated", "<br><strong>Deprecated</strong>");
return val;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
import io.quarkus.devconsole.runtime.spi.DevConsolePostHandler;
import io.quarkus.devconsole.spi.DevConsoleRouteBuildItem;
import io.quarkus.devconsole.spi.DevConsoleRuntimeTemplateInfoBuildItem;
import io.quarkus.devui.runtime.ConfigJsonRpcService;
import io.quarkus.devui.spi.JsonRPCProvidersBuildItem;
import io.quarkus.vertx.http.runtime.devmode.ConfigDescription;
import io.quarkus.vertx.http.runtime.devmode.ConfigDescriptionsManager;
import io.quarkus.vertx.http.runtime.devmode.ConfigDescriptionsRecorder;
Expand Down Expand Up @@ -127,7 +129,7 @@ protected void handlePost(RoutingContext event, MultiMap form) throws Exception

}

private String cleanUpAsciiDocIfNecessary(String docs) {
public static String cleanUpAsciiDocIfNecessary(String docs) {
if (docs == null || !docs.toLowerCase(Locale.ROOT).contains("@asciidoclet")) {
return docs;
}
Expand All @@ -152,6 +154,16 @@ void handleRequests(BuildProducer<DevConsoleRouteBuildItem> devConsoleRouteProdu
}));
}

@BuildStep(onlyIf = IsDevelopment.class)
JsonRPCProvidersBuildItem registerJsonRpcService() {
DevConsoleManager.register("config-update-property", map -> {
Map<String, String> values = Collections.singletonMap(map.get("name"), map.get("value"));
updateConfig(values);
return null;
});
return new JsonRPCProvidersBuildItem("ConfigJsonRpcService", ConfigJsonRpcService.class);
}

private Map<String, String> filterAndApplyProfile(Map<String, String> autoconfig, List<String> configFilter,
String profile) {
return autoconfig.entrySet().stream()
Expand Down Expand Up @@ -208,7 +220,7 @@ static byte[] getConfig() {
}
}

static void updateConfig(Map<String, String> values) {
public static void updateConfig(Map<String, String> values) {
if (values != null && !values.isEmpty()) {
try {
Path configPath = getConfigPath();
Expand Down Expand Up @@ -292,7 +304,7 @@ private static Path getConfigPath() throws IOException {
return configPath;
}

private boolean isSetByDevServices(Optional<DevServicesLauncherConfigResultBuildItem> devServicesLauncherConfig,
public static boolean isSetByDevServices(Optional<DevServicesLauncherConfigResultBuildItem> devServicesLauncherConfig,
String propertyName) {
if (devServicesLauncherConfig.isPresent()) {
return devServicesLauncherConfig.get().getConfig().containsKey(propertyName);
Expand Down
5 changes: 5 additions & 0 deletions extensions/vertx-http/dev-ui-resources/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@
<artifactId>number-field</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mvnpm.at.vaadin</groupId>
<artifactId>integer-field</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mvnpm.at.vaadin</groupId>
<artifactId>field-base</artifactId>
Expand Down
Loading

0 comments on commit 9579d9d

Please sign in to comment.