diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata/src/main/java/org/springframework/boot/configurationmetadata/SimpleConfigurationMetadataRepository.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata/src/main/java/org/springframework/boot/configurationmetadata/SimpleConfigurationMetadataRepository.java index 5becb4d690..03948f699c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata/src/main/java/org/springframework/boot/configurationmetadata/SimpleConfigurationMetadataRepository.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata/src/main/java/org/springframework/boot/configurationmetadata/SimpleConfigurationMetadataRepository.java @@ -61,7 +61,7 @@ public class SimpleConfigurationMetadataRepository implements ConfigurationMetad } String sourceType = source.getType(); if (sourceType != null) { - putIfAbsent(group.getSources(), sourceType, source); + addOrMergeSource(group.getSources(), sourceType, source); } } } @@ -93,7 +93,7 @@ public class SimpleConfigurationMetadataRepository implements ConfigurationMetad // Merge properties group.getProperties().forEach((name, value) -> putIfAbsent(existingGroup.getProperties(), name, value)); // Merge sources - group.getSources().forEach((name, value) -> putIfAbsent(existingGroup.getSources(), name, value)); + group.getSources().forEach((name, value) -> addOrMergeSource(existingGroup.getSources(), name, value)); } } @@ -111,6 +111,17 @@ public class SimpleConfigurationMetadataRepository implements ConfigurationMetad return this.allGroups.get(source.getGroupId()); } + private void addOrMergeSource(Map 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 void putIfAbsent(Map map, String key, V value) { if (!map.containsKey(key)) { map.put(key, value); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata/src/test/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepositoryJsonBuilderTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata/src/test/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepositoryJsonBuilderTests.java index 0706b9036d..fd5dbcd3c1 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata/src/test/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepositoryJsonBuilderTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata/src/test/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepositoryJsonBuilderTests.java @@ -18,6 +18,7 @@ package org.springframework.boot.configurationmetadata; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import java.util.Map; import org.junit.jupiter.api.Test; @@ -99,16 +100,56 @@ class ConfigurationMetadataRepositoryJsonBuilderTests extends AbstractConfigurat try (InputStream foo2 = getInputStreamFor("foo2")) { ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder.create(foo, foo2) .build(); - assertThat(repo.getAllGroups()).hasSize(1); + Iterable 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"); - contains(group.getSources(), "org.acme.Foo", "org.acme.Foo2", "org.springframework.boot.FooProperties"); - assertThat(group.getSources()).hasSize(3); - contains(group.getProperties(), "spring.foo.name", "spring.foo.description", "spring.foo.counter", - "spring.foo.enabled", "spring.foo.type"); - assertThat(group.getProperties()).hasSize(5); - contains(repo.getAllProperties(), "spring.foo.name", "spring.foo.description", "spring.foo.counter", - "spring.foo.enabled", "spring.foo.type"); - assertThat(repo.getAllProperties()).hasSize(5); + assertThat(group.getProperties()).containsOnlyKeys(allKeys); + 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"); + } + } + } + + @Test + void severalRepositoriesIdenticalGroupsWithSameType() throws IOException { + try (InputStream foo = getInputStreamFor("foo")) { + try (InputStream foo3 = getInputStreamFor("foo3")) { + ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder.create(foo, foo3) + .build(); + Iterable 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"); + assertThat(group.getProperties()).containsOnlyKeys(allKeys); + 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", "spring.foo.type"); + assertThat(group.getSources().get("org.springframework.boot.FooProperties").getProperties()) + .containsOnlyKeys("spring.foo.name", "spring.foo.counter"); + } + } + } + + @Test + void severalRepositoriesIdenticalGroupsWithSameTypeDoesNotOverrideSource() throws IOException { + try (InputStream foo = getInputStreamFor("foo")) { + try (InputStream foo3 = getInputStreamFor("foo3")) { + ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder.create(foo, foo3) + .build(); + ConfigurationMetadataGroup group = repo.getAllGroups().get("spring.foo"); + ConfigurationMetadataSource fooSource = group.getSources().get("org.acme.Foo"); + assertThat(fooSource.getSourceMethod()).isEqualTo("foo()"); + assertThat(fooSource.getDescription()).isEqualTo("This is Foo."); } } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata/src/test/resources/metadata/configuration-metadata-foo3.json b/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata/src/test/resources/metadata/configuration-metadata-foo3.json new file mode 100644 index 0000000000..e3ea2f120c --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata/src/test/resources/metadata/configuration-metadata-foo3.json @@ -0,0 +1,23 @@ +{ + "groups": [ + { + "name": "spring.foo", + "type": "org.acme.Foo", + "sourceType": "org.acme.config.FooApp", + "sourceMethod": "foo3()", + "description": "This is Foo3." + } + ], + "properties": [ + { + "name": "spring.foo.enabled", + "type": "java.lang.Boolean", + "sourceType": "org.acme.Foo" + }, + { + "name": "spring.foo.type", + "type": "java.lang.String", + "sourceType": "org.acme.Foo" + } + ] +} \ No newline at end of file