mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-24 10:19:03 +00:00
Merge branch '4.0.x'
Closes gh-49035
This commit is contained in:
+2
-1
@@ -58,7 +58,8 @@ public abstract class CheckAdditionalSpringConfigurationMetadata extends SourceT
|
||||
void check() throws IOException {
|
||||
ConfigurationPropertiesAnalyzer analyzer = new ConfigurationPropertiesAnalyzer(getSource().getFiles());
|
||||
Report report = new Report(this.projectDir);
|
||||
analyzer.analyzeSort(report);
|
||||
analyzer.analyzeOrder(report);
|
||||
analyzer.analyzeDuplicates(report);
|
||||
analyzer.analyzeDeprecationSince(report);
|
||||
File reportFile = getReportLocation().get().getAsFile();
|
||||
report.write(reportFile);
|
||||
|
||||
+2
-1
@@ -64,7 +64,8 @@ public abstract class CheckManualSpringConfigurationMetadata extends DefaultTask
|
||||
ConfigurationPropertiesAnalyzer analyzer = new ConfigurationPropertiesAnalyzer(
|
||||
List.of(getMetadataLocation().get()));
|
||||
Report report = new Report(this.projectDir);
|
||||
analyzer.analyzeSort(report);
|
||||
analyzer.analyzeOrder(report);
|
||||
analyzer.analyzeDuplicates(report);
|
||||
analyzer.analyzePropertyDescription(report, getExclusions().get());
|
||||
analyzer.analyzeDeprecationSince(report);
|
||||
File reportFile = getReportLocation().get().getAsFile();
|
||||
|
||||
+44
-10
@@ -25,9 +25,11 @@ import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import tools.jackson.core.StreamReadFeature;
|
||||
@@ -40,15 +42,18 @@ import org.springframework.util.function.SingletonSupplier;
|
||||
/**
|
||||
* Check configuration metadata for inconsistencies. The available checks are:
|
||||
* <ul>
|
||||
* <li>Metadata element should be sorted alphabetically: {@link #analyzeSort(Report)}</li>
|
||||
* <li>Property must have a description:
|
||||
* {@link #analyzePropertyDescription(Report, List)}</li>
|
||||
* <li>Metadata elements {@link #analyzeOrder(Report) must be sorted alphabetically}</li>
|
||||
* <li>Metadata elements {@link #analyzeDuplicates(Report) must not be duplicates}</li>
|
||||
* <li>Properties {@link #analyzePropertyDescription(Report, List) must have a
|
||||
* description}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ConfigurationPropertiesAnalyzer {
|
||||
|
||||
private static final List<String> ELEMENT_TYPES = List.of("groups", "properties", "hints");
|
||||
|
||||
private final Collection<File> sources;
|
||||
|
||||
private final SingletonSupplier<JsonMapper> jsonMapperSupplier;
|
||||
@@ -62,23 +67,23 @@ class ConfigurationPropertiesAnalyzer {
|
||||
.of(() -> JsonMapper.builder().enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION).build());
|
||||
}
|
||||
|
||||
void analyzeSort(Report report) {
|
||||
void analyzeOrder(Report report) {
|
||||
for (File source : this.sources) {
|
||||
report.registerAnalysis(source, analyzeSort(source));
|
||||
report.registerAnalysis(source, analyzeOrder(source));
|
||||
}
|
||||
}
|
||||
|
||||
private Analysis analyzeSort(File source) {
|
||||
private Analysis analyzeOrder(File source) {
|
||||
Map<String, Object> json = readJsonContent(source);
|
||||
Analysis analysis = new Analysis("Metadata element order:");
|
||||
analyzeMetadataElementsSort("groups", json, analysis);
|
||||
analyzeMetadataElementsSort("properties", json, analysis);
|
||||
analyzeMetadataElementsSort("hints", json, analysis);
|
||||
for (String elementType : ELEMENT_TYPES) {
|
||||
analyzeMetadataElementOrder(elementType, json, analysis);
|
||||
}
|
||||
return analysis;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void analyzeMetadataElementsSort(String key, Map<String, Object> json, Analysis analysis) {
|
||||
private void analyzeMetadataElementOrder(String key, Map<String, Object> json, Analysis analysis) {
|
||||
List<Map<String, Object>> groups = (List<Map<String, Object>>) json.getOrDefault(key, Collections.emptyList());
|
||||
List<String> names = groups.stream().map((group) -> (String) group.get("name")).toList();
|
||||
List<String> sortedNames = names.stream().sorted().toList();
|
||||
@@ -92,6 +97,35 @@ class ConfigurationPropertiesAnalyzer {
|
||||
}
|
||||
}
|
||||
|
||||
void analyzeDuplicates(Report report) {
|
||||
for (File source : this.sources) {
|
||||
report.registerAnalysis(source, analyzeDuplicates(source));
|
||||
}
|
||||
}
|
||||
|
||||
private Analysis analyzeDuplicates(File source) {
|
||||
Map<String, Object> json = readJsonContent(source);
|
||||
Analysis analysis = new Analysis("Metadata element duplicates:");
|
||||
for (String elementType : ELEMENT_TYPES) {
|
||||
analyzeMetadataElementDuplicates(elementType, json, analysis);
|
||||
}
|
||||
return analysis;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void analyzeMetadataElementDuplicates(String key, Map<String, Object> json, Analysis analysis) {
|
||||
List<Map<String, Object>> elements = (List<Map<String, Object>>) json.getOrDefault(key,
|
||||
Collections.emptyList());
|
||||
List<String> names = elements.stream().map((group) -> (String) group.get("name")).toList();
|
||||
Set<String> uniqueNames = new HashSet<>();
|
||||
for (int i = 0; i < names.size(); i++) {
|
||||
String name = names.get(i);
|
||||
if (!uniqueNames.add(name)) {
|
||||
analysis.addItem("Duplicate name '" + name + "' at $." + key + "[" + i + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void analyzePropertyDescription(Report report, List<String> exclusions) {
|
||||
for (File source : this.sources) {
|
||||
report.registerAnalysis(source, analyzePropertyDescription(source, exclusions));
|
||||
|
||||
+37
-4
@@ -46,7 +46,7 @@ class ConfigurationPropertiesAnalyzerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeSortWithAlphabeticalOrder(@TempDir File tempDir) throws IOException {
|
||||
void analyzeOrderWithAlphabeticalOrder(@TempDir File tempDir) throws IOException {
|
||||
File metadata = new File(tempDir, "metadata.json");
|
||||
Files.writeString(metadata.toPath(), """
|
||||
{ "properties": [
|
||||
@@ -55,14 +55,14 @@ class ConfigurationPropertiesAnalyzerTests {
|
||||
}""");
|
||||
Report report = new Report(tempDir);
|
||||
ConfigurationPropertiesAnalyzer analyzer = new ConfigurationPropertiesAnalyzer(List.of(metadata));
|
||||
analyzer.analyzeSort(report);
|
||||
analyzer.analyzeOrder(report);
|
||||
assertThat(report.hasProblems()).isFalse();
|
||||
assertThat(report.getAnalyses(metadata)).singleElement()
|
||||
.satisfies(((analysis) -> assertThat(analysis.getItems()).isEmpty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeSortWithViolations(@TempDir File tempDir) throws IOException {
|
||||
void analyzeOrderWithViolations(@TempDir File tempDir) throws IOException {
|
||||
File metadata = new File(tempDir, "metadata.json");
|
||||
Files.writeString(metadata.toPath(), """
|
||||
{ "properties": [
|
||||
@@ -71,7 +71,7 @@ class ConfigurationPropertiesAnalyzerTests {
|
||||
}""");
|
||||
Report report = new Report(tempDir);
|
||||
ConfigurationPropertiesAnalyzer analyzer = new ConfigurationPropertiesAnalyzer(List.of(metadata));
|
||||
analyzer.analyzeSort(report);
|
||||
analyzer.analyzeOrder(report);
|
||||
assertThat(report.hasProblems()).isTrue();
|
||||
assertThat(report.getAnalyses(metadata)).singleElement()
|
||||
.satisfies((analysis) -> assertThat(analysis.getItems()).containsExactly(
|
||||
@@ -79,6 +79,39 @@ class ConfigurationPropertiesAnalyzerTests {
|
||||
"Wrong order at $.properties[1].name - expected 'def' but found 'abc'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeDuplicatesWithNoDuplicates(@TempDir File tempDir) throws IOException {
|
||||
File metadata = new File(tempDir, "metadata.json");
|
||||
Files.writeString(metadata.toPath(), """
|
||||
{ "properties": [
|
||||
{ "name": "abc"}, {"name": "def"}, {"name": "xyz"}
|
||||
]
|
||||
}""");
|
||||
Report report = new Report(tempDir);
|
||||
ConfigurationPropertiesAnalyzer analyzer = new ConfigurationPropertiesAnalyzer(List.of(metadata));
|
||||
analyzer.analyzeOrder(report);
|
||||
assertThat(report.hasProblems()).isFalse();
|
||||
assertThat(report.getAnalyses(metadata)).singleElement()
|
||||
.satisfies(((analysis) -> assertThat(analysis.getItems()).isEmpty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeDuplicatesWithDuplicate(@TempDir File tempDir) throws IOException {
|
||||
File metadata = new File(tempDir, "metadata.json");
|
||||
Files.writeString(metadata.toPath(), """
|
||||
{ "properties": [
|
||||
{ "name": "abc"}, {"name": "abc"}, {"name": "def"}
|
||||
]
|
||||
}""");
|
||||
Report report = new Report(tempDir);
|
||||
ConfigurationPropertiesAnalyzer analyzer = new ConfigurationPropertiesAnalyzer(List.of(metadata));
|
||||
analyzer.analyzeDuplicates(report);
|
||||
assertThat(report.hasProblems()).isTrue();
|
||||
assertThat(report.getAnalyses(metadata)).singleElement()
|
||||
.satisfies((analysis) -> assertThat(analysis.getItems())
|
||||
.containsExactly("Duplicate name 'abc' at $.properties[1]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzePropertyDescription(@TempDir File tempDir) throws IOException {
|
||||
File metadata = new File(tempDir, "metadata.json");
|
||||
|
||||
-11
@@ -151,17 +151,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "spring.datasource.xa.data-source-class-name",
|
||||
"providers": [
|
||||
{
|
||||
"name": "class-reference",
|
||||
"parameters": {
|
||||
"target": "javax.sql.XADataSource"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "spring.datasource.xa.data-source-class-name",
|
||||
"providers": [
|
||||
|
||||
Reference in New Issue
Block a user