Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: validation group inheritance, group validation #7260

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -97,7 +97,7 @@
@Requires(property = ValidatorConfiguration.ENABLED, value = StringUtils.TRUE, defaultValue = StringUtils.TRUE)
public class DefaultValidator implements Validator, ExecutableMethodValidator, ReactiveValidator, AnnotatedElementValidator, BeanDefinitionValidator {

private static final List<Class> DEFAULT_GROUPS = Collections.singletonList(Default.class);
private static final List<Class<?>> DEFAULT_GROUPS = Collections.singletonList(Default.class);
private final ConstraintValidatorRegistry constraintValidatorRegistry;
private final ClockProvider clockProvider;
private final ValueExtractorRegistry valueExtractorRegistry;
Expand Down Expand Up @@ -272,7 +272,6 @@ public Set<String> validatedAnnotatedElement(@NonNull AnnotatedElement element,
final DefaultConstraintValidatorContext context = new DefaultConstraintValidatorContext();
try {
context.addPropertyNode(element.getName(), null);
//noinspection unchecked
validatePropertyInternal(
null,
element,
Expand All @@ -286,7 +285,6 @@ public Set<String> validatedAnnotatedElement(@NonNull AnnotatedElement element,
context.removeLast();
}

//noinspection unchecked
return Collections.unmodifiableSet(overallViolations.stream()
.map(ConstraintViolation::getMessage).collect(Collectors.toSet()));
}
Expand Down Expand Up @@ -1807,7 +1805,7 @@ private <T> DefaultConstraintViolation<T> createIntrospectionConstraintViolation
private final class DefaultConstraintValidatorContext implements ConstraintValidatorContext {
final Set<Object> validatedObjects = new HashSet<>(20);
final PathImpl currentPath;
final List<Class> groups;
final List<Class<?>> groups;
String messageTemplate = null;

private <T> DefaultConstraintValidatorContext(T object, Class<?>... groups) {
Expand All @@ -1819,7 +1817,13 @@ private <T> DefaultConstraintValidatorContext(T object, PathImpl path, Class<?>.
validatedObjects.add(object);
}
if (ArrayUtils.isNotEmpty(groups)) {
this.groups = Arrays.asList(groups);
sanityCheckGroups(groups);

List<Class<?>> groupList = new ArrayList<>();
for (Class<?> group: groups) {
addInheritedGroups(group, groupList);
}
this.groups = Collections.unmodifiableList(groupList);
} else {
this.groups = DEFAULT_GROUPS;
}
Expand All @@ -1831,6 +1835,30 @@ private DefaultConstraintValidatorContext(Class<?>... groups) {
this(null, groups);
}

private void sanityCheckGroups(Class<?>[] groups) {
ArgumentUtils.requireNonNull("groups", groups);

for (Class<?> clazz : groups) {
if (clazz == null) {
throw new IllegalArgumentException("Validation groups must be non-null");
}
if (!clazz.isInterface()) {
throw new IllegalArgumentException(
"Validation groups must be interfaces. " + clazz.getName() + " is not.");
}
}
}

private void addInheritedGroups(Class<?> group, List<Class<?>> groups) {
if (!groups.contains(group)) {
groups.add(group);
}

for (Class<?> inheritedGroup : group.getInterfaces()) {
addInheritedGroups(inheritedGroup, groups);
}
}

@NonNull
@Override
public ClockProvider getClockProvider() {
Expand Down
Expand Up @@ -37,9 +37,21 @@ class ValidatorGroupsSpec extends AbstractTypeElementSpec {

when:
violations = validator.validate(address, GroupThree, GroupTwo)
List messageTemplates = violations*.messageTemplate

then:
violations.size() == 2
messageTemplates.contains('{javax.validation.constraints.Size.message}')
messageTemplates.contains('different message')

when:
violations = validator.validate(address, InheritedGroup)
messageTemplates = violations*.messageTemplate

then:
violations.size() == 2
messageTemplates.contains('{javax.validation.constraints.Size.message}')
messageTemplates.contains('message for default')
}

void "test validate with default group"() {
Expand Down Expand Up @@ -126,13 +138,15 @@ interface GroupThree {}
class Address {
@NotBlank(groups = GroupOne)
@NotBlank(groups = GroupThree, message = "different message")
@NotBlank(message = "message for default")
@Size(min = 5, max = 20, groups = GroupTwo)
String street
}

interface GroupOne {}
interface GroupTwo {}
interface GroupThree {}
interface InheritedGroup extends Default, GroupTwo {}

@Introspected
class AddressTwo {
Expand Down