From 449a814acc35078f46fee35a7f2a1d1e0683e095 Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Mon, 8 Jan 2024 17:51:47 +0000 Subject: [PATCH] Support multiple profile names in dynamic property names --- .../ProfileConfigSourceInterceptor.java | 45 ++++++++++++- .../config/SmallRyeConfigBuilder.java | 4 ++ .../ProfileConfigSourceInterceptorTest.java | 67 +++++++++++++------ 3 files changed, 93 insertions(+), 23 deletions(-) diff --git a/implementation/src/main/java/io/smallrye/config/ProfileConfigSourceInterceptor.java b/implementation/src/main/java/io/smallrye/config/ProfileConfigSourceInterceptor.java index 4ce829a09..280d35a42 100644 --- a/implementation/src/main/java/io/smallrye/config/ProfileConfigSourceInterceptor.java +++ b/implementation/src/main/java/io/smallrye/config/ProfileConfigSourceInterceptor.java @@ -84,9 +84,48 @@ public String[] getProfiles() { public String normalizeName(final String name) { if (name.length() > 0 && name.charAt(0) == '%') { - for (String profile : profiles) { - if (name.startsWith(profile + ".", 1)) { - return name.substring(profile.length() + 2); + int profilesEnd = name.indexOf('.', 1); + int multipleSplit = -1; + for (int i = 1; i < profilesEnd; i++) { + if (name.charAt(i) == ',') { + multipleSplit = i; + break; + } + } + + if (multipleSplit == -1) { + // Single profile property name (%profile.foo.bar) + for (String profile : profiles) { + if (profilesEnd == profile.length() + 1 && name.regionMatches(1, profile, 0, profile.length())) { + return name.substring(profilesEnd + 1); + } + } + } else { + // Multiple profile property name (%profile,another.foo.bar) + int nextSplit = multipleSplit; + int toOffset = 1; + while (nextSplit != -1) { + for (String profile : profiles) { + char expectedEnd = name.charAt(toOffset + profile.length()); + if ((expectedEnd == '.' || expectedEnd == ',') && + name.regionMatches(toOffset, profile, 0, profile.length())) { + return name.substring(profilesEnd + 1); + } + } + + toOffset = nextSplit + 1; + nextSplit = -1; + + for (int i = toOffset; i < profilesEnd; i++) { + if (name.charAt(i) == ',') { + nextSplit = i; + break; + } + } + + if (toOffset < profilesEnd && nextSplit == -1) { + nextSplit = profilesEnd; + } } } } diff --git a/implementation/src/main/java/io/smallrye/config/SmallRyeConfigBuilder.java b/implementation/src/main/java/io/smallrye/config/SmallRyeConfigBuilder.java index f95f4ab80..ad368b137 100644 --- a/implementation/src/main/java/io/smallrye/config/SmallRyeConfigBuilder.java +++ b/implementation/src/main/java/io/smallrye/config/SmallRyeConfigBuilder.java @@ -622,6 +622,10 @@ public List getInterceptors() { return interceptors; } + public List getProfiles() { + return profiles; + } + public ConfigValidator getValidator() { if (isAddDiscoveredValidator()) { this.validator = discoverValidator(); diff --git a/implementation/src/test/java/io/smallrye/config/ProfileConfigSourceInterceptorTest.java b/implementation/src/test/java/io/smallrye/config/ProfileConfigSourceInterceptorTest.java index 6adf136f4..336f6af94 100644 --- a/implementation/src/test/java/io/smallrye/config/ProfileConfigSourceInterceptorTest.java +++ b/implementation/src/test/java/io/smallrye/config/ProfileConfigSourceInterceptorTest.java @@ -21,6 +21,7 @@ import java.util.NoSuchElementException; import java.util.OptionalInt; import java.util.Set; +import java.util.stream.StreamSupport; import org.eclipse.microprofile.config.Config; import org.junit.jupiter.api.Test; @@ -30,7 +31,7 @@ class ProfileConfigSourceInterceptorTest { @Test void profile() { - final Config config = buildConfig("my.prop", "1", "%prof.my.prop", "2", SMALLRYE_CONFIG_PROFILE, "prof"); + SmallRyeConfig config = buildConfig("my.prop", "1", "%prof.my.prop", "2", SMALLRYE_CONFIG_PROFILE, "prof"); assertEquals("2", config.getValue("my.prop", String.class)); @@ -41,28 +42,28 @@ void profile() { @Test void profileOnly() { - final Config config = buildConfig("%prof.my.prop", "2", SMALLRYE_CONFIG_PROFILE, "prof"); + SmallRyeConfig config = buildConfig("%prof.my.prop", "2", SMALLRYE_CONFIG_PROFILE, "prof"); assertEquals("2", config.getValue("my.prop", String.class)); } @Test void fallback() { - final Config config = buildConfig("my.prop", "1", SMALLRYE_CONFIG_PROFILE, "prof"); + SmallRyeConfig config = buildConfig("my.prop", "1", SMALLRYE_CONFIG_PROFILE, "prof"); assertEquals("1", config.getValue("my.prop", String.class)); } @Test void expressions() { - final Config config = buildConfig("my.prop", "1", "%prof.my.prop", "${my.prop}", SMALLRYE_CONFIG_PROFILE, "prof"); + SmallRyeConfig config = buildConfig("my.prop", "1", "%prof.my.prop", "${my.prop}", SMALLRYE_CONFIG_PROFILE, "prof"); assertThrows(IllegalArgumentException.class, () -> config.getValue("my.prop", String.class)); } @Test void profileExpressions() { - final Config config = buildConfig("my.prop", "1", + SmallRyeConfig config = buildConfig("my.prop", "1", "%prof.my.prop", "${%prof.my.prop.profile}", "%prof.my.prop.profile", "2", SMALLRYE_CONFIG_PROFILE, "prof"); @@ -72,7 +73,7 @@ void profileExpressions() { @Test void cannotExpand() { - final Config config = new SmallRyeConfigBuilder() + SmallRyeConfig config = new SmallRyeConfigBuilder() .addDefaultInterceptors() .withSources(config("my.prop", "${another.prop}", SMALLRYE_CONFIG_PROFILE, "prof", "config_ordinal", "1000")) .withSources(config("my.prop", "${another.prop}", "%prof.my.prop", "2", SMALLRYE_CONFIG_PROFILE, "prof")) @@ -83,8 +84,8 @@ void cannotExpand() { @Test void customConfigProfile() { - final String[] configs = { "my.prop", "1", "%prof.my.prop", "2", "config.profile", "prof" }; - final Config config = new SmallRyeConfigBuilder() + String[] configs = { "my.prop", "1", "%prof.my.prop", "2", "config.profile", "prof" }; + SmallRyeConfig config = new SmallRyeConfigBuilder() .addDefaultSources() .addDiscoveredInterceptors() .withSources(config(configs)) @@ -95,8 +96,8 @@ void customConfigProfile() { @Test void noConfigProfile() { - final String[] configs = { "my.prop", "1", "%prof.my.prop", "2" }; - final Config config = new SmallRyeConfigBuilder() + String[] configs = { "my.prop", "1", "%prof.my.prop", "2" }; + SmallRyeConfig config = new SmallRyeConfigBuilder() .addDefaultInterceptors() .withSources(config(configs)) .build(); @@ -106,7 +107,7 @@ void noConfigProfile() { @Test void priorityProfile() { - final Config config = new SmallRyeConfigBuilder() + SmallRyeConfig config = new SmallRyeConfigBuilder() .addDefaultInterceptors() .withProfile("prof") .withSources( @@ -130,7 +131,7 @@ void priorityProfile() { @Test void priorityOverrideProfile() { - final Config config = new SmallRyeConfigBuilder() + SmallRyeConfig config = new SmallRyeConfigBuilder() .addDefaultInterceptors() .withSources( new MapBackedConfigSource("higher", new HashMap() { @@ -153,7 +154,7 @@ void priorityOverrideProfile() { @Test void priorityProfileOverOriginal() { - final Config config = new SmallRyeConfigBuilder() + SmallRyeConfig config = new SmallRyeConfigBuilder() .addDefaultInterceptors() .withProfile("prof") .withSources( @@ -178,30 +179,36 @@ void priorityProfileOverOriginal() { @Test void propertyNames() { - final Config config = buildConfig("my.prop", "1", "%prof.my.prop", "2", "%prof.prof.only", "1", + SmallRyeConfig config = buildConfig( + "my.prop", "1", + "%prof.my.prop", "2", + "%prof.prof.only", "1", + "%inactive.prop", "1", SMALLRYE_CONFIG_PROFILE, "prof"); assertEquals("2", config.getConfigValue("my.prop").getValue()); assertEquals("1", config.getConfigValue("prof.only").getValue()); - final List properties = stream(config.getPropertyNames().spliterator(), false).collect(toList()); + List properties = stream(config.getPropertyNames().spliterator(), false).collect(toList()); assertFalse(properties.contains("%prof.my.prop")); assertTrue(properties.contains("my.prop")); assertTrue(properties.contains("prof.only")); + // Inactive profile properties are included. We may need to revise this + assertTrue(properties.contains("%inactive.prop")); } @Test void excludePropertiesFromInactiveProfiles() { - final Config config = buildConfig("%prof.my.prop", "1", "%foo.another", "2"); + SmallRyeConfig config = buildConfig("%prof.my.prop", "1", "%foo.another", "2"); - final List properties = stream(config.getPropertyNames().spliterator(), false).collect(toList()); + List properties = stream(config.getPropertyNames().spliterator(), false).collect(toList()); assertTrue(properties.contains("my.prop")); assertFalse(properties.contains("another")); } @Test void profileName() { - final SmallRyeConfig config = new SmallRyeConfigBuilder() + SmallRyeConfig config = new SmallRyeConfigBuilder() .withSources(config("my.prop", "1", "%prof.my.prop", "2")) .withProfile("prof") .build(); @@ -371,7 +378,7 @@ void parentProfileInActiveProfileWithRelocate() { SmallRyeConfig config = new SmallRyeConfigBuilder() .withInterceptorFactories(new ConfigSourceInterceptorFactory() { @Override - public ConfigSourceInterceptor getInterceptor(final ConfigSourceInterceptorContext context) { + public ConfigSourceInterceptor getInterceptor(ConfigSourceInterceptorContext context) { Map relocations = new HashMap<>(); relocations.put(SMALLRYE_CONFIG_PROFILE_PARENT, "quarkus.config.profile.parent"); @@ -482,7 +489,8 @@ void multipleProfileProperty() { .withSources(config("%prod,dev.my.prop", "value", "%prod,dev.my.override", "value", "config_ordinal", "100")) .withSources(config("%dev.my.prop", "minimal", "config_ordinal", "0")) .withSources(config("%prod,dev.another.prop", "multi", "%prod.another.prop", "single")) - .withSources(config("%common,prod,dev.triple.prop", "triple", "%common,prod.triple.prop", "double")); + .withSources(config("%common,prod,dev.triple.prop", "triple", "%common,prod.triple.prop", "double")) + .withSources(config("%commonone,prodone,devone.prop.start.with", "1")); SmallRyeConfig prod = builder.withProfile("prod").build(); assertEquals("value", prod.getRawValue("my.prop")); @@ -491,6 +499,13 @@ void multipleProfileProperty() { assertEquals("override", prod.getRawValue("%prod.my.override")); assertEquals("single", prod.getRawValue("another.prop")); assertEquals("double", prod.getRawValue("triple.prop")); + Set prodNames = StreamSupport.stream(prod.getPropertyNames().spliterator(), false).collect(toSet()); + assertTrue(prodNames.contains("my.prop")); + assertTrue(prodNames.contains("my.override")); + assertTrue(prodNames.contains("another.prop")); + assertTrue(prodNames.contains("triple.prop")); + assertFalse(prodNames.contains("prop.start.with")); + builder.getProfiles().clear(); SmallRyeConfig dev = builder.withProfile("dev").build(); assertEquals("value", dev.getRawValue("my.prop")); @@ -498,9 +513,21 @@ void multipleProfileProperty() { assertEquals("value", dev.getRawValue("my.override")); assertEquals("value", dev.getRawValue("%dev.my.override")); assertEquals("triple", dev.getRawValue("triple.prop")); + Set devNames = StreamSupport.stream(dev.getPropertyNames().spliterator(), false).collect(toSet()); + assertTrue(devNames.contains("my.prop")); + assertTrue(devNames.contains("my.override")); + assertTrue(devNames.contains("another.prop")); + assertTrue(devNames.contains("triple.prop")); + assertFalse(devNames.contains("prop.start.with")); + builder.getProfiles().clear(); SmallRyeConfig common = builder.withProfile("common").build(); assertEquals("double", common.getRawValue("triple.prop")); + Set commonNames = StreamSupport.stream(common.getPropertyNames().spliterator(), false).collect(toSet()); + assertTrue(commonNames.contains("triple.prop")); + assertFalse(commonNames.contains("my.prop")); + assertFalse(commonNames.contains("prop.start.with")); + builder.getProfiles().clear(); } @Test