Commit 6ebc69d7 authored by Stephane Nicoll's avatar Stephane Nicoll

Polish "Include properties in source merge algorithm"

See gh-25507
parent cf4bc6e9
...@@ -61,7 +61,7 @@ public class SimpleConfigurationMetadataRepository implements ConfigurationMetad ...@@ -61,7 +61,7 @@ public class SimpleConfigurationMetadataRepository implements ConfigurationMetad
} }
String sourceType = source.getType(); String sourceType = source.getType();
if (sourceType != null) { if (sourceType != null) {
putIfAbsent(group.getSources(), sourceType, source); addOrMergeSource(group.getSources(), sourceType, source);
} }
} }
} }
...@@ -93,7 +93,7 @@ public class SimpleConfigurationMetadataRepository implements ConfigurationMetad ...@@ -93,7 +93,7 @@ public class SimpleConfigurationMetadataRepository implements ConfigurationMetad
// Merge properties // Merge properties
group.getProperties().forEach((name, value) -> putIfAbsent(existingGroup.getProperties(), name, value)); group.getProperties().forEach((name, value) -> putIfAbsent(existingGroup.getProperties(), name, value));
// Merge sources // Merge sources
group.getSources().forEach((name, value) -> putIfAbsent(existingGroup.getSources(), name, value)); group.getSources().forEach((name, value) -> addOrMergeSource(existingGroup.getSources(), name, value));
} }
} }
...@@ -111,23 +111,21 @@ public class SimpleConfigurationMetadataRepository implements ConfigurationMetad ...@@ -111,23 +111,21 @@ public class SimpleConfigurationMetadataRepository implements ConfigurationMetad
return this.allGroups.get(source.getGroupId()); return this.allGroups.get(source.getGroupId());
} }
private void addOrMergeSource(Map<String, ConfigurationMetadataSource> sources, String name,
ConfigurationMetadataSource source) {
ConfigurationMetadataSource existingSource = sources.get(name);
if (existingSource == null) {
sources.put(name, source);
}
else {
source.getProperties().forEach((k, v) -> putIfAbsent(existingSource.getProperties(), k, v));
}
}
private <V> void putIfAbsent(Map<String, V> map, String key, V value) { private <V> void putIfAbsent(Map<String, V> map, String key, V value) {
if (!map.containsKey(key)) { if (!map.containsKey(key)) {
map.put(key, value); map.put(key, value);
} }
} }
/*
Uncomment this code to fix issue revealed by ConfigurationMetadataRepositoryJsonBuilderTests#severalRepositoriesIdenticalGroups3()
private void putIfAbsent(Map<String, ConfigurationMetadataSource> sources, String name,
ConfigurationMetadataSource source) {
ConfigurationMetadataSource existing = sources.get(name);
if (existing == null) {
sources.put(name, source);
} else {
source.getProperties().forEach((k, v) -> putIfAbsent(existing.getProperties(), k, v));
}
}
*/
} }
...@@ -18,6 +18,7 @@ package org.springframework.boot.configurationmetadata; ...@@ -18,6 +18,7 @@ package org.springframework.boot.configurationmetadata;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Arrays;
import java.util.Map; import java.util.Map;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
...@@ -99,116 +100,60 @@ class ConfigurationMetadataRepositoryJsonBuilderTests extends AbstractConfigurat ...@@ -99,116 +100,60 @@ class ConfigurationMetadataRepositoryJsonBuilderTests extends AbstractConfigurat
try (InputStream foo2 = getInputStreamFor("foo2")) { try (InputStream foo2 = getInputStreamFor("foo2")) {
ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder.create(foo, foo2) ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder.create(foo, foo2)
.build(); .build();
assertThat(repo.getAllGroups()).hasSize(1); Iterable<String> allKeys = Arrays.asList("spring.foo.name", "spring.foo.description",
"spring.foo.counter", "spring.foo.enabled", "spring.foo.type");
assertThat(repo.getAllProperties()).containsOnlyKeys(allKeys);
assertThat(repo.getAllGroups()).containsOnlyKeys("spring.foo");
ConfigurationMetadataGroup group = repo.getAllGroups().get("spring.foo"); ConfigurationMetadataGroup group = repo.getAllGroups().get("spring.foo");
contains(group.getSources(), "org.acme.Foo", "org.acme.Foo2", "org.springframework.boot.FooProperties"); assertThat(group.getProperties()).containsOnlyKeys(allKeys);
assertThat(group.getSources()).hasSize(3); assertThat(group.getSources()).containsOnlyKeys("org.acme.Foo", "org.acme.Foo2",
contains(group.getProperties(), "spring.foo.name", "spring.foo.description", "spring.foo.counter", "org.springframework.boot.FooProperties");
"spring.foo.enabled", "spring.foo.type"); assertThat(group.getSources().get("org.acme.Foo").getProperties()).containsOnlyKeys("spring.foo.name",
assertThat(group.getProperties()).hasSize(5); "spring.foo.description");
contains(repo.getAllProperties(), "spring.foo.name", "spring.foo.description", "spring.foo.counter", assertThat(group.getSources().get("org.acme.Foo2").getProperties())
"spring.foo.enabled", "spring.foo.type"); .containsOnlyKeys("spring.foo.enabled", "spring.foo.type");
assertThat(repo.getAllProperties()).hasSize(5); assertThat(group.getSources().get("org.springframework.boot.FooProperties").getProperties())
.containsOnlyKeys("spring.foo.name", "spring.foo.counter");
} }
} }
} }
/*
* A rewrite of severalRepositoriesIdenticalGroups() using "containsOnlyKeys" to show actual vs. expected when assert fails.
*/
@Test @Test
void severalRepositoriesIdenticalGroups_rewritten() throws IOException { void severalRepositoriesIdenticalGroupsWithSameType() throws IOException {
try (InputStream foo = getInputStreamFor("foo")) { try (InputStream foo = getInputStreamFor("foo")) {
try (InputStream foo2 = getInputStreamFor("foo2")) { try (InputStream foo3 = getInputStreamFor("foo3")) {
ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder.create(foo, foo2) ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder.create(foo, foo3)
.build(); .build();
Iterable<String> allKeys = Arrays.asList("spring.foo.name", "spring.foo.description",
// assert all properties are found "spring.foo.counter", "spring.foo.enabled", "spring.foo.type");
assertThat(repo.getAllProperties()).containsOnlyKeys( assertThat(repo.getAllProperties()).containsOnlyKeys(allKeys);
"spring.foo.name",
"spring.foo.description",
"spring.foo.counter",
"spring.foo.enabled",
"spring.foo.type");
// we have a single group containing all properties
assertThat(repo.getAllGroups()).containsOnlyKeys("spring.foo"); assertThat(repo.getAllGroups()).containsOnlyKeys("spring.foo");
ConfigurationMetadataGroup group = repo.getAllGroups().get("spring.foo"); ConfigurationMetadataGroup group = repo.getAllGroups().get("spring.foo");
assertThat(group.getProperties()).containsOnlyKeys( assertThat(group.getProperties()).containsOnlyKeys(allKeys);
"spring.foo.name", assertThat(group.getSources()).containsOnlyKeys("org.acme.Foo",
"spring.foo.description", "org.springframework.boot.FooProperties");
"spring.foo.counter", assertThat(group.getSources().get("org.acme.Foo").getProperties()).containsOnlyKeys("spring.foo.name",
"spring.foo.enabled", "spring.foo.description", "spring.foo.enabled", "spring.foo.type");
"spring.foo.type"); assertThat(group.getSources().get("org.springframework.boot.FooProperties").getProperties())
.containsOnlyKeys("spring.foo.name", "spring.foo.counter");
// the group contains 3 different sources
assertThat(group.getSources()).containsOnlyKeys(
"org.acme.Foo", "org.acme.Foo2", "org.springframework.boot.FooProperties");
assertThat(group.getSources().get("org.acme.Foo").getProperties()).containsOnlyKeys(
"spring.foo.name",
"spring.foo.description");
assertThat(group.getSources().get("org.acme.Foo2").getProperties()).containsOnlyKeys(
"spring.foo.enabled",
"spring.foo.type");
assertThat(group.getSources().get("org.springframework.boot.FooProperties").getProperties()).containsOnlyKeys(
"spring.foo.name",
"spring.foo.counter");
} }
} }
} }
/*
* "foo3" contains the same properties as "foo2" except they refer to a group that already exists in
* "foo1" (same NAME, same TYPE).
*
* This test shows that the union of properties collected from the sources is less than what the group actually
* contains (some properties are missing).
*/
@Test @Test
void severalRepositoriesIdenticalGroups3() throws IOException { void severalRepositoriesIdenticalGroupsWithSameTypeDoesNotOverrideSource() throws IOException {
try (InputStream foo = getInputStreamFor("foo")) { try (InputStream foo = getInputStreamFor("foo")) {
try (InputStream foo3 = getInputStreamFor("foo3")) { try (InputStream foo3 = getInputStreamFor("foo3")) {
ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder.create(foo, foo3) ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder.create(foo, foo3)
.build(); .build();
assertThat(repo.getAllProperties()).containsOnlyKeys(
"spring.foo.name",
"spring.foo.description",
"spring.foo.counter",
"spring.foo.enabled",
"spring.foo.type");
assertThat(repo.getAllGroups()).containsOnlyKeys("spring.foo");
ConfigurationMetadataGroup group = repo.getAllGroups().get("spring.foo"); ConfigurationMetadataGroup group = repo.getAllGroups().get("spring.foo");
assertThat(group.getProperties()).containsOnlyKeys( ConfigurationMetadataSource fooSource = group.getSources().get("org.acme.Foo");
"spring.foo.name", assertThat(fooSource.getSourceMethod()).isEqualTo("foo()");
"spring.foo.description", assertThat(fooSource.getDescription()).isEqualTo("This is Foo.");
"spring.foo.counter",
"spring.foo.enabled",
"spring.foo.type");
assertThat(group.getSources()).containsOnlyKeys("org.acme.Foo", "org.springframework.boot.FooProperties");
assertThat(group.getSources().get("org.acme.Foo").getProperties()).containsOnlyKeys(
"spring.foo.name",
"spring.foo.description",
"spring.foo.enabled", // <-- missing although present in repo.getAllProperties()
"spring.foo.type"); // <-- missing although present in repo.getAllProperties()
assertThat(group.getSources().get("org.springframework.boot.FooProperties").getProperties()).containsOnlyKeys(
"spring.foo.name",
"spring.foo.counter");
} }
} }
} }
@Test @Test
void emptyGroups() throws IOException { void emptyGroups() throws IOException {
try (InputStream in = getInputStreamFor("empty-groups")) { try (InputStream in = getInputStreamFor("empty-groups")) {
......
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
"name": "spring.foo", "name": "spring.foo",
"type": "org.acme.Foo", "type": "org.acme.Foo",
"sourceType": "org.acme.config.FooApp", "sourceType": "org.acme.config.FooApp",
"sourceMethod": "foo2()", "sourceMethod": "foo3()",
"description": "This is Foo." "description": "This is Foo3."
} }
], ],
"properties": [ "properties": [
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment