parent
b655523ef9
commit
32b7b312f0
@ -0,0 +1,58 @@
|
|||||||
|
plugins {
|
||||||
|
id "java"
|
||||||
|
id "org.springframework.boot.conventions"
|
||||||
|
}
|
||||||
|
|
||||||
|
description = "Spring Boot Configuration Metadata Changelog Generator"
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
oldMetadata
|
||||||
|
newMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(enforcedPlatform(project(":spring-boot-project:spring-boot-dependencies")))
|
||||||
|
implementation(project(":spring-boot-project:spring-boot-tools:spring-boot-configuration-metadata"))
|
||||||
|
|
||||||
|
testImplementation(enforcedPlatform(project(":spring-boot-project:spring-boot-dependencies")))
|
||||||
|
testImplementation("org.assertj:assertj-core")
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.hasProperty("oldVersion") && project.hasProperty("newVersion")) {
|
||||||
|
dependencies {
|
||||||
|
["spring-boot",
|
||||||
|
"spring-boot-actuator",
|
||||||
|
"spring-boot-actuator-autoconfigure",
|
||||||
|
"spring-boot-autoconfigure",
|
||||||
|
"spring-boot-devtools",
|
||||||
|
"spring-boot-test-autoconfigure"].each {
|
||||||
|
oldMetadata("org.springframework.boot:$it:$oldVersion")
|
||||||
|
newMetadata("org.springframework.boot:$it:$newVersion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def prepareOldMetadata = tasks.register("prepareOldMetadata", Sync) {
|
||||||
|
from(configurations.oldMetadata)
|
||||||
|
if (project.hasProperty("oldVersion")) {
|
||||||
|
destinationDir = project.file("build/configuration-metadata-diff/$oldVersion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def prepareNewMetadata = tasks.register("prepareNewMetadata", Sync) {
|
||||||
|
from(configurations.newMetadata)
|
||||||
|
if (project.hasProperty("newVersion")) {
|
||||||
|
destinationDir = project.file("build/configuration-metadata-diff/$newVersion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("generate", JavaExec) {
|
||||||
|
inputs.files(prepareOldMetadata, prepareNewMetadata)
|
||||||
|
outputs.file(project.file("build/configuration-metadata-changelog.adoc"))
|
||||||
|
classpath = sourceSets.main.runtimeClasspath
|
||||||
|
mainClass = 'org.springframework.boot.configurationmetadata.changelog.ConfigurationMetadataChangelogGenerator'
|
||||||
|
if (project.hasProperty("oldVersion") && project.hasProperty("newVersion")) {
|
||||||
|
args = [project.file("build/configuration-metadata-diff/$oldVersion"), project.file("build/configuration-metadata-diff/$newVersion"), project.file("build/configuration-metadata-changelog.adoc")]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.configurationmetadata.changelog;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a configuration metadata changelog. Requires three arguments:
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>The path of a directory containing jar files from which the old metadata will be
|
||||||
|
* extracted
|
||||||
|
* <li>The path of a directory containing jar files from which the new metadata will be
|
||||||
|
* extracted
|
||||||
|
* <li>The path of a file to which the changelog will be written
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* The name of each directory will be used to name the old and new metadata in the
|
||||||
|
* generated changelog
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
final class ConfigurationMetadataChangelogGenerator {
|
||||||
|
|
||||||
|
private ConfigurationMetadataChangelogGenerator() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
ConfigurationMetadataDiff diff = ConfigurationMetadataDiff.of(
|
||||||
|
NamedConfigurationMetadataRepository.from(new File(args[0])),
|
||||||
|
NamedConfigurationMetadataRepository.from(new File(args[1])));
|
||||||
|
try (ConfigurationMetadataChangelogWriter writer = new ConfigurationMetadataChangelogWriter(
|
||||||
|
new FileWriter(new File(args[2])))) {
|
||||||
|
writer.write(diff);
|
||||||
|
}
|
||||||
|
System.out.println("\nConfiguration metadata changelog written to '" + args[2] + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.configurationmetadata.changelog;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.text.BreakIterator;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||||
|
import org.springframework.boot.configurationmetadata.Deprecation;
|
||||||
|
import org.springframework.boot.configurationmetadata.changelog.ConfigurationMetadataDiff.Difference;
|
||||||
|
import org.springframework.boot.configurationmetadata.changelog.ConfigurationMetadataDiff.Difference.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a configuration metadata changelog from a {@link ConfigurationMetadataDiff}.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
class ConfigurationMetadataChangelogWriter implements AutoCloseable {
|
||||||
|
|
||||||
|
private final PrintWriter out;
|
||||||
|
|
||||||
|
ConfigurationMetadataChangelogWriter(Writer out) {
|
||||||
|
this.out = new PrintWriter(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(ConfigurationMetadataDiff diff) {
|
||||||
|
this.out.append(String.format("Configuration property changes between `%s` and " + "`%s`%n", diff.leftName(),
|
||||||
|
diff.rightName()));
|
||||||
|
this.out.append(System.lineSeparator());
|
||||||
|
this.out.append(String.format("== Deprecated in `%s`%n", diff.rightName()));
|
||||||
|
Map<Type, List<Difference>> differencesByType = differencesByType(diff);
|
||||||
|
writeDeprecatedProperties(differencesByType.get(Type.DEPRECATED));
|
||||||
|
this.out.append(System.lineSeparator());
|
||||||
|
this.out.append(String.format("== New in `%s`%n", diff.rightName()));
|
||||||
|
writeAddedProperties(differencesByType.get(Type.ADDED));
|
||||||
|
this.out.append(System.lineSeparator());
|
||||||
|
this.out.append(String.format("== Removed in `%s`%n", diff.rightName()));
|
||||||
|
writeRemovedProperties(differencesByType.get(Type.DELETED), differencesByType.get(Type.DEPRECATED));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Type, List<Difference>> differencesByType(ConfigurationMetadataDiff diff) {
|
||||||
|
Map<Type, List<Difference>> differencesByType = new HashMap<>();
|
||||||
|
for (Type type : Type.values()) {
|
||||||
|
differencesByType.put(type, new ArrayList<>());
|
||||||
|
}
|
||||||
|
for (Difference difference : diff.differences()) {
|
||||||
|
differencesByType.get(difference.type()).add(difference);
|
||||||
|
}
|
||||||
|
return differencesByType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeDeprecatedProperties(List<Difference> differences) {
|
||||||
|
if (differences.isEmpty()) {
|
||||||
|
this.out.append(String.format("None.%n"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
List<Difference> properties = sortProperties(differences, Difference::right).stream()
|
||||||
|
.filter(this::isDeprecatedInRelease)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
this.out.append(String.format("|======================%n"));
|
||||||
|
this.out.append(String.format("|Key |Replacement |Reason%n"));
|
||||||
|
properties.forEach((diff) -> {
|
||||||
|
ConfigurationMetadataProperty property = diff.right();
|
||||||
|
writeDeprecatedProperty(property);
|
||||||
|
});
|
||||||
|
this.out.append(String.format("|======================%n"));
|
||||||
|
}
|
||||||
|
this.out.append(String.format("%n%n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDeprecatedInRelease(Difference difference) {
|
||||||
|
return difference.right().getDeprecation() != null
|
||||||
|
&& Deprecation.Level.ERROR != difference.right().getDeprecation().getLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeAddedProperties(List<Difference> differences) {
|
||||||
|
if (differences.isEmpty()) {
|
||||||
|
this.out.append(String.format("None.%n"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
List<Difference> properties = sortProperties(differences, Difference::right);
|
||||||
|
this.out.append(String.format("|======================%n"));
|
||||||
|
this.out.append(String.format("|Key |Default value |Description%n"));
|
||||||
|
properties.forEach((diff) -> writeRegularProperty(diff.right()));
|
||||||
|
this.out.append(String.format("|======================%n"));
|
||||||
|
}
|
||||||
|
this.out.append(String.format("%n%n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeRemovedProperties(List<Difference> deleted, List<Difference> deprecated) {
|
||||||
|
List<Difference> removed = getRemovedProperties(deleted, deprecated);
|
||||||
|
if (removed.isEmpty()) {
|
||||||
|
this.out.append(String.format("None.%n"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.out.append(String.format("|======================%n"));
|
||||||
|
this.out.append(String.format("|Key |Replacement |Reason%n"));
|
||||||
|
removed.forEach((property) -> writeDeprecatedProperty(
|
||||||
|
(property.right() != null) ? property.right() : property.left()));
|
||||||
|
this.out.append(String.format("|======================%n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Difference> getRemovedProperties(List<Difference> deleted, List<Difference> deprecated) {
|
||||||
|
List<Difference> properties = new ArrayList<>(deleted);
|
||||||
|
properties.addAll(deprecated.stream().filter((p) -> !isDeprecatedInRelease(p)).collect(Collectors.toList()));
|
||||||
|
return sortProperties(properties,
|
||||||
|
(difference) -> (difference.left() != null) ? difference.left() : difference.right());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeRegularProperty(ConfigurationMetadataProperty property) {
|
||||||
|
this.out.append("|`").append(property.getId()).append("` |");
|
||||||
|
if (property.getDefaultValue() != null) {
|
||||||
|
this.out.append("`").append(defaultValueToString(property.getDefaultValue())).append("`");
|
||||||
|
}
|
||||||
|
this.out.append(" |");
|
||||||
|
if (property.getDescription() != null) {
|
||||||
|
this.out.append(property.getShortDescription());
|
||||||
|
}
|
||||||
|
this.out.append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeDeprecatedProperty(ConfigurationMetadataProperty property) {
|
||||||
|
Deprecation deprecation = (property.getDeprecation() != null) ? property.getDeprecation() : new Deprecation();
|
||||||
|
this.out.append("|`").append(property.getId()).append("` |");
|
||||||
|
if (deprecation.getReplacement() != null) {
|
||||||
|
this.out.append("`").append(deprecation.getReplacement()).append("`");
|
||||||
|
}
|
||||||
|
this.out.append(" |");
|
||||||
|
if (deprecation.getReason() != null) {
|
||||||
|
this.out.append(getFirstSentence(deprecation.getReason()));
|
||||||
|
}
|
||||||
|
this.out.append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFirstSentence(String text) {
|
||||||
|
int dot = text.indexOf('.');
|
||||||
|
if (dot != -1) {
|
||||||
|
BreakIterator breakIterator = BreakIterator.getSentenceInstance(Locale.US);
|
||||||
|
breakIterator.setText(text);
|
||||||
|
String sentence = text.substring(breakIterator.first(), breakIterator.next()).trim();
|
||||||
|
return removeSpaceBetweenLine(sentence);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
String[] lines = text.split(System.lineSeparator());
|
||||||
|
return lines[0].trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String removeSpaceBetweenLine(String text) {
|
||||||
|
String[] lines = text.split(System.lineSeparator());
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (String line : lines) {
|
||||||
|
sb.append(line.trim()).append(" ");
|
||||||
|
}
|
||||||
|
return sb.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Difference> sortProperties(List<Difference> properties,
|
||||||
|
Function<Difference, ConfigurationMetadataProperty> property) {
|
||||||
|
List<Difference> sorted = new ArrayList<>(properties);
|
||||||
|
sorted.sort((o1, o2) -> property.apply(o1).getId().compareTo(property.apply(o2).getId()));
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String defaultValueToString(Object defaultValue) {
|
||||||
|
if (defaultValue instanceof Object[]) {
|
||||||
|
return Stream.of((Object[]) defaultValue).map(Object::toString).collect(Collectors.joining(", "));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return defaultValue.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
this.out.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.configurationmetadata.changelog;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository;
|
||||||
|
import org.springframework.boot.configurationmetadata.Deprecation.Level;
|
||||||
|
import org.springframework.boot.configurationmetadata.changelog.ConfigurationMetadataDiff.Difference.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A diff of two repositories of configuration metadata.
|
||||||
|
*
|
||||||
|
* @param leftName the name of the left-hand side of the diff
|
||||||
|
* @param rightName the name of the right-hand side of the diff
|
||||||
|
* @param differences the differences
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
record ConfigurationMetadataDiff(String leftName, String rightName, List<Difference> differences) {
|
||||||
|
|
||||||
|
static ConfigurationMetadataDiff of(NamedConfigurationMetadataRepository left,
|
||||||
|
NamedConfigurationMetadataRepository right) {
|
||||||
|
return new ConfigurationMetadataDiff(left.getName(), right.getName(), differences(left, right));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Difference> differences(ConfigurationMetadataRepository left,
|
||||||
|
ConfigurationMetadataRepository right) {
|
||||||
|
List<Difference> differences = new ArrayList<>();
|
||||||
|
List<String> matches = new ArrayList<>();
|
||||||
|
Map<String, ConfigurationMetadataProperty> leftProperties = left.getAllProperties();
|
||||||
|
Map<String, ConfigurationMetadataProperty> rightProperties = right.getAllProperties();
|
||||||
|
for (ConfigurationMetadataProperty leftProperty : leftProperties.values()) {
|
||||||
|
String id = leftProperty.getId();
|
||||||
|
matches.add(id);
|
||||||
|
ConfigurationMetadataProperty rightProperty = rightProperties.get(id);
|
||||||
|
if (rightProperty == null) {
|
||||||
|
if (!(leftProperty.isDeprecated() && leftProperty.getDeprecation().getLevel() == Level.ERROR)) {
|
||||||
|
differences.add(new Difference(Type.DELETED, leftProperty, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rightProperty.isDeprecated() && !leftProperty.isDeprecated()) {
|
||||||
|
differences.add(new Difference(Type.DEPRECATED, leftProperty, rightProperty));
|
||||||
|
}
|
||||||
|
else if (leftProperty.isDeprecated() && leftProperty.getDeprecation().getLevel() == Level.WARNING
|
||||||
|
&& rightProperty.isDeprecated() && rightProperty.getDeprecation().getLevel() == Level.ERROR) {
|
||||||
|
differences.add(new Difference(Type.DELETED, leftProperty, rightProperty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (ConfigurationMetadataProperty rightProperty : rightProperties.values()) {
|
||||||
|
if ((!matches.contains(rightProperty.getId())) && (!rightProperty.isDeprecated())) {
|
||||||
|
differences.add(new Difference(Type.ADDED, null, rightProperty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return differences;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A difference in the metadata.
|
||||||
|
*
|
||||||
|
* @param type the type of the difference
|
||||||
|
* @param left the left-hand side of the difference
|
||||||
|
* @param right the right-hand side of the difference
|
||||||
|
*/
|
||||||
|
static record Difference(Type type, ConfigurationMetadataProperty left, ConfigurationMetadataProperty right) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of a difference in the metadata.
|
||||||
|
*/
|
||||||
|
enum Type {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry has been added.
|
||||||
|
*/
|
||||||
|
ADDED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry has been made deprecated. It may or may not still exist in the
|
||||||
|
* previous version.
|
||||||
|
*/
|
||||||
|
DEPRECATED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry has been deleted.
|
||||||
|
*/
|
||||||
|
DELETED
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.configurationmetadata.changelog;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataGroup;
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository;
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link ConfigurationMetadataRepository} with a name.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
class NamedConfigurationMetadataRepository implements ConfigurationMetadataRepository {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final ConfigurationMetadataRepository delegate;
|
||||||
|
|
||||||
|
NamedConfigurationMetadataRepository(String name, ConfigurationMetadataRepository delegate) {
|
||||||
|
this.name = name;
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the metadata held in the repository.
|
||||||
|
* @return the name of the metadata
|
||||||
|
*/
|
||||||
|
String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, ConfigurationMetadataGroup> getAllGroups() {
|
||||||
|
return this.delegate.getAllGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, ConfigurationMetadataProperty> getAllProperties() {
|
||||||
|
return this.delegate.getAllProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
static NamedConfigurationMetadataRepository from(File metadataDir) {
|
||||||
|
ConfigurationMetadataRepositoryJsonBuilder builder = ConfigurationMetadataRepositoryJsonBuilder.create();
|
||||||
|
for (File jar : metadataDir.listFiles()) {
|
||||||
|
try (JarFile jarFile = new JarFile(jar)) {
|
||||||
|
JarEntry jsonMetadata = jarFile.getJarEntry("META-INF/spring-configuration-metadata.json");
|
||||||
|
if (jsonMetadata != null) {
|
||||||
|
builder.withJsonResource(jarFile.getInputStream(jsonMetadata));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new NamedConfigurationMetadataRepository(metadataDir.getName(), builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring Boot configuration metadata changelog generator.
|
||||||
|
*/
|
||||||
|
package org.springframework.boot.configurationmetadata.changelog;
|
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.configurationmetadata.changelog;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository;
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder;
|
||||||
|
import org.springframework.boot.configurationmetadata.changelog.ConfigurationMetadataDiff.Difference;
|
||||||
|
import org.springframework.boot.configurationmetadata.changelog.ConfigurationMetadataDiff.Difference.Type;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ConfigurationMetadataDiff}.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
class ConfigurationMetadataDiffTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void diffContainsDifferencesBetweenLeftAndRightInputs() {
|
||||||
|
NamedConfigurationMetadataRepository left = new NamedConfigurationMetadataRepository("1.0",
|
||||||
|
load("sample-1.0.json"));
|
||||||
|
NamedConfigurationMetadataRepository right = new NamedConfigurationMetadataRepository("2.0",
|
||||||
|
load("sample-2.0.json"));
|
||||||
|
ConfigurationMetadataDiff diff = ConfigurationMetadataDiff.of(left, right);
|
||||||
|
assertThat(diff).isNotNull();
|
||||||
|
assertThat(diff.leftName()).isEqualTo("1.0");
|
||||||
|
assertThat(diff.rightName()).isEqualTo("2.0");
|
||||||
|
assertThat(diff.differences()).hasSize(4);
|
||||||
|
List<Difference> added = diff.differences()
|
||||||
|
.stream()
|
||||||
|
.filter((difference) -> difference.type() == Type.ADDED)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
assertThat(added).hasSize(1);
|
||||||
|
assertProperty(added.get(0).right(), "test.add", String.class, "new");
|
||||||
|
List<Difference> deleted = diff.differences()
|
||||||
|
.stream()
|
||||||
|
.filter((difference) -> difference.type() == Type.DELETED)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
assertThat(deleted).hasSize(2)
|
||||||
|
.anySatisfy((entry) -> assertProperty(entry.left(), "test.delete", String.class, "delete"))
|
||||||
|
.anySatisfy((entry) -> assertProperty(entry.right(), "test.delete.deprecated", String.class, "delete"));
|
||||||
|
List<Difference> deprecated = diff.differences()
|
||||||
|
.stream()
|
||||||
|
.filter((difference) -> difference.type() == Type.DEPRECATED)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
assertThat(deprecated).hasSize(1);
|
||||||
|
assertProperty(deprecated.get(0).left(), "test.deprecate", String.class, "wrong");
|
||||||
|
assertProperty(deprecated.get(0).right(), "test.deprecate", String.class, "wrong");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertProperty(ConfigurationMetadataProperty property, String id, Class<?> type, Object defaultValue) {
|
||||||
|
assertThat(property).isNotNull();
|
||||||
|
assertThat(property.getId()).isEqualTo(id);
|
||||||
|
assertThat(property.getType()).isEqualTo(type.getName());
|
||||||
|
assertThat(property.getDefaultValue()).isEqualTo(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigurationMetadataRepository load(String filename) {
|
||||||
|
try (InputStream inputStream = new FileInputStream("src/test/resources/" + filename)) {
|
||||||
|
return ConfigurationMetadataRepositoryJsonBuilder.create(inputStream).build();
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "test.equal",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Test equality.",
|
||||||
|
"defaultValue": "test"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "test.deprecate",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Test deprecate.",
|
||||||
|
"defaultValue": "wrong"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "test.delete",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Test delete.",
|
||||||
|
"defaultValue": "delete"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "test.delete.deprecated",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Test delete deprecated.",
|
||||||
|
"defaultValue": "delete",
|
||||||
|
"deprecation": {
|
||||||
|
"level": "warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "test.add",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Test add.",
|
||||||
|
"defaultValue": "new"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "test.equal",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Test equality.",
|
||||||
|
"defaultValue": "test"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "test.deprecate",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Test deprecate.",
|
||||||
|
"defaultValue": "wrong",
|
||||||
|
"deprecation": {
|
||||||
|
"level": "error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "test.delete.deprecated",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Test delete deprecated.",
|
||||||
|
"defaultValue": "delete",
|
||||||
|
"deprecation": {
|
||||||
|
"level": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue