Skip to content

Commit

Permalink
Validate constructor bound config props that implement Validator
Browse files Browse the repository at this point in the history
Closes gh-33669
  • Loading branch information
wilkinsona committed Jun 15, 2023
1 parent 836d88c commit e779fb0
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.PropertySources;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import org.springframework.validation.annotation.Validated;

Expand Down Expand Up @@ -136,6 +137,7 @@ private IgnoreTopLevelConverterNotFoundBindHandler getHandler() {
: new IgnoreTopLevelConverterNotFoundBindHandler();
}

@SuppressWarnings("unchecked")
private List<Validator> getValidators(Bindable<?> target) {
List<Validator> validators = new ArrayList<>(3);
if (this.configurationPropertiesValidator != null) {
Expand All @@ -144,8 +146,13 @@ private List<Validator> getValidators(Bindable<?> target) {
if (this.jsr303Present && target.getAnnotation(Validated.class) != null) {
validators.add(getJsr303Validator());
}
if (target.getValue() != null && target.getValue().get() instanceof Validator) {
validators.add((Validator) target.getValue().get());
if (target.getValue() != null) {
if (target.getValue().get() instanceof Validator) {
validators.add((Validator) target.getValue().get());
}
}
else if (Validator.class.isAssignableFrom(target.getType().resolve())) {
validators.add(new SelfValidatingConstructorBoundBindableValidator((Bindable<? extends Validator>) target));
}
return validators;
}
Expand Down Expand Up @@ -258,4 +265,28 @@ private boolean isConfigurationProperties(Class<?> target) {

}

/**
* A {@code Validator} for a constructor-bound {@code Bindable} where the type being
* bound is itself a {@code Validator} implementation.
*/
static class SelfValidatingConstructorBoundBindableValidator implements Validator {

private final Bindable<? extends Validator> bindable;

SelfValidatingConstructorBoundBindableValidator(Bindable<? extends Validator> bindable) {
this.bindable = bindable;
}

@Override
public boolean supports(Class<?> clazz) {
return clazz.isAssignableFrom(this.bindable.getType().resolve());
}

@Override
public void validate(Object target, Errors errors) {
((Validator) target).validate(target, errors);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,16 @@ void loadWhenConfigurationPropertiesIsAlsoValidatorShouldApplyValidator() {
});
}

@Test
void loadWhenConstructorBoundConfigurationPropertiesIsAlsoValidatorShouldApplyValidator() {
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> load(ValidatorConstructorBoundPropertiesConfiguration.class))
.satisfies((ex) -> {
assertThat(ex).hasCauseInstanceOf(BindException.class);
assertThat(ex.getCause()).hasCauseExactlyInstanceOf(BindValidationException.class);
});
}

@Test
void loadWhenConfigurationPropertiesWithValidDefaultValuesShouldNotFail() {
AnnotationConfigApplicationContext context = load(ValidatorPropertiesWithDefaultValues.class);
Expand Down Expand Up @@ -2025,6 +2035,37 @@ void setFoo(String foo) {

}

@EnableConfigurationProperties(ValidatorConstructorBoundProperties.class)
static class ValidatorConstructorBoundPropertiesConfiguration {

}

@ConstructorBinding
@ConfigurationProperties
static class ValidatorConstructorBoundProperties implements Validator {

private final String foo;

ValidatorConstructorBoundProperties(String foo) {
this.foo = foo;
}

@Override
public boolean supports(Class<?> type) {
return type == ValidatorConstructorBoundProperties.class;
}

@Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmpty(errors, "foo", "TEST1");
}

String getFoo() {
return this.foo;
}

}

@EnableConfigurationProperties
@ConfigurationProperties(prefix = "test")
static class WithSetterThatThrowsValidationExceptionProperties {
Expand Down

0 comments on commit e779fb0

Please sign in to comment.