From ac93b31d9bd545efa5a9987d8078f48d54f3d672 Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Wed, 11 Dec 2024 14:51:55 +0000 Subject: [PATCH] Match most specific mapping prefix when matching environment variables --- .../smallrye/config/ConfigMappingContext.java | 40 +++++++--- .../smallrye/config/EnvConfigSourceTest.java | 80 +++++++++++++++++++ 2 files changed, 111 insertions(+), 9 deletions(-) diff --git a/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java b/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java index 79ec7cd9e..2d1e97bfc 100644 --- a/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java +++ b/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java @@ -10,6 +10,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; @@ -70,7 +71,11 @@ public Map>> get() { this.config = config; this.names = new ConfigMappingNames(names); - matchPropertiesWithEnv(roots); + Set mappingsPrefixes = new HashSet<>(); + for (Set mappingPrefixes : roots.values()) { + mappingsPrefixes.addAll(mappingPrefixes); + } + matchPropertiesWithEnv(mappingsPrefixes); for (Map.Entry, Set> mapping : roots.entrySet()) { Map mappingObjects = new HashMap<>(); for (String rootPath : mapping.getValue()) { @@ -172,19 +177,36 @@ Map, Map> getRootsMap() { return roots; } - private void matchPropertiesWithEnv(final Map, Set> roots) { + private void matchPropertiesWithEnv(final Set mappingsPrefixes) { // TODO - We shouldn't be mutating the EnvSource. // We should do the calculation when creating the EnvSource, but right now mappings and sources are not well integrated. - Set rootPaths = new HashSet<>(); - for (Set paths : roots.values()) { - rootPaths.addAll(paths); - } - boolean all = rootPaths.contains(""); + List prefixes = new ArrayList<>(mappingsPrefixes); + // Sort by number of segments to match the most specific ones first + prefixes.sort(new Comparator() { + @Override + public int compare(final String o1, final String o2) { + int segmentsO1 = 0; + for (int i = 0; i < o1.length(); i++) { + if (o1.charAt(i) == '.') { + segmentsO1++; + } + } + + int segmentsO2 = 0; + for (int i = 0; i < o2.length(); i++) { + if (o2.charAt(i) == '.') { + segmentsO2++; + } + } + return Integer.compare(segmentsO2, segmentsO1); + } + }); + boolean all = prefixes.contains(""); StringBuilder sb = new StringBuilder(); for (ConfigSource configSource : config.getConfigSources(EnvConfigSource.class)) { - if (roots.isEmpty()) { + if (prefixes.isEmpty()) { break; } @@ -201,7 +223,7 @@ private void matchPropertiesWithEnv(final Map, Set> roots) { String matchedRoot = null; if (!all) { - for (String rootPath : rootPaths) { + for (String rootPath : prefixes) { if (StringUtil.isInPath(rootPath, activeEnvProperty)) { matchedRoot = rootPath; break; diff --git a/implementation/src/test/java/io/smallrye/config/EnvConfigSourceTest.java b/implementation/src/test/java/io/smallrye/config/EnvConfigSourceTest.java index e8b0b56d5..ac97a6140 100644 --- a/implementation/src/test/java/io/smallrye/config/EnvConfigSourceTest.java +++ b/implementation/src/test/java/io/smallrye/config/EnvConfigSourceTest.java @@ -590,6 +590,86 @@ interface ServiceDiscoveryConfiguration { } } + @Test + void mappingMapsWithEnvMultiplePrefixes() { + SmallRyeConfig config = new SmallRyeConfigBuilder() + .withSources(new EnvConfigSource(Map.of( + "PREFIX_COMPOSED_SERVICE_SERVICE_DISCOVERY_ADDRESS", "from-env"), 300)) + .withMapping(SimplePrefix.class) + .withMapping(ComposedPrefix.class) + .build(); + + ComposedPrefix mapping = config.getConfigMapping(ComposedPrefix.class); + assertEquals("from-env", mapping.serviceConfiguration().get("service").serviceDiscovery().params().get("address")); + } + + @ConfigMapping(prefix = "prefix") + interface SimplePrefix { + Optional value(); + } + + @ConfigMapping(prefix = "prefix.composed") + interface ComposedPrefix { + @WithParentName + Map serviceConfiguration(); + + interface ServiceConfiguration { + ServiceDiscoveryConfiguration serviceDiscovery(); + + interface ServiceDiscoveryConfiguration { + @WithParentName + Map params(); + } + } + } + + @Test + void mappingsMapsWithEnvSplit() { + SmallRyeConfig config = new SmallRyeConfigBuilder() + .withSources(new EnvConfigSource(Map.of( + "SERVICES_SERVICE_SERVICE_DISCOVERY_ADDRESS", "from-env", + "SERVICES_SERVICE_ANOTHER_DISCOVERY_ADDRESS", "from-env"), 300)) + .withMapping(ServicesOne.class) + .withMapping(ServicesTwo.class) + .build(); + + ServicesOne servicesOne = config.getConfigMapping(ServicesOne.class); + ServicesTwo servicesTwo = config.getConfigMapping(ServicesTwo.class); + + assertEquals("from-env", servicesOne.serviceConfiguration().get("service").serviceDiscovery().params().get("address")); + assertEquals("from-env", servicesTwo.serviceConfiguration().get("service").anotherDiscovery().params().get("address")); + } + + @ConfigMapping(prefix = "services") + interface ServicesOne { + @WithParentName + Map serviceConfiguration(); + + interface ServiceConfiguration { + ServiceDiscoveryConfiguration serviceDiscovery(); + + interface ServiceDiscoveryConfiguration { + @WithParentName + Map params(); + } + } + } + + @ConfigMapping(prefix = "services") + interface ServicesTwo { + @WithParentName + Map serviceConfiguration(); + + interface ServiceConfiguration { + ServiceDiscoveryConfiguration anotherDiscovery(); + + interface ServiceDiscoveryConfiguration { + @WithParentName + Map params(); + } + } + } + private static boolean envSourceEquals(String name, String lookup) { return BOOLEAN_CONVERTER.convert(new EnvConfigSource(Map.of(name, "true"), 100).getValue(lookup)); }