Skip to content

Commit

Permalink
Support custom validator annotations at the nested element method (#1004
Browse files Browse the repository at this point in the history
)
  • Loading branch information
radcortez authored Sep 22, 2023
1 parent de53124 commit b084300
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package io.smallrye.config;

import java.util.Iterator;
import java.util.List;

public class SmallRyeConfigSourceInterceptors implements ConfigSourceInterceptorContext {
private final List<ConfigSourceInterceptor> interceptors;

private int current = 0;

public SmallRyeConfigSourceInterceptors(final List<ConfigSourceInterceptor> interceptors) {
this.interceptors = interceptors;
}

ConfigValue getValue(final String name) {
ConfigValue configValue = null;
for (int i = 0; i < interceptors.size(); i++) {
ConfigSourceInterceptor interceptor = interceptors.get(i);
configValue = interceptor.getValue(this, name);
}
return configValue;
}

@Override
public ConfigValue proceed(final String name) {
ConfigSourceInterceptorContext context = new ConfigSourceInterceptorContext() {
int position = 0;

@Override
public ConfigValue proceed(final String name) {
return interceptors.get(position++).getValue(this, name);
}

@Override
public Iterator<String> iterateNames() {
return null;
}

@Override
public Iterator<ConfigValue> iterateValues() {
return null;
}
};

return context.proceed(name);
}

@Override
public Iterator<String> iterateNames() {
return null;
}

@Override
public Iterator<ConfigValue> iterateValues() {
return null;
}

public static void main(String[] args) {
SmallRyeConfigSourceInterceptors interceptors = new SmallRyeConfigSourceInterceptors(
List.of(new ConfigSourceInterceptor() {
@Override
public ConfigValue getValue(final ConfigSourceInterceptorContext context, final String name) {
return context.proceed(name);
}
}, new ConfigSourceInterceptor() {
@Override
public ConfigValue getValue(final ConfigSourceInterceptorContext context, final String name) {
return context.proceed(name);
}
}, new ConfigSourceInterceptor() {
@Override
public ConfigValue getValue(final ConfigSourceInterceptorContext context, final String name) {
throw new RuntimeException();
}
}));

interceptors.getValue("foo.bar");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ default void validateProperty(
group = optionalGroup.get();
}

validatePropertyValue(property, currentPath, namingStrategy, mappingObject, problems);
validateMappingInterface(property.asGroup().getGroupType(), appendPropertyName(currentPath, property),
namingStrategy, group, problems);
} catch (IllegalAccessException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import io.smallrye.config.ConfigValidationException;
import io.smallrye.config.SmallRyeConfig;
import io.smallrye.config.SmallRyeConfigBuilder;
import io.smallrye.config.WithDefault;
import io.smallrye.config.WithParentName;

public class ValidateConfigTest {
Expand Down Expand Up @@ -384,6 +385,53 @@ public interface Child extends Parent {

}

@Test
void nestedMethodValidation() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.withValidator(new BeanValidationConfigValidatorImpl())
.withMapping(MethodValidation.class)
.build();

ConfigValidationException validationException = assertThrows(ConfigValidationException.class,
() -> config.getConfigMapping(MethodValidation.class));
List<String> validations = new ArrayList<>();
for (int i = 0; i < validationException.getProblemCount(); i++) {
validations.add(validationException.getProblem(i).getMessage());
}

assertEquals(1, validationException.getProblemCount());
assertTrue(validations.contains("method.validation.nested validation executed"));
}

@ConfigMapping(prefix = "method.validation")
interface MethodValidation {
@NestedValidation
Nested nested();

interface Nested {
@WithDefault("value")
String value();
}
}

@Target({ METHOD, TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = { NestedValidator.class })
public @interface NestedValidation {
String message() default "validation executed";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}

public static class NestedValidator implements ConstraintValidator<NestedValidation, MethodValidation.Nested> {
@Override
public boolean isValid(final MethodValidation.Nested value, final ConstraintValidatorContext context) {
return false;
}
}

private static void assertValidationsEqual(List<String> validations, String... expectedProblemMessages) {
List<String> remainingActual = new ArrayList<>(validations);
List<String> remainingExpected = Stream.of(expectedProblemMessages).collect(Collectors.toList());
Expand Down

0 comments on commit b084300

Please sign in to comment.