diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/Picocli.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/Picocli.java index 9af30afbe4ac..3e547da9f3b5 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/Picocli.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/Picocli.java @@ -379,7 +379,7 @@ public void validateConfig(List cliArgs, AbstractCommand abstractCommand Optional.ofNullable(PropertyMappers.getRuntimeMappers().get(category)).ifPresent(mappers::addAll); Optional.ofNullable(PropertyMappers.getBuildTimeMappers().get(category)).ifPresent(mappers::addAll); for (PropertyMapper mapper : mappers) { - Configuration.getKcConfigValues(mapper).forEach(configValue -> { + mapper.getKcConfigValues().forEach(configValue -> { String configValueStr = configValue.getValue(); // don't consider missing or anything below standard env properties diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/ConfigArgsConfigSource.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/ConfigArgsConfigSource.java index 2978dce0efd2..8832a69eab7c 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/ConfigArgsConfigSource.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/ConfigArgsConfigSource.java @@ -129,15 +129,15 @@ public void accept(String key, String value) { PropertyMapper mapper = PropertyMappers.getMapper(key); if (mapper != null) { - String to = mapper.getTo(); + mapper = mapper.forKey(key); - String mappedKey = mapper.getMappedKey(key).orElse(null); + String to = mapper.getTo(); if (to != null) { - properties.put(mapper.getTo(mappedKey), value); + properties.put(mapper.getTo(), value); } - properties.put(mapper.getFrom(mappedKey), value); + properties.put(mapper.getFrom(), value); } } }, ignored -> {}); diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/Configuration.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/Configuration.java index db14df50273b..d196111ee500 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/Configuration.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/Configuration.java @@ -19,7 +19,6 @@ import static org.keycloak.quarkus.runtime.cli.Picocli.ARG_PREFIX; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Properties; @@ -113,19 +112,6 @@ public static ConfigValue getKcConfigValue(String propertyName) { return getConfigValue(NS_KEYCLOAK_PREFIX.concat(propertyName)); } - /** - * Get all Keycloak config values for the mapper. A multivalued config option is a config option that - * has a wildcard in its name, e.g. log-level-. - * - * @return a map of config values where the key is the resolved wildcard (e.g. category) and the value is the config value - */ - public static List getKcConfigValues(PropertyMapper mapper) { - if (mapper.hasWildcard()) { - return mapper.getWildcardKeys().stream().map(v -> getConfigValue(mapper.getFrom(v))).toList(); - } - return List.of(Configuration.getConfigValue(mapper.getFrom())); - } - public static Optional getOptionalValue(String name) { return getConfig().getOptionalValue(name, String.class); } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/KcEnvConfigSource.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/KcEnvConfigSource.java index 844d3dfc31ce..4aa0799931b6 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/KcEnvConfigSource.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/KcEnvConfigSource.java @@ -50,13 +50,15 @@ private static Map buildProperties() { PropertyMapper mapper = PropertyMappers.getMapper(key); if (mapper != null) { + mapper = mapper.forEnvKey(key); + String to = mapper.getTo(); if (to != null) { properties.put(to, value); } - properties.put(mapper.getFrom(mapper.getMappedEnvVarKey(key).orElse(null)), value); + properties.put(mapper.getFrom(), value); } else { // most probably an SPI but could be also something else diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/PropertyMappingInterceptor.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/PropertyMappingInterceptor.java index b100ac80a790..3d8c983c257d 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/PropertyMappingInterceptor.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/PropertyMappingInterceptor.java @@ -27,6 +27,7 @@ import org.keycloak.quarkus.runtime.Environment; import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper; import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers; +import org.keycloak.quarkus.runtime.configuration.mappers.WildcardPropertyMapper; import java.util.Iterator; import java.util.List; @@ -87,7 +88,7 @@ public Iterator iterateNames(ConfigSourceInterceptorContext context) { disableAdditionalNames.set(true); try { mappedWildcardNames = PropertyMappers.getWildcardMappers().stream() - .map(PropertyMapper::getToWithWildcards) + .map(WildcardPropertyMapper::getToWithWildcards) .flatMap(Set::stream) .toList(); } finally { diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java index 4ec5a35c9baf..dba3424a4838 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java @@ -112,7 +112,7 @@ public static PropertyMapper[] getMappers() { .validator(LoggingPropertyMappers::validateCategoryLogLevel) .wildcardKeysTransformer(LoggingPropertyMappers::getConfiguredLogCategories) .transformer((v,c) -> toLevel(v).getName()) - .mapFrom(LoggingOptions.LOG_LEVEL, LoggingPropertyMappers::resolveCategoryLogLevelFromParentLogLevelOption) // a fallback to log-level + .wildcardMapFrom(LoggingOptions.LOG_LEVEL, LoggingPropertyMappers::resolveCategoryLogLevelFromParentLogLevelOption) // a fallback to log-level .paramLabel("level") .build(), // Syslog diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java index 562f5fb1d1b5..40605b8c122f 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java @@ -19,7 +19,6 @@ import static java.util.Optional.ofNullable; import static org.keycloak.config.Option.WILDCARD_PLACEHOLDER_PATTERN; import static org.keycloak.quarkus.runtime.Environment.isRebuild; -import static org.keycloak.quarkus.runtime.cli.Picocli.ARG_PREFIX; import static org.keycloak.quarkus.runtime.configuration.Configuration.OPTION_PART_SEPARATOR; import static org.keycloak.quarkus.runtime.configuration.Configuration.OPTION_PART_SEPARATOR_CHAR; import static org.keycloak.quarkus.runtime.configuration.Configuration.toCliFormat; @@ -35,11 +34,7 @@ import java.util.function.BooleanSupplier; import java.util.function.Consumer; import java.util.function.Function; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; -import java.util.stream.StreamSupport; import io.smallrye.config.ConfigSourceInterceptorContext; import io.smallrye.config.ConfigValue; @@ -61,13 +56,13 @@ public class PropertyMapper { - private final Option option; + protected final Option option; private final String to; private BooleanSupplier enabled; private String enabledWhen; - private final ValueMapper mapper; + private final BiFunction mapper; private final String mapFrom; - private final ValueMapper parentMapper; + private final BiFunction parentMapper; private final boolean mask; private final String paramLabel; private final String envVarFormat; @@ -76,19 +71,21 @@ public class PropertyMapper { private final String description; private final BooleanSupplier required; private final String requiredWhen; - private Matcher fromWildcardMatcher; - private Pattern fromWildcardPattern; - private Pattern envVarNameWildcardPattern; - private Matcher toWildcardMatcher; - private Pattern toWildcardPattern; - private Function, Set> wildcardKeysTransformer; + private final String from; + + PropertyMapper(PropertyMapper mapper, String from, String to, BiFunction parentMapper) { + this(mapper.option, to, mapper.enabled, mapper.enabledWhen, mapper.mapper, mapper.mapFrom, parentMapper, + mapper.paramLabel, mapper.mask, mapper.validator, mapper.description, mapper.required, + mapper.requiredWhen, from); + } PropertyMapper(Option option, String to, BooleanSupplier enabled, String enabledWhen, - ValueMapper mapper, - String mapFrom, ValueMapper parentMapper, + BiFunction mapper, + String mapFrom, BiFunction parentMapper, String paramLabel, boolean mask, BiConsumer, ConfigValue> validator, - String description, BooleanSupplier required, String requiredWhen, Function, Set> wildcardKeysTransformer) { + String description, BooleanSupplier required, String requiredWhen, String from) { this.option = option; + this.from = from == null ? MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + this.option.getKey() : from; this.to = to == null ? getFrom() : to; this.enabled = enabled; this.enabledWhen = enabledWhen; @@ -103,31 +100,6 @@ public class PropertyMapper { this.validator = validator; this.description = description; this.parentMapper = parentMapper; - - - // The wildcard pattern (e.g. log-level-) is matching only a-z, 0-0 and dots. For env vars, dots are replaced by underscores. - if (option.getKey() != null) { - fromWildcardMatcher = WILDCARD_PLACEHOLDER_PATTERN.matcher(option.getKey()); - if (fromWildcardMatcher.find()) { - // Includes handling for both "--" prefix for CLI options and "kc." prefix - this.fromWildcardPattern = Pattern.compile("(?:" + ARG_PREFIX + "|kc\\.)" + fromWildcardMatcher.replaceFirst("([\\\\\\\\.a-zA-Z0-9]+)")); - - // Not using toEnvVarFormat because it would process the whole string incl the <...> wildcard. - Matcher envVarMatcher = WILDCARD_PLACEHOLDER_PATTERN.matcher(option.getKey().toUpperCase().replace("-", "_")); - this.envVarNameWildcardPattern = Pattern.compile("KC_" + envVarMatcher.replaceFirst("([_A-Z0-9]+)")); - - if (to != null) { - toWildcardMatcher = WILDCARD_PLACEHOLDER_PATTERN.matcher(to); - if (!toWildcardMatcher.find()) { - throw new IllegalArgumentException("Attempted to map a wildcard option to a non-wildcard option"); - } - - this.toWildcardPattern = Pattern.compile(toWildcardMatcher.replaceFirst("([\\\\\\\\.a-zA-Z0-9]+)")); - } - } - - this.wildcardKeysTransformer = wildcardKeysTransformer; - } } ConfigValue getConfigValue(ConfigSourceInterceptorContext context) { @@ -135,7 +107,7 @@ ConfigValue getConfigValue(ConfigSourceInterceptorContext context) { } ConfigValue getConfigValue(String name, ConfigSourceInterceptorContext context) { - String from = getFrom(getMappedKey(name).orElse(null)); + String from = getFrom(); if (to != null && to.endsWith(OPTION_PART_SEPARATOR)) { // in case mapping is based on prefixes instead of full property names @@ -174,36 +146,6 @@ ConfigValue getConfigValue(String name, ConfigSourceInterceptorContext context) return context.proceed(name); } - public Set getWildcardKeys() { - if (!hasWildcard()) { - return Set.of(); - } - - // this is not optimal - // TODO find an efficient way to get all values that match the wildcard - Set values = StreamSupport.stream(Configuration.getPropertyNames().spliterator(), false) - .map(n -> getMappedKey(n, true, false, false)) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toSet()); - - if (wildcardKeysTransformer != null) { - return wildcardKeysTransformer.apply(values); - } - - return values; - } - - public Set getToWithWildcards() { - if (toWildcardMatcher == null) { - return Set.of(); - } - - return getWildcardKeys().stream() - .map(v -> toWildcardMatcher.replaceFirst(v)) - .collect(Collectors.toSet()); - } - public Option getOption() { return this.option; } @@ -240,16 +182,8 @@ public Class getType() { return this.option.getType(); } - public String getFrom(String wildcardKey) { - String from = this.option.getKey(); - if (hasWildcard() && wildcardKey != null) { - from = fromWildcardMatcher.replaceFirst(wildcardKey); - } - return MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + from; - } - public String getFrom() { - return getFrom(null); + return from; } public String getDescription() { @@ -286,16 +220,8 @@ public boolean isRunTime() { return !this.option.isBuildTime(); } - public String getTo(String wildcardKey) { - String to = this.to; - if (hasWildcard() && wildcardKey != null) { - to = toWildcardMatcher.replaceFirst(wildcardKey); - } - return to; - } - public String getTo() { - return getTo(null); + return to; } public String getParamLabel() { @@ -323,64 +249,7 @@ public Optional getDeprecatedMetadata() { * The placeholder must be denoted by the '<' and '>' characters. */ public boolean hasWildcard() { - return fromWildcardPattern != null; - } - - /** - * Checks if the given option name matches the wildcard pattern of this option. - * E.g. check if "log-level-io.quarkus" matches the wildcard pattern "log-level-". - */ - public boolean matchesWildcardOptionName(String name) { - if (!hasWildcard()) { - return false; - } - return fromWildcardPattern.matcher(name).matches() || envVarNameWildcardPattern.matcher(name).matches() - || (toWildcardPattern != null && toWildcardPattern.matcher(name).matches()); - } - - /** - * Returns a mapped key for the given option name if a relevant mapping is available, or empty otherwise. - * Currently, it only attempts to extract the wildcard key from the given option name. - * E.g. for the option "log-level-" and the option name "log-level-io.quarkus", - * the wildcard value would be "io.quarkus". - */ - private Optional getMappedKey(String originalKey, boolean tryFrom, boolean tryEnvVar, boolean tryTo) { - if (!hasWildcard()) { - return Optional.empty(); - } - - if (tryFrom) { - Matcher matcher = fromWildcardPattern.matcher(originalKey); - if (matcher.matches()) { - return Optional.of(matcher.group(1)); - } - } - - if (tryEnvVar) { - Matcher matcher = envVarNameWildcardPattern.matcher(originalKey); - if (matcher.matches()) { - String value = matcher.group(1); - value = value.toLowerCase().replace("_", "."); // we opiniotatedly convert env var names to CLI format with dots - return Optional.of(value); - } - } - - if (tryTo && toWildcardPattern != null) { - Matcher matcher = toWildcardPattern.matcher(originalKey); - if (matcher.matches()) { - return Optional.of(matcher.group(1)); - } - } - - return Optional.empty(); - } - - public Optional getMappedKey(String originalKey) { - return getMappedKey(originalKey, true, false, true); - } - - public Optional getMappedEnvVarKey(String originalKey) { - return getMappedKey(originalKey, false, true, false); + return false; } private ConfigValue transformValue(String name, ConfigValue configValue, ConfigSourceInterceptorContext context, boolean parentValue) { @@ -390,8 +259,7 @@ private ConfigValue transformValue(String name, ConfigValue configValue, ConfigS boolean mapped = false; var theMapper = parentValue ? this.parentMapper : this.mapper; if (theMapper != null && (!name.equals(getFrom()) || parentValue)) { - String nameForMapper = getMappedKey(name).orElse(name); - mappedValue = theMapper.map(nameForMapper, value, context); + mappedValue = theMapper.apply(value, context); mapped = true; } @@ -459,9 +327,9 @@ public static class Builder { private final Option option; private String to; - private ValueMapper mapper; + private BiFunction mapper; private String mapFrom = null; - private ValueMapper parentMapper; + private BiFunction parentMapper; private boolean isMasked = false; private BooleanSupplier isEnabled = () -> true; private String enabledWhen = ""; @@ -471,6 +339,7 @@ public static class Builder { private BooleanSupplier isRequired = () -> false; private String requiredWhen = ""; private Function, Set> wildcardKeysTransformer; + private ValueMapper wildcardMapFrom; public Builder(Option option) { this.option = option; @@ -485,19 +354,15 @@ public Builder to(String to) { /** * NOTE: This transformer will not apply to the mapFrom value. When using * {@link #mapFrom} you generally need a transformer specifically for the parent - * value, see {@link #mapFrom(Option, ValueMapper)} + * value, see {@link #mapFrom(Option, BiFunction)} *

* The value passed into the transformer may be null if the property has no value set, and no default */ - public Builder transformer(ValueMapper mapper) { + public Builder transformer(BiFunction mapper) { this.mapper = mapper; return this; } - public Builder transformer(BiFunction mapper) { - return transformer((name, value, context) -> mapper.apply(value, context)); - } - public Builder paramLabel(String label) { this.paramLabel = label; return this; @@ -508,16 +373,12 @@ public Builder mapFrom(Option mapFrom) { return this; } - public Builder mapFrom(Option mapFrom, ValueMapper parentMapper) { + public Builder mapFrom(Option mapFrom, BiFunction parentMapper) { this.mapFrom = mapFrom.getKey(); this.parentMapper = parentMapper; return this; } - public Builder mapFrom(Option mapFrom, BiFunction parentMapper) { - return mapFrom(mapFrom, (name, value, context) -> parentMapper.apply(value, context)); - } - public Builder isMasked(boolean isMasked) { this.isMasked = isMasked; return this; @@ -608,11 +469,25 @@ public Builder wildcardKeysTransformer(Function, Set> wil return this; } + public Builder wildcardMapFrom(Option mapFrom, ValueMapper function) { + this.mapFrom = mapFrom.getKey(); + this.wildcardMapFrom = function; + return this; + } + public PropertyMapper build() { if (paramLabel == null && Boolean.class.equals(option.getType())) { paramLabel = Boolean.TRUE + "|" + Boolean.FALSE; } - return new PropertyMapper<>(option, to, isEnabled, enabledWhen, mapper, mapFrom, parentMapper, paramLabel, isMasked, validator, description, isRequired, requiredWhen, wildcardKeysTransformer); + // The wildcard pattern (e.g. log-level-) is matching only a-z, 0-0 and dots. For env vars, dots are replaced by underscores. + var fromWildcardMatcher = WILDCARD_PLACEHOLDER_PATTERN.matcher(option.getKey()); + if (fromWildcardMatcher.find()) { + return new WildcardPropertyMapper<>(option, to, isEnabled, enabledWhen, mapper, mapFrom, parentMapper, paramLabel, isMasked, validator, description, isRequired, requiredWhen, fromWildcardMatcher, wildcardKeysTransformer, wildcardMapFrom); + } + if (wildcardKeysTransformer != null || wildcardMapFrom != null) { + throw new AssertionError("wildcardKeysTransformer not expected with non-wildcard mapper"); + } + return new PropertyMapper<>(option, to, isEnabled, enabledWhen, mapper, mapFrom, parentMapper, paramLabel, isMasked, validator, description, isRequired, requiredWhen, null); } } @@ -694,4 +569,22 @@ String getOptionAndSourceMessage(ConfigValue configValue) { KeycloakConfigSourceProvider.getConfigSourceDisplayName(configValue.getConfigSourceName())); } + /** + * Get all Keycloak config values for the mapper. A multivalued config option is a config option that + * has a wildcard in its name, e.g. log-level-. + * + * @return a list of config values where the key is the resolved wildcard (e.g. category) and the value is the config value + */ + public List getKcConfigValues() { + return List.of(Configuration.getConfigValue(getFrom())); + } + + public PropertyMapper forEnvKey(String key) { + return this; + } + + public PropertyMapper forKey(String key) { + return this; + } + } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMappers.java index 43236afd11e5..56c4885a3e4e 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMappers.java @@ -84,7 +84,7 @@ public static ConfigValue getValue(ConfigSourceInterceptorContext context, Strin if (mapper == null) { return context.proceed(name); } - return mapper.getConfigValue(name, context); + return mapper.forKey(name).getConfigValue(name, context); } public static boolean isSpiBuildTimeProperty(String name) { @@ -179,7 +179,7 @@ public static Set> getMappers() { return MAPPERS.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()); } - public static Set> getWildcardMappers() { + public static Set> getWildcardMappers() { return MAPPERS.getWildcardMappers(); } @@ -225,7 +225,7 @@ private static class MappersConfig extends MultivaluedHashMap> disabledBuildTimeMappers = new HashMap<>(); private final Map> disabledRuntimeMappers = new HashMap<>(); - private static final Set> wildcardMappers = new HashSet<>(); + private final Set> wildcardMappers = new HashSet<>(); public void addAll(PropertyMapper[] mappers) { for (PropertyMapper mapper : mappers) { @@ -245,7 +245,7 @@ private static void addMapperByStage(PropertyMapper mapper, Map mapper) { if (mapper.hasWildcard()) { - wildcardMappers.add(mapper); + wildcardMappers.add((WildcardPropertyMapper)mapper); } handleMapper(mapper, this::add); } @@ -266,7 +266,7 @@ private void remove(String key, PropertyMapper mapper) { public List> get(Object key) { // First check if the requested option matches any wildcard mappers String strKey = (String) key; - List> ret = wildcardMappers.stream() + List ret = wildcardMappers.stream() .filter(m -> m.matchesWildcardOptionName(strKey)) .toList(); if (!ret.isEmpty()) { @@ -282,7 +282,7 @@ public List> remove(Object mapper) { return super.remove(mapper); } - public Set> getWildcardMappers() { + public Set> getWildcardMappers() { return Collections.unmodifiableSet(wildcardMappers); } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/WildcardPropertyMapper.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/WildcardPropertyMapper.java index 14e62eb859ec..34d2e77e8cfa 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/WildcardPropertyMapper.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/WildcardPropertyMapper.java @@ -37,7 +37,7 @@ public WildcardPropertyMapper(Option option, String to, BooleanSupplier enabl String mapFrom, BiFunction parentMapper, String paramLabel, boolean mask, BiConsumer, ConfigValue> validator, String description, BooleanSupplier required, String requiredWhen, Matcher fromWildcardMatcher, Function, Set> wildcardKeysTransformer, ValueMapper wildcardMapFrom) { - super(option, to, enabled, enabledWhen, mapper, mapFrom, parentMapper, paramLabel, mask, validator, description, required, requiredWhen); + super(option, to, enabled, enabledWhen, mapper, mapFrom, parentMapper, paramLabel, mask, validator, description, required, requiredWhen, null); this.wildcardMapFrom = wildcardMapFrom; this.fromWildcardMatcher = fromWildcardMatcher; // Includes handling for both "--" prefix for CLI options and "kc." prefix @@ -139,6 +139,9 @@ public boolean matchesWildcardOptionName(String name) { @Override public PropertyMapper forEnvKey(String key) { Matcher matcher = envVarNameWildcardPattern.matcher(key); + if (!matcher.matches()) { + throw new IllegalStateException(); + } String value = matcher.group(1); final String wildcardValue = value.toLowerCase().replace("_", "."); // we opiniotatedly convert env var names to CLI format with dots return forWildcardValue(wildcardValue); @@ -147,7 +150,7 @@ public PropertyMapper forEnvKey(String key) { private PropertyMapper forWildcardValue(final String wildcardValue) { String to = getTo(wildcardValue); String from = getFrom(wildcardValue); - return new PropertyMapper(this, to, from, wildcardMapFrom == null ? null : (v, context) -> wildcardMapFrom.map(wildcardValue, v, context)); + return new PropertyMapper(this, from, to, wildcardMapFrom == null ? null : (v, context) -> wildcardMapFrom.map(wildcardValue, v, context)); } @Override diff --git a/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/cli/PicocliTest.java b/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/cli/PicocliTest.java index 0d21829e438c..4fdc6edf4565 100644 --- a/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/cli/PicocliTest.java +++ b/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/cli/PicocliTest.java @@ -397,7 +397,18 @@ public void wrongLevelForCategory() { public void wildcardLevelForCategory() { NonRunningPicocli nonRunningPicocli = pseudoLaunch("start-dev", "--log-level-org.keycloak=warn"); assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode); - assertTrue(nonRunningPicocli.getErrString().contains("Invalid log level: wrong. Possible values are: warn, trace, debug, error, fatal, info.")); + var value = nonRunningPicocli.config.getConfigValue("quarkus.log.category.\"org.keycloak\".level"); + assertEquals("quarkus.log.category.\"org.keycloak\".level", value.getName()); + assertEquals("WARN", value.getValue()); + } + + @Test + public void wildcardLevelFromParent() { + NonRunningPicocli nonRunningPicocli = pseudoLaunch("start-dev", "--log-level=org.keycloak:warn"); + assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode); + var value = nonRunningPicocli.config.getConfigValue("quarkus.log.category.\"org.keycloak\".level"); + assertEquals("quarkus.log.category.\"org.keycloak\".level", value.getName()); + assertEquals("WARN", value.getValue()); } }