From 8e650ba74afa04bf9a9184d057974afa8171d941 Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Thu, 13 Jul 2023 19:14:52 +0100 Subject: [PATCH] Move DevUI Config resolution to Runtime --- .../menu/ConfigurationProcessor.java | 284 +++--------------- .../resources/dev-ui/qwc/qwc-configuration.js | 15 +- .../runtime/config/ConfigDescriptionBean.java | 17 ++ .../runtime/config/ConfigDevUiRecorder.java | 267 ++++++++++++++++ .../runtime/config/ConfigJsonRPCService.java | 18 +- 5 files changed, 342 insertions(+), 259 deletions(-) create mode 100644 extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/config/ConfigDescriptionBean.java create mode 100644 extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/config/ConfigDevUiRecorder.java diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/ConfigurationProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/ConfigurationProcessor.java index bef8c88d577cd..7347c5ad90db5 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/ConfigurationProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/ConfigurationProcessor.java @@ -2,11 +2,9 @@ import static io.quarkus.vertx.http.deployment.devmode.console.ConfigEditorProcessor.cleanUpAsciiDocIfNecessary; import static io.quarkus.vertx.http.deployment.devmode.console.ConfigEditorProcessor.isSetByDevServices; -import static io.smallrye.config.Expressions.withoutExpansion; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -15,33 +13,34 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.eclipse.microprofile.config.ConfigProvider; -import org.eclipse.microprofile.config.spi.ConfigSource; +import jakarta.inject.Singleton; +import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.deployment.IsDevelopment; +import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.ConfigDescriptionBuildItem; import io.quarkus.deployment.builditem.DevServicesLauncherConfigResultBuildItem; import io.quarkus.dev.console.DevConsoleManager; import io.quarkus.devui.deployment.InternalPageBuildItem; +import io.quarkus.devui.runtime.config.ConfigDescriptionBean; +import io.quarkus.devui.runtime.config.ConfigDevUiRecorder; import io.quarkus.devui.runtime.config.ConfigJsonRPCService; import io.quarkus.devui.spi.JsonRPCProvidersBuildItem; import io.quarkus.devui.spi.page.Page; import io.quarkus.vertx.http.deployment.devmode.console.ConfigEditorProcessor; import io.quarkus.vertx.http.runtime.devmode.ConfigDescription; -import io.smallrye.config.ConfigValue; -import io.smallrye.config.SmallRyeConfig; /** * This creates Extensions Page */ public class ConfigurationProcessor { - private static final String QUOTED_DOT = "\".\""; - private static final String QUOTED_DOT_KEY = "$$QUOTED_DOT$$"; - @BuildStep(onlyIf = IsDevelopment.class) - InternalPageBuildItem createConfigurationPages(List configDescriptionBuildItems, + InternalPageBuildItem createConfigurationPages( + List configDescriptionBuildItems, Optional devServicesLauncherConfig) { InternalPageBuildItem configurationPages = new InternalPageBuildItem("Configuration", 20); @@ -58,29 +57,17 @@ InternalPageBuildItem createConfigurationPages(List .icon("font-awesome-solid:code") .componentLink("qwc-configuration-editor.js")); - configurationPages.addBuildTimeData("allConfiguration", - getAllConfig(configDescriptionBuildItems, devServicesLauncherConfig)); + configurationPages.addBuildTimeData("allConfiguration", new ArrayList()); return configurationPages; } @BuildStep(onlyIf = IsDevelopment.class) - JsonRPCProvidersBuildItem registerJsonRpcService() { - DevConsoleManager.register("config-update-property", map -> { - Map values = Collections.singletonMap(map.get("name"), map.get("value")); - ConfigEditorProcessor.updateConfig(values, false); - return null; - }); - DevConsoleManager.register("config-set-properties", value -> { - String content = value.get("content"); - ConfigEditorProcessor.setConfig(content, false); - return null; - }); - return new JsonRPCProvidersBuildItem("devui-configuration", ConfigJsonRPCService.class); - } + @Record(ExecutionTime.STATIC_INIT) + void registerConfigs(List configDescriptionBuildItems, + Optional devServicesLauncherConfig, + ConfigDevUiRecorder recorder) { - private List getAllConfig(List configDescriptionBuildItems, - Optional devServicesLauncherConfig) { List configDescriptions = new ArrayList<>(); for (ConfigDescriptionBuildItem item : configDescriptionBuildItems) { configDescriptions.add( @@ -98,228 +85,41 @@ private List getAllConfig(List co devServicesConfig.addAll(devServicesLauncherConfig.get().getConfig().keySet()); } - return calculate(configDescriptions, devServicesConfig); + recorder.registerConfigs(configDescriptions, devServicesConfig); } - private List calculate(List cd, Set devServicesProperties) { - List configDescriptions = new ArrayList<>(cd); - - List ordered = new ArrayList<>(); - List properties = new ArrayList<>(); - SmallRyeConfig current = (SmallRyeConfig) ConfigProvider.getConfig(); - - Map, Set> allPropertySegments = new HashMap<>(); - Set propertyNames = new HashSet<>(); - current.getPropertyNames().forEach(propertyNames::add); - for (String propertyName : propertyNames) { - propertyName = propertyName.replace(QUOTED_DOT, QUOTED_DOT_KEY); // Make sure dots can be quoted - String[] parts = propertyName.split("\\."); - - List accumulate = new ArrayList<>(); - //we never want to add the full string - //hence -1 - for (int i = 0; i < parts.length - 1; ++i) { - if (parts[i].isEmpty()) { - //this can't map to a quarkus prop as it has an empty segment - //so skip - break; - } - // If there was a quoted dot, put that back - if (parts[i].contains(QUOTED_DOT_KEY)) { - parts[i] = parts[i].replaceAll(QUOTED_DOT_KEY, QUOTED_DOT); - } - - accumulate.add(parts[i]); - //if there is both a quoted and unquoted version we only want to apply the quoted version - //and remove the unquoted one - Set potentialSegmentSet = allPropertySegments.computeIfAbsent(List.copyOf(accumulate), - (k) -> new HashSet<>()); - if (isQuoted(parts[i + 1])) { - potentialSegmentSet.add(parts[i + 1]); - potentialSegmentSet.remove(parts[i + 1].substring(1, parts[i + 1].length() - 1)); - } else { - if (!potentialSegmentSet.contains(ensureQuoted(parts[i + 1]))) { - potentialSegmentSet.add(parts[i + 1]); - } - } - - } - } - - Map, Set> wildcardsToAdd = new HashMap<>(); - Map foundItems = new HashMap<>(); - Set bannedExpansionCombos = new HashSet<>(); - //we iterate over every config description - for (ConfigDescription item : configDescriptions) { - //if they are a non-wildcard description we just add them directly - if (!item.getName().contains("{*}")) { - //we don't want to accidentally use these properties as name expansions - //we ban them which means that the only way the name can be expanded into a map - //is if it is quoted - bannedExpansionCombos.add(item.getName()); - for (int i = 0; i < item.getName().length(); ++i) { - //add all possible segments to the banned list - if (item.getName().charAt(i) == '.') { - bannedExpansionCombos.add(item.getName().substring(0, i)); - } - } - properties.add(item.getName()); - item.setConfigValue(getConfigValue(current, item.getName())); - ordered.add(item); - } else if (!item.getName().startsWith("quarkus.log.filter")) { //special case, we use this internally and we don't want it clogging up the editor - //we need to figure out how to expand it - //this can have multiple stars - List> componentParts = new ArrayList<>(); - List accumulator = new ArrayList<>(); - //keys that were used to expand, checked against the banned list before adding - for (var i : item.getName().split("\\.")) { - if (i.equals("{*}")) { - componentParts.add(accumulator); - accumulator = new ArrayList<>(); - } else { - accumulator.add(i); - } - } - //note that accumulator is still holding the final part - //we need it later, but we don't want it in this loop - Map, Set> building = new HashMap<>(); - building.put(List.of(), new HashSet<>()); - for (List currentPart : componentParts) { - Map, Set> newBuilding = new HashMap<>(); - for (Map.Entry, Set> entry : building.entrySet()) { - List attempt = entry.getKey(); - List newBase = new ArrayList<>(attempt); - newBase.addAll(currentPart); - wildcardsToAdd.put(newBase, entry.getValue()); - Set potential = allPropertySegments.get(newBase); - if (potential != null) { - bannedExpansionCombos.add(String.join(".", newBase).replace("\"", "")); - for (String definedName : potential) { - List toAdd = new ArrayList<>(newBase); - toAdd.add(definedName); - //for expansion keys we always use unquoted values, same with banned - //so we are always comparing unquoted - Set expansionKeys = new HashSet<>(entry.getValue()); - expansionKeys.add(String.join(".", newBase) + "." + definedName); - newBuilding.put(toAdd, expansionKeys); - } - } - } - building = newBuilding; - } - //now we have our config properties - for (var entry : building.entrySet()) { - List segments = entry.getKey(); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < segments.size(); ++i) { - if (i > 0) { - sb.append("."); - } - sb.append(segments.get(i)); - } - //accumulator holds the find string - for (String s : accumulator) { - sb.append(".").append(s); - } - String expandedName = sb.toString(); - foundItems.put(expandedName, new Holder(entry.getValue(), item)); - } - } - } - for (Map.Entry e : foundItems.entrySet()) { - boolean ok = true; - for (String key : e.getValue().expansionKeys) { - if (bannedExpansionCombos.contains(key)) { - ok = false; - break; - } - } - if (!ok) { - continue; - } - String expandedName = e.getKey(); - var item = e.getValue().configDescription; - ConfigDescription newDesc = new ConfigDescription(expandedName, item.getDescription(), - item.getDefaultValue(), devServicesProperties.contains(expandedName), item.getTypeName(), - item.getAllowedValues(), - item.getConfigPhase()); - - properties.add(newDesc.getName()); - newDesc.setConfigValue(getConfigValue(current, newDesc.getName())); - ordered.add(newDesc); - } - - //now add our star properties - for (var entry : wildcardsToAdd.entrySet()) { - boolean ok = true; - for (String key : entry.getValue()) { - if (bannedExpansionCombos.contains(key)) { - ok = false; - break; - } - } - if (!ok) { - continue; - } - List segments = entry.getKey(); - StringBuilder sb = new StringBuilder(); - for (String segment : segments) { - sb.append(segment); - sb.append("."); - } - String expandedName = sb.toString(); - ConfigDescription newDesc = new ConfigDescription(expandedName, true); - - properties.add(newDesc.getName()); - newDesc.setConfigValue(getConfigValue(current, newDesc.getName())); - ordered.add(newDesc); - } - - for (ConfigSource configSource : current.getConfigSources()) { - if (configSource.getName().equals("PropertiesConfigSource[source=Build system]")) { - properties.addAll(configSource.getPropertyNames()); - } - } - - withoutExpansion(() -> { - for (String propertyName : current.getPropertyNames()) { - if (properties.contains(propertyName)) { - continue; - } - - ConfigDescription item = new ConfigDescription(propertyName, null, null, getConfigValue(current, propertyName)); - ordered.add(item); + @BuildStep(onlyIf = IsDevelopment.class) + @Record(ExecutionTime.RUNTIME_INIT) + void registerJsonRpcService( + BuildProducer jsonRPCProvidersProducer, + BuildProducer syntheticBeanProducer, + ConfigDevUiRecorder recorder) { - configDescriptions.add(item); - } + DevConsoleManager.register("config-update-property", map -> { + Map values = Collections.singletonMap(map.get("name"), map.get("value")); + ConfigEditorProcessor.updateConfig(values, false); + return null; }); - - return ordered; - } - - private ConfigValue getConfigValue(SmallRyeConfig config, String name) { - try { - return config.getConfigValue(name); - } catch (java.util.NoSuchElementException nse) { + DevConsoleManager.register("config-set-properties", value -> { + String content = value.get("content"); + ConfigEditorProcessor.setConfig(content, false); return null; - } - } + }); - private String ensureQuoted(String part) { - if (isQuoted(part)) { - return part; - } - return "\"" + part + "\""; - } + syntheticBeanProducer.produce( + SyntheticBeanBuildItem.configure(ConfigDescriptionBean.class).unremovable() + .supplier(recorder.configDescriptionBean()) + .scope(Singleton.class) + .setRuntimeInit() + .done()); - private boolean isQuoted(String part) { - return part.length() >= 2 && part.charAt(0) == '\"' && part.charAt(part.length() - 1) == '\"'; + jsonRPCProvidersProducer.produce(new JsonRPCProvidersBuildItem("devui-configuration", ConfigJsonRPCService.class)); } private static final Pattern codePattern = Pattern.compile("(\\{@code )([^}]+)(\\})"); private static final Pattern linkPattern = Pattern.compile("(\\{@link )([^}]+)(\\})"); - static String formatJavadoc(String val) { + private static String formatJavadoc(String val) { if (val == null) { return val; } @@ -331,14 +131,4 @@ static String formatJavadoc(String val) { val = val.replace("@deprecated", "
Deprecated"); return val; } - - static class Holder { - final Set expansionKeys; - final ConfigDescription configDescription; - - private Holder(Set expansionKeys, ConfigDescription configDescription) { - this.expansionKeys = expansionKeys; - this.configDescription = configDescription; - } - } } diff --git a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-configuration.js b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-configuration.js index 89e1fa0f44ea1..84670fc933c50 100644 --- a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-configuration.js +++ b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-configuration.js @@ -17,7 +17,6 @@ import { notifier } from 'notifier'; import { unsafeHTML } from 'lit/directives/unsafe-html.js'; import { gridRowDetailsRenderer } from '@vaadin/grid/lit.js'; import { observeState } from 'lit-element-state'; -import { devuiState } from 'devui-state'; import { connectionState } from 'connection-state'; import 'qui-badge'; @@ -90,6 +89,7 @@ export class QwcConfiguration extends observeState(LitElement) { static properties = { _filtered: {state: true, type: Array}, // Filter the visible configuration _visibleConfiguration: {state: true, type: Array}, // Either all or just user's configuration + _allConfiguration: {state: true, type: Array}, _values: {state: true}, _detailsOpenedItem: {state: true, type: Array}, _busy: {state: true}, @@ -105,8 +105,11 @@ export class QwcConfiguration extends observeState(LitElement) { if(this._filteredValue){ this._filteredValue = this._filteredValue.replaceAll(",", " OR "); } - this._visibleConfiguration = devuiState.allConfiguration; - this._filtered = this._visibleConfiguration; + this.jsonRpc.getAllConfiguration().then(e => { + this._allConfiguration = e.result; + this._visibleConfiguration = e.result; + this._filtered = e.result; + }) this.jsonRpc.getAllValues().then(e => { this._values = e.result; }); @@ -184,12 +187,12 @@ export class QwcConfiguration extends observeState(LitElement) { _toggleShowOnlyOwnProperties(onlyMine){ this._showOnlyOwnProperties = onlyMine; if(this._showOnlyOwnProperties){ - this._visibleConfiguration = devuiState.allConfiguration.filter((prop) => { + this._visibleConfiguration = this._allConfiguration.filter((prop) => { return (prop.configValue.sourceName && prop.configValue.sourceName.startsWith("PropertiesConfigSource[source") && prop.configValue.sourceName.endsWith("/application.properties]")); }); }else { - this._visibleConfiguration = devuiState.allConfiguration; + this._visibleConfiguration = this._allConfiguration; } return this._filterGrid(); } @@ -424,4 +427,4 @@ export class QwcConfiguration extends observeState(LitElement) { } } -customElements.define('qwc-configuration', QwcConfiguration); \ No newline at end of file +customElements.define('qwc-configuration', QwcConfiguration); diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/config/ConfigDescriptionBean.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/config/ConfigDescriptionBean.java new file mode 100644 index 0000000000000..7d687de6770bc --- /dev/null +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/config/ConfigDescriptionBean.java @@ -0,0 +1,17 @@ +package io.quarkus.devui.runtime.config; + +import java.util.List; + +import io.quarkus.vertx.http.runtime.devmode.ConfigDescription; + +public class ConfigDescriptionBean { + private List allConfig; + + public ConfigDescriptionBean(final List allConfig) { + this.allConfig = allConfig; + } + + public List getAllConfig() { + return allConfig; + } +} diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/config/ConfigDevUiRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/config/ConfigDevUiRecorder.java new file mode 100644 index 0000000000000..bc1ef42721023 --- /dev/null +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/config/ConfigDevUiRecorder.java @@ -0,0 +1,267 @@ +package io.quarkus.devui.runtime.config; + +import static io.smallrye.config.Expressions.withoutExpansion; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.config.spi.ConfigSource; + +import io.quarkus.runtime.annotations.Recorder; +import io.quarkus.vertx.http.runtime.devmode.ConfigDescription; +import io.smallrye.config.ConfigValue; +import io.smallrye.config.SmallRyeConfig; + +@Recorder +public class ConfigDevUiRecorder { + private static final String QUOTED_DOT = "\".\""; + private static final String QUOTED_DOT_KEY = "$$QUOTED_DOT$$"; + + private static List configDescriptions; + private static Set devServicesConfig; + + public void registerConfigs(final List configDescriptions, final Set devServicesConfig) { + ConfigDevUiRecorder.configDescriptions = configDescriptions; + ConfigDevUiRecorder.devServicesConfig = devServicesConfig; + } + + public Supplier configDescriptionBean() { + return new Supplier<>() { + @Override + public ConfigDescriptionBean get() { + return new ConfigDescriptionBean(calculate(configDescriptions, devServicesConfig)); + } + }; + } + + private List calculate(List cd, Set devServicesProperties) { + List configDescriptions = new ArrayList<>(cd); + + List ordered = new ArrayList<>(); + List properties = new ArrayList<>(); + SmallRyeConfig current = (SmallRyeConfig) ConfigProvider.getConfig(); + + Map, Set> allPropertySegments = new HashMap<>(); + Set propertyNames = new HashSet<>(); + current.getPropertyNames().forEach(propertyNames::add); + for (String propertyName : propertyNames) { + propertyName = propertyName.replace(QUOTED_DOT, QUOTED_DOT_KEY); // Make sure dots can be quoted + String[] parts = propertyName.split("\\."); + + List accumulate = new ArrayList<>(); + //we never want to add the full string + //hence -1 + for (int i = 0; i < parts.length - 1; ++i) { + if (parts[i].isEmpty()) { + //this can't map to a quarkus prop as it has an empty segment + //so skip + break; + } + // If there was a quoted dot, put that back + if (parts[i].contains(QUOTED_DOT_KEY)) { + parts[i] = parts[i].replaceAll(QUOTED_DOT_KEY, QUOTED_DOT); + } + + accumulate.add(parts[i]); + //if there is both a quoted and unquoted version we only want to apply the quoted version + //and remove the unquoted one + Set potentialSegmentSet = allPropertySegments.computeIfAbsent(List.copyOf(accumulate), + (k) -> new HashSet<>()); + if (isQuoted(parts[i + 1])) { + potentialSegmentSet.add(parts[i + 1]); + potentialSegmentSet.remove(parts[i + 1].substring(1, parts[i + 1].length() - 1)); + } else { + if (!potentialSegmentSet.contains(ensureQuoted(parts[i + 1]))) { + potentialSegmentSet.add(parts[i + 1]); + } + } + + } + } + + Map, Set> wildcardsToAdd = new HashMap<>(); + Map foundItems = new HashMap<>(); + Set bannedExpansionCombos = new HashSet<>(); + //we iterate over every config description + for (ConfigDescription item : configDescriptions) { + //if they are a non-wildcard description we just add them directly + if (!item.getName().contains("{*}")) { + //we don't want to accidentally use these properties as name expansions + //we ban them which means that the only way the name can be expanded into a map + //is if it is quoted + bannedExpansionCombos.add(item.getName()); + for (int i = 0; i < item.getName().length(); ++i) { + //add all possible segments to the banned list + if (item.getName().charAt(i) == '.') { + bannedExpansionCombos.add(item.getName().substring(0, i)); + } + } + properties.add(item.getName()); + item.setConfigValue(getConfigValue(current, item.getName())); + ordered.add(item); + } else if (!item.getName().startsWith("quarkus.log.filter")) { //special case, we use this internally and we don't want it clogging up the editor + //we need to figure out how to expand it + //this can have multiple stars + List> componentParts = new ArrayList<>(); + List accumulator = new ArrayList<>(); + //keys that were used to expand, checked against the banned list before adding + for (var i : item.getName().split("\\.")) { + if (i.equals("{*}")) { + componentParts.add(accumulator); + accumulator = new ArrayList<>(); + } else { + accumulator.add(i); + } + } + //note that accumulator is still holding the final part + //we need it later, but we don't want it in this loop + Map, Set> building = new HashMap<>(); + building.put(List.of(), new HashSet<>()); + for (List currentPart : componentParts) { + Map, Set> newBuilding = new HashMap<>(); + for (Map.Entry, Set> entry : building.entrySet()) { + List attempt = entry.getKey(); + List newBase = new ArrayList<>(attempt); + newBase.addAll(currentPart); + wildcardsToAdd.put(newBase, entry.getValue()); + Set potential = allPropertySegments.get(newBase); + if (potential != null) { + bannedExpansionCombos.add(String.join(".", newBase).replace("\"", "")); + for (String definedName : potential) { + List toAdd = new ArrayList<>(newBase); + toAdd.add(definedName); + //for expansion keys we always use unquoted values, same with banned + //so we are always comparing unquoted + Set expansionKeys = new HashSet<>(entry.getValue()); + expansionKeys.add(String.join(".", newBase) + "." + definedName); + newBuilding.put(toAdd, expansionKeys); + } + } + } + building = newBuilding; + } + //now we have our config properties + for (var entry : building.entrySet()) { + List segments = entry.getKey(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < segments.size(); ++i) { + if (i > 0) { + sb.append("."); + } + sb.append(segments.get(i)); + } + //accumulator holds the find string + for (String s : accumulator) { + sb.append(".").append(s); + } + String expandedName = sb.toString(); + foundItems.put(expandedName, new Holder(entry.getValue(), item)); + } + } + } + for (Map.Entry e : foundItems.entrySet()) { + boolean ok = true; + for (String key : e.getValue().expansionKeys) { + if (bannedExpansionCombos.contains(key)) { + ok = false; + break; + } + } + if (!ok) { + continue; + } + String expandedName = e.getKey(); + var item = e.getValue().configDescription; + ConfigDescription newDesc = new ConfigDescription(expandedName, item.getDescription(), + item.getDefaultValue(), devServicesProperties.contains(expandedName), item.getTypeName(), + item.getAllowedValues(), + item.getConfigPhase()); + + properties.add(newDesc.getName()); + newDesc.setConfigValue(getConfigValue(current, newDesc.getName())); + ordered.add(newDesc); + } + + //now add our star properties + for (var entry : wildcardsToAdd.entrySet()) { + boolean ok = true; + for (String key : entry.getValue()) { + if (bannedExpansionCombos.contains(key)) { + ok = false; + break; + } + } + if (!ok) { + continue; + } + List segments = entry.getKey(); + StringBuilder sb = new StringBuilder(); + for (String segment : segments) { + sb.append(segment); + sb.append("."); + } + String expandedName = sb.toString(); + ConfigDescription newDesc = new ConfigDescription(expandedName, true); + + properties.add(newDesc.getName()); + newDesc.setConfigValue(getConfigValue(current, newDesc.getName())); + ordered.add(newDesc); + } + + for (ConfigSource configSource : current.getConfigSources()) { + if (configSource.getName().equals("PropertiesConfigSource[source=Build system]")) { + properties.addAll(configSource.getPropertyNames()); + } + } + + withoutExpansion(() -> { + for (String propertyName : current.getPropertyNames()) { + if (properties.contains(propertyName)) { + continue; + } + + ConfigDescription item = new ConfigDescription(propertyName, null, null, getConfigValue(current, propertyName)); + ordered.add(item); + + configDescriptions.add(item); + } + }); + + return ordered; + } + + private ConfigValue getConfigValue(SmallRyeConfig config, String name) { + try { + return config.getConfigValue(name); + } catch (java.util.NoSuchElementException nse) { + return null; + } + } + + private String ensureQuoted(String part) { + if (isQuoted(part)) { + return part; + } + return "\"" + part + "\""; + } + + private boolean isQuoted(String part) { + return part.length() >= 2 && part.charAt(0) == '\"' && part.charAt(part.length() - 1) == '\"'; + } + + static class Holder { + final Set expansionKeys; + final ConfigDescription configDescription; + + private Holder(Set expansionKeys, ConfigDescription configDescription) { + this.expansionKeys = expansionKeys; + this.configDescription = configDescription; + } + } +} diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/config/ConfigJsonRPCService.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/config/ConfigJsonRPCService.java index a6eb1eec696b6..289565ef5b588 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/config/ConfigJsonRPCService.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/config/ConfigJsonRPCService.java @@ -11,27 +11,30 @@ import java.util.stream.Collectors; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; -import org.eclipse.microprofile.config.Config; -import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.logging.Logger; import io.quarkus.dev.console.DevConsoleManager; import io.quarkus.devui.runtime.comms.JsonRpcMessage; import io.quarkus.devui.runtime.comms.MessageType; +import io.quarkus.vertx.http.runtime.devmode.ConfigDescription; +import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; @ApplicationScoped public class ConfigJsonRPCService { private static final Logger LOG = Logger.getLogger(ConfigJsonRPCService.class.getName()); + @Inject + ConfigDescriptionBean configDescriptionBean; + public JsonRpcMessage updateProperty(String name, String value) { DevConsoleManager.invoke("config-update-property", Map.of("name", name, "value", value)); return new JsonRpcMessage(true, MessageType.HotReload); } public boolean updateProperties(String content, String type) { - if (type.equalsIgnoreCase("properties")) { Properties p = new Properties(); try (StringReader sr = new StringReader(content)) { @@ -47,11 +50,14 @@ public boolean updateProperties(String content, String type) { return false; } + public JsonArray getAllConfiguration() { + return new JsonArray(configDescriptionBean.getAllConfig()); + } + public JsonObject getAllValues() { JsonObject values = new JsonObject(); - Config config = ConfigProvider.getConfig(); - for (String name : config.getPropertyNames()) { - values.put(name, config.getConfigValue(name).getValue()); + for (ConfigDescription configDescription : configDescriptionBean.getAllConfig()) { + values.put(configDescription.getName(), configDescription.getConfigValue().getValue()); } return values; }