List valid values in failure analysis for enum binding failure

Closes gh-11771
pull/11775/head
Andy Wilkinson 7 years ago
parent cedb6b2f17
commit 11064b5d78

@ -16,12 +16,19 @@
package org.springframework.boot.diagnostics.analyzer;
import java.util.Collection;
import java.util.Collections;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.boot.context.properties.bind.BindException;
import org.springframework.boot.context.properties.bind.UnboundConfigurationPropertiesException;
import org.springframework.boot.context.properties.bind.validation.BindValidationException;
import org.springframework.boot.context.properties.source.ConfigurationProperty;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.util.StringUtils;
/**
@ -46,7 +53,7 @@ class BindFailureAnalyzer extends AbstractFailureAnalyzer<BindException> {
private FailureAnalysis analyzeGenericBindException(BindException cause) {
StringBuilder description = new StringBuilder(
String.format("Binding to target %s failed:%n", cause.getTarget()));
String.format("%s:%n", cause.getMessage()));
ConfigurationProperty property = cause.getProperty();
buildDescription(description, property);
description.append(String.format("%n Reason: %s", getMessage(cause)));
@ -71,8 +78,29 @@ class BindFailureAnalyzer extends AbstractFailureAnalyzer<BindException> {
}
private FailureAnalysis getFailureAnalysis(Object description, BindException cause) {
return new FailureAnalysis(description.toString(),
"Update your application's configuration", cause);
StringBuilder message = new StringBuilder(
"Update your application's configuration");
Collection<String> validValues = findValidValues(cause);
if (!validValues.isEmpty()) {
message.append(String.format(". The following values are valid:%n"));
validValues
.forEach((value) -> message.append(String.format("%n %s", value)));
}
return new FailureAnalysis(description.toString(), message.toString(), cause);
}
private Collection<String> findValidValues(BindException ex) {
ConversionFailedException conversionFailure = findCause(ex,
ConversionFailedException.class);
if (conversionFailure != null) {
Object[] enumConstants = conversionFailure.getTargetType().getType()
.getEnumConstants();
if (enumConstants != null) {
return Stream.of(enumConstants).map(Object::toString)
.collect(Collectors.toCollection(TreeSet::new));
}
}
return Collections.emptySet();
}
}

@ -19,6 +19,7 @@ package org.springframework.boot.diagnostics.analyzer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.validation.constraints.Min;
@ -67,6 +68,15 @@ public class BindFailureAnalyzerTests {
"Could not resolve placeholder 'BAR' in value \"${BAR}\""));
}
@Test
public void bindExceptionForUnknownValueInEnumListsValidValuesInAction() {
FailureAnalysis analysis = performAnalysis(EnumFailureConfiguration.class,
"test.foo.fruit=apple,strawberry");
for (Fruit fruit : Fruit.values()) {
assertThat(analysis.getAction()).contains(fruit.name());
}
}
private static String failure(String property, String value, String origin,
String reason) {
return String.format(
@ -124,6 +134,11 @@ public class BindFailureAnalyzerTests {
}
@EnableConfigurationProperties(EnumFailureProperties.class)
static class EnumFailureConfiguration {
}
@ConfigurationProperties("test.foo")
@Validated
static class FieldValidationFailureProperties {
@ -170,4 +185,25 @@ public class BindFailureAnalyzerTests {
}
@ConfigurationProperties("test.foo")
static class EnumFailureProperties {
private Set<Fruit> fruit;
public Set<Fruit> getFruit() {
return this.fruit;
}
public void setFruit(Set<Fruit> fruit) {
this.fruit = fruit;
}
}
enum Fruit {
APPLE, BANANA, ORANGE;
}
}

Loading…
Cancel
Save