diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySource.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySource.java index 303748c0b7..2154204ee3 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySource.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySource.java @@ -16,6 +16,8 @@ package org.springframework.boot.context.properties.source; +import java.util.Optional; + import org.springframework.util.Assert; /** @@ -50,6 +52,17 @@ class AliasedConfigurationPropertySource implements ConfigurationPropertySource return result; } + @Override + public Optional containsDescendantOf(ConfigurationPropertyName name) { + Assert.notNull(name, "Name must not be null"); + Optional result = this.source.containsDescendantOf(name); + for (ConfigurationPropertyName alias : getAliases().getAliases(name)) { + Optional aliasResult = this.source.containsDescendantOf(alias); + result = result.flatMap((r) -> aliasResult.flatMap(a -> Optional.of(r || a))); + } + return result; + } + protected ConfigurationPropertySource getSource() { return this.source; } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySource.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySource.java index 80f729577f..d6cd35b3c7 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySource.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySource.java @@ -16,6 +16,7 @@ package org.springframework.boot.context.properties.source; +import java.util.Optional; import java.util.function.Predicate; import org.springframework.boot.origin.OriginTrackedValue; @@ -40,6 +41,27 @@ public interface ConfigurationPropertySource { */ ConfigurationProperty getConfigurationProperty(ConfigurationPropertyName name); + /** + * Optionally returns if the source contains any descendants of the specified name. + *
    + *
  • A result of {@code true} means that there is at least on property in the source + * with a name that's an + * {@link ConfigurationPropertyName#isAncestorOf(ConfigurationPropertyName) ancestor} + * of {@code name}.
  • + *
  • A result of {@code false} means that that there are no properties in the source + * with a name that's an + * {@link ConfigurationPropertyName#isAncestorOf(ConfigurationPropertyName) ancestor} + * of {@code name}.
  • + *
  • A result of {@code empty} means it is not possible to determine up determine if + * there's a property in the source with a name that's an + * {@link ConfigurationPropertyName#isAncestorOf(ConfigurationPropertyName) ancestor} + * of {@code name}. + *
+ * @param name the name to check + * @return an optional boolean determining if a descendant is contained in the source + */ + Optional containsDescendantOf(ConfigurationPropertyName name); + /** * Return a filtered variant of this source, containing only names that match the * given {@link Predicate}. diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySources.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySources.java index 78292f48e5..1ca9f163e5 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySources.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySources.java @@ -18,15 +18,19 @@ package org.springframework.boot.context.properties.source; import java.util.Iterator; import java.util.Map; +import java.util.Optional; import java.util.WeakHashMap; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import org.springframework.boot.env.RandomValuePropertySource; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; +import org.springframework.core.env.PropertySource.StubPropertySource; import org.springframework.core.env.PropertySources; import org.springframework.core.env.PropertySourcesPropertyResolver; import org.springframework.core.env.SystemEnvironmentPropertySource; @@ -45,6 +49,9 @@ import org.springframework.util.Assert; public class ConfigurationPropertySources implements Iterable { + private static final ConfigurationPropertyName RANDOM = ConfigurationPropertyName + .of("random"); + /** * The name of the {@link PropertySource} {@link #adapt adapter}. */ @@ -71,7 +78,8 @@ public class ConfigurationPropertySources } private Stream> streamPropertySources(PropertySources sources) { - return StreamSupport.stream(sources.spliterator(), false).flatMap(this::flatten); + return StreamSupport.stream(sources.spliterator(), false).flatMap(this::flatten) + .filter(this::notStubSource); } private Stream> flatten(PropertySource source) { @@ -82,6 +90,10 @@ public class ConfigurationPropertySources return Stream.of(source); } + private boolean notStubSource(PropertySource source) { + return !(source instanceof StubPropertySource); + } + private ConfigurationPropertySource adapt(PropertySource source) { return this.adapters.computeIfAbsent(source, this::createAdapter); } @@ -92,7 +104,8 @@ public class ConfigurationPropertySources return new PropertySourceIterableConfigurationPropertySource( (EnumerablePropertySource) source, mapper); } - return new PropertySourceConfigurationPropertySource(source, mapper); + return new PropertySourceConfigurationPropertySource(source, mapper, + getContainsDescendantOfMethod(source)); } private PropertyMapper getPropertyMapper(PropertySource source) { @@ -124,6 +137,15 @@ public class ConfigurationPropertySources return source; } + private Function> getContainsDescendantOfMethod( + PropertySource source) { + if (source instanceof RandomValuePropertySource) { + return (name) -> Optional + .of(name.isAncestorOf(RANDOM) || name.equals(RANDOM)); + } + return null; + } + /** * Attach a {@link ConfigurationPropertySources} instance to the specified * {@link ConfigurableEnvironment} so that classic diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSource.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSource.java index 3788f0d9bf..1c201eb850 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSource.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSource.java @@ -16,6 +16,7 @@ package org.springframework.boot.context.properties.source; +import java.util.Optional; import java.util.function.Predicate; import org.springframework.util.Assert; @@ -47,6 +48,13 @@ class FilteredConfigurationPropertiesSource implements ConfigurationPropertySour return (filtered ? getSource().getConfigurationProperty(name) : null); } + @Override + public Optional containsDescendantOf(ConfigurationPropertyName name) { + // We can't be sure a contained descendant won't be filtered + return this.source.containsDescendantOf(name) + .flatMap((result) -> result ? Optional.empty() : Optional.of(result)); + } + protected ConfigurationPropertySource getSource() { return this.source; } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSource.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSource.java index 7b85cbf36f..133155381b 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSource.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSource.java @@ -16,6 +16,7 @@ package org.springframework.boot.context.properties.source; +import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -46,4 +47,9 @@ class FilteredIterableConfigurationPropertiesSource return (IterableConfigurationPropertySource) super.getSource(); } + @Override + public Optional containsDescendantOf(ConfigurationPropertyName name) { + return Optional.of(stream().filter(name::isAncestorOf).findFirst().isPresent()); + } + } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/IterableConfigurationPropertySource.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/IterableConfigurationPropertySource.java index 55ae3a6c55..3658524fe4 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/IterableConfigurationPropertySource.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/IterableConfigurationPropertySource.java @@ -17,6 +17,7 @@ package org.springframework.boot.context.properties.source; import java.util.Iterator; +import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Stream; @@ -60,6 +61,11 @@ public interface IterableConfigurationPropertySource */ Stream stream(); + @Override + default Optional containsDescendantOf(ConfigurationPropertyName name) { + return Optional.of(stream().filter(name::isAncestorOf).findFirst().isPresent()); + } + @Override default IterableConfigurationPropertySource filter( Predicate filter) { diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/PropertySourceConfigurationPropertySource.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/PropertySourceConfigurationPropertySource.java index d3808b85e7..92167ac1cd 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/PropertySourceConfigurationPropertySource.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/PropertySourceConfigurationPropertySource.java @@ -19,6 +19,8 @@ package org.springframework.boot.context.properties.source; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; import org.springframework.boot.origin.Origin; import org.springframework.boot.origin.PropertySourceOrigin; @@ -54,17 +56,24 @@ class PropertySourceConfigurationPropertySource implements ConfigurationProperty private final PropertyMapper mapper; + private final Function> containsDescendantOfMethod; + /** * Create a new {@link PropertySourceConfigurationPropertySource} implementation. * @param propertySource the source property source * @param mapper the property mapper + * @param containsDescendantOfMethod function used to implement + * {@link #containsDescendantOf(ConfigurationPropertyName)} (may be {@code null}) */ PropertySourceConfigurationPropertySource(PropertySource propertySource, - PropertyMapper mapper) { + PropertyMapper mapper, + Function> containsDescendantOfMethod) { Assert.notNull(propertySource, "PropertySource must not be null"); Assert.notNull(mapper, "Mapper must not be null"); this.propertySource = propertySource; this.mapper = new ExceptionSwallowingPropertyMapper(mapper); + this.containsDescendantOfMethod = (containsDescendantOfMethod != null ? containsDescendantOfMethod + : (n) -> Optional.empty()); } @Override @@ -74,6 +83,11 @@ class PropertySourceConfigurationPropertySource implements ConfigurationProperty return find(mappings, name); } + @Override + public Optional containsDescendantOf(ConfigurationPropertyName name) { + return this.containsDescendantOfMethod.apply(name); + } + protected final ConfigurationProperty find(List mappings, ConfigurationPropertyName name) { return mappings.stream().filter((m) -> m.isApplicable(name)).map(this::find) @@ -101,6 +115,11 @@ class PropertySourceConfigurationPropertySource implements ConfigurationProperty return this.mapper; } + @Override + public String toString() { + return this.propertySource.toString(); + } + /** * {@link PropertyMapper} that swallows exceptions when the mapping fails. */ diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/PropertySourceIterableConfigurationPropertySource.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/PropertySourceIterableConfigurationPropertySource.java index 79f06d8a33..72a81f35e7 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/PropertySourceIterableConfigurationPropertySource.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/PropertySourceIterableConfigurationPropertySource.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Optional; import java.util.stream.Stream; import org.springframework.core.env.EnumerablePropertySource; @@ -45,7 +46,7 @@ class PropertySourceIterableConfigurationPropertySource PropertySourceIterableConfigurationPropertySource( EnumerablePropertySource propertySource, PropertyMapper mapper) { - super(propertySource, mapper); + super(propertySource, mapper, null); assertEnumerablePropertySource(propertySource); } @@ -87,6 +88,11 @@ class PropertySourceIterableConfigurationPropertySource return getConfigurationPropertyNames().iterator(); } + @Override + public Optional containsDescendantOf(ConfigurationPropertyName name) { + return Optional.of(stream().filter(name::isAncestorOf).findFirst().isPresent()); + } + private List getConfigurationPropertyNames() { Cache cache = getCache(); List names = (cache != null ? cache.getNames() : null); diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySourceTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySourceTests.java index 7daf45a35f..a437c66f83 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySourceTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySourceTests.java @@ -16,9 +16,15 @@ package org.springframework.boot.context.properties.source; +import java.util.Optional; + import org.junit.Test; +import org.mockito.Answers; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.withSettings; /** * Tests for {@link AliasedConfigurationPropertySource}. @@ -50,6 +56,59 @@ public class AliasedConfigurationPropertySourceTests { assertThat(getValue(aliased, "foo.baz")).isEqualTo("biff"); } + @Test + public void containsDescendantOfWhenSourceReturnsEmptyShouldReturnEmpty() + throws Exception { + ConfigurationPropertyName name = ConfigurationPropertyName.of("foo"); + ConfigurationPropertySource source = mock(ConfigurationPropertySource.class, + withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)); + given(source.containsDescendantOf(name)).willReturn(Optional.empty()); + ConfigurationPropertySource aliased = source + .withAliases(new ConfigurationPropertyNameAliases("foo.bar", "foo.bar1")); + assertThat(aliased.containsDescendantOf(name)).isEmpty(); + } + + @Test + public void containsDescendantOfWhenAliasReturnsEmptyShouldReturnEmpty() + throws Exception { + ConfigurationPropertyName name = ConfigurationPropertyName.of("foo"); + ConfigurationPropertySource source = mock(ConfigurationPropertySource.class, + withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)); + given(source.containsDescendantOf(name)).willReturn(Optional.of(true)); + given(source.containsDescendantOf(ConfigurationPropertyName.of("bar"))) + .willReturn(Optional.empty()); + ConfigurationPropertySource aliased = source + .withAliases(new ConfigurationPropertyNameAliases("foo", "bar")); + assertThat(aliased.containsDescendantOf(name)).isEmpty(); + } + + @Test + public void containsDescendantOfWhenAllAreFalseShouldReturnFalse() throws Exception { + ConfigurationPropertyName name = ConfigurationPropertyName.of("foo"); + ConfigurationPropertySource source = mock(ConfigurationPropertySource.class, + withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)); + given(source.containsDescendantOf(name)).willReturn(Optional.of(false)); + given(source.containsDescendantOf(ConfigurationPropertyName.of("bar"))) + .willReturn(Optional.of(false)); + ConfigurationPropertySource aliased = source + .withAliases(new ConfigurationPropertyNameAliases("foo", "bar")); + assertThat(aliased.containsDescendantOf(name)).contains(false); + + } + + @Test + public void containsDescendantOfWhenAnyIsTrueShouldReturnTrue() throws Exception { + ConfigurationPropertyName name = ConfigurationPropertyName.of("foo"); + ConfigurationPropertySource source = mock(ConfigurationPropertySource.class, + withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)); + given(source.containsDescendantOf(name)).willReturn(Optional.of(false)); + given(source.containsDescendantOf(ConfigurationPropertyName.of("bar"))) + .willReturn(Optional.of(true)); + ConfigurationPropertySource aliased = source + .withAliases(new ConfigurationPropertyNameAliases("foo", "bar")); + assertThat(aliased.containsDescendantOf(name)).contains(true); + } + private Object getValue(ConfigurationPropertySource source, String name) { ConfigurationProperty property = source .getConfigurationProperty(ConfigurationPropertyName.of(name)); diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSourceTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSourceTests.java index 5a991a8ef1..621b100677 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSourceTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSourceTests.java @@ -17,12 +17,17 @@ package org.springframework.boot.context.properties.source; import java.util.Objects; +import java.util.Optional; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.mockito.Answers; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.withSettings; /** * Test for {@link FilteredIterableConfigurationPropertiesSource}. @@ -61,7 +66,39 @@ public class FilteredConfigurationPropertiesSourceTests { assertThat(source.getConfigurationProperty(bracketName).getValue()) .isEqualTo("2"); assertThat(filtered.getConfigurationProperty(bracketName)).isNull(); + } + @Test + public void containsDescendantOfWhenSourceReturnsEmptyShouldReturnEmpty() + throws Exception { + ConfigurationPropertyName name = ConfigurationPropertyName.of("foo"); + ConfigurationPropertySource source = mock(ConfigurationPropertySource.class, + withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)); + given(source.containsDescendantOf(name)).willReturn(Optional.empty()); + ConfigurationPropertySource filtered = source.filter((n) -> true); + assertThat(filtered.containsDescendantOf(name)).isEmpty(); + } + + @Test + public void containsDescendantOfWhenSourceReturnsFalseShouldReturnFalse() + throws Exception { + ConfigurationPropertyName name = ConfigurationPropertyName.of("foo"); + ConfigurationPropertySource source = mock(ConfigurationPropertySource.class, + withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)); + given(source.containsDescendantOf(name)).willReturn(Optional.of(false)); + ConfigurationPropertySource filtered = source.filter((n) -> true); + assertThat(filtered.containsDescendantOf(name)).contains(false); + } + + @Test + public void containsDescendantOfWhenSourceReturnsTrueShouldReturnEmpty() + throws Exception { + ConfigurationPropertyName name = ConfigurationPropertyName.of("foo"); + ConfigurationPropertySource source = mock(ConfigurationPropertySource.class, + withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)); + given(source.containsDescendantOf(name)).willReturn(Optional.of(true)); + ConfigurationPropertySource filtered = source.filter((n) -> true); + assertThat(filtered.containsDescendantOf(name)).isEmpty(); } protected final ConfigurationPropertySource createTestSource() { diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSourceTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSourceTests.java index c274376338..b6d4a7030a 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSourceTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSourceTests.java @@ -43,6 +43,19 @@ public class FilteredIterableConfigurationPropertiesSourceTests return source; } + @Test + public void containsDescendantOfShouldUseContents() throws Exception { + MockConfigurationPropertySource source = new MockConfigurationPropertySource(); + source.put("foo.bar.baz", "1"); + source.put("foo.bar[0]", "1"); + source.put("faf.bar[0]", "1"); + IterableConfigurationPropertySource filtered = source.filter(this::noBrackets); + assertThat(filtered.containsDescendantOf(ConfigurationPropertyName.of("foo"))) + .contains(true); + assertThat(filtered.containsDescendantOf(ConfigurationPropertyName.of("faf"))) + .contains(false); + } + private boolean noBrackets(ConfigurationPropertyName name) { return name.toString().indexOf("[") == -1; } diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/MockConfigurationPropertySource.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/MockConfigurationPropertySource.java index 838f268c64..e01681a47c 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/MockConfigurationPropertySource.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/MockConfigurationPropertySource.java @@ -19,6 +19,7 @@ package org.springframework.boot.context.properties.source; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Optional; import java.util.stream.Stream; import org.springframework.boot.origin.MockOrigin; @@ -97,6 +98,11 @@ public class MockConfigurationPropertySource return MockConfigurationPropertySource.this.getConfigurationProperty(name); } + @Override + public Optional containsDescendantOf(ConfigurationPropertyName name) { + return Optional.empty(); + } + } } diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/PropertySourceConfigurationPropertySourceTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/PropertySourceConfigurationPropertySourceTests.java index 2c8faa827e..9823354a7c 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/PropertySourceConfigurationPropertySourceTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/PropertySourceConfigurationPropertySourceTests.java @@ -46,14 +46,16 @@ public class PropertySourceConfigurationPropertySourceTests { public void createWhenPropertySourceIsNullShouldThrowException() throws Exception { this.thrown.expect(IllegalArgumentException.class); this.thrown.expectMessage("PropertySource must not be null"); - new PropertySourceConfigurationPropertySource(null, mock(PropertyMapper.class)); + new PropertySourceConfigurationPropertySource(null, mock(PropertyMapper.class), + null); } @Test public void createWhenMapperIsNullShouldThrowException() throws Exception { this.thrown.expect(IllegalArgumentException.class); this.thrown.expectMessage("Mapper must not be null"); - new PropertySourceConfigurationPropertySource(mock(PropertySource.class), null); + new PropertySourceConfigurationPropertySource(mock(PropertySource.class), null, + null); } @Test @@ -67,7 +69,7 @@ public class PropertySourceConfigurationPropertySourceTests { ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key"); mapper.addFromConfigurationProperty(name, "key2"); PropertySourceConfigurationPropertySource adapter = new PropertySourceConfigurationPropertySource( - propertySource, mapper); + propertySource, mapper, null); assertThat(adapter.getConfigurationProperty(name).getValue()).isEqualTo("value2"); } @@ -81,7 +83,7 @@ public class PropertySourceConfigurationPropertySourceTests { mapper.addFromConfigurationProperty(name, "key", (value) -> value.toString().replace("ue", "let")); PropertySourceConfigurationPropertySource adapter = new PropertySourceConfigurationPropertySource( - propertySource, mapper); + propertySource, mapper, null); assertThat(adapter.getConfigurationProperty(name).getValue()).isEqualTo("vallet"); } @@ -94,7 +96,7 @@ public class PropertySourceConfigurationPropertySourceTests { ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key"); mapper.addFromConfigurationProperty(name, "key"); PropertySourceConfigurationPropertySource adapter = new PropertySourceConfigurationPropertySource( - propertySource, mapper); + propertySource, mapper, null); assertThat(adapter.getConfigurationProperty(name).getOrigin().toString()) .isEqualTo("\"key\" from property source \"test\""); } @@ -109,11 +111,22 @@ public class PropertySourceConfigurationPropertySourceTests { ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key"); mapper.addFromConfigurationProperty(name, "key"); PropertySourceConfigurationPropertySource adapter = new PropertySourceConfigurationPropertySource( - propertySource, mapper); + propertySource, mapper, null); assertThat(adapter.getConfigurationProperty(name).getOrigin().toString()) .isEqualTo("TestOrigin key"); } + @Test + public void containsDescendantOfShouldReturnEmpty() throws Exception { + Map source = new LinkedHashMap<>(); + source.put("foo.bar", "value"); + PropertySource propertySource = new MapPropertySource("test", source); + PropertySourceConfigurationPropertySource adapter = new PropertySourceConfigurationPropertySource( + propertySource, new DefaultPropertyMapper(), null); + assertThat(adapter.containsDescendantOf(ConfigurationPropertyName.of("foo"))) + .isEmpty(); + } + /** * Test {@link PropertySource} that's also a {@link OriginLookup}. */ diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/PropertySourceIterableConfigurationPropertySourceTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/PropertySourceIterableConfigurationPropertySourceTests.java index 9f0de2dfee..62884f3f52 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/PropertySourceIterableConfigurationPropertySourceTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/PropertySourceIterableConfigurationPropertySourceTests.java @@ -156,6 +156,23 @@ public class PropertySourceIterableConfigurationPropertySourceTests { .isEqualTo("TestOrigin key"); } + @Test + public void containsDescendantOfShouldCheckSourceNames() throws Exception { + Map source = new LinkedHashMap<>(); + source.put("foo.bar", "value"); + source.put("faf", "value"); + EnumerablePropertySource propertySource = new OriginCapablePropertySource<>( + new MapPropertySource("test", source)); + PropertySourceIterableConfigurationPropertySource adapter = new PropertySourceIterableConfigurationPropertySource( + propertySource, new DefaultPropertyMapper()); + assertThat(adapter.containsDescendantOf(ConfigurationPropertyName.of("foo"))) + .contains(true); + assertThat(adapter.containsDescendantOf(ConfigurationPropertyName.of("faf"))) + .contains(false); + assertThat(adapter.containsDescendantOf(ConfigurationPropertyName.of("fof"))) + .contains(false); + } + /** * Test {@link PropertySource} that's also a {@link OriginLookup}. */