Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
package org.springframework.restdocs.constraints;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import jakarta.validation.Validation;
import jakarta.validation.Validator;
Expand All @@ -38,40 +40,51 @@
*/
public class ValidatorConstraintResolver implements ConstraintResolver {

private final Class<?>[] groups;

private final Validator validator;

/**
* Creates a new {@code ValidatorConstraintResolver} that will use a {@link Validator}
* in its default configuration to resolve constraints.
*
* @param groups the validation groups to consider when resolving constraints
* @see Validation#buildDefaultValidatorFactory()
* @see ValidatorFactory#getValidator()
*/
public ValidatorConstraintResolver() {
this(Validation.buildDefaultValidatorFactory().getValidator());
public ValidatorConstraintResolver(Class<?>... groups) {
this(Validation.buildDefaultValidatorFactory().getValidator(), groups);
}

/**
* Creates a new {@code ValidatorConstraintResolver} that will use the given
* {@code Validator} to resolve constraints.
* @param validator the validator
* @param groups the validation groups to consider when resolving constraints.
*/
public ValidatorConstraintResolver(Validator validator) {
public ValidatorConstraintResolver(Validator validator, Class<?>... groups) {
this.validator = validator;
this.groups = groups;
}

@Override
public List<Constraint> resolveForProperty(String property, Class<?> clazz) {
List<Constraint> constraints = new ArrayList<>();
for (ConstraintDescriptor<?> constraintDescriptor : getConstraintDescriptors(property, clazz)) {
constraints.add(new Constraint(constraintDescriptor.getAnnotation().annotationType().getName(),
constraintDescriptor.getAttributes()));
}
return constraints;
}

private Set<ConstraintDescriptor<?>> getConstraintDescriptors(String property, Class<?> clazz) {
BeanDescriptor beanDescriptor = this.validator.getConstraintsForClass(clazz);
PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty(property);
if (propertyDescriptor != null) {
for (ConstraintDescriptor<?> constraintDescriptor : propertyDescriptor.getConstraintDescriptors()) {
constraints.add(new Constraint(constraintDescriptor.getAnnotation().annotationType().getName(),
constraintDescriptor.getAttributes()));
}
return propertyDescriptor.findConstraints()
.unorderedAndMatchingGroups(this.groups)
.getConstraintDescriptors();
}
return constraints;
return Collections.emptySet();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import jakarta.validation.constraints.Size;
import jakarta.validation.groups.Default;
import org.assertj.core.api.Condition;
import org.assertj.core.description.TextDescription;
import org.hibernate.validator.constraints.CompositionType;
Expand All @@ -46,33 +47,84 @@
*/
class ValidatorConstraintResolverTests {

private final ValidatorConstraintResolver resolver = new ValidatorConstraintResolver();

@Test
void singleFieldConstraint() {
List<Constraint> constraints = this.resolver.resolveForProperty("single", ConstrainedFields.class);
List<Constraint> constraints = resolveForProperty("single", ConstrainedFields.class);
assertThat(constraints).hasSize(1);
assertThat(constraints.get(0).getName()).isEqualTo(NotNull.class.getName());
}

@Test
void multipleFieldConstraints() {
List<Constraint> constraints = this.resolver.resolveForProperty("multiple", ConstrainedFields.class);
List<Constraint> constraints = resolveForProperty("multiple", ConstrainedFields.class);
assertThat(constraints).hasSize(2);
assertThat(constraints.get(0)).is(constraint(NotNull.class));
assertThat(constraints.get(1)).is(constraint(Size.class).config("min", 8).config("max", 16));
}

@Test
void noFieldConstraints() {
List<Constraint> constraints = this.resolver.resolveForProperty("none", ConstrainedFields.class);
List<Constraint> constraints = resolveForProperty("none", ConstrainedFields.class);
assertThat(constraints).hasSize(0);
}

@Test
void compositeConstraint() {
List<Constraint> constraints = this.resolver.resolveForProperty("composite", ConstrainedFields.class);
List<Constraint> constraints = resolveForProperty("composite", ConstrainedFields.class);
assertThat(constraints).hasSize(1);
}

@Test
void constraintsWithSpecificGroup() {
List<Constraint> constraints = resolveForProperty("street1", ConstrainedFields.class, BasicPostal.class);
assertThat(constraints).hasSize(1);
assertThat(constraints.get(0)).is(constraint(NotNull.class));
}

@Test
void constraintsWithSpecificGroupInheritance() {
List<Constraint> constraints = resolveForProperty("street1", ConstrainedFields.class, FullPostal.class);
assertThat(constraints).hasSize(1);
assertThat(constraints.get(0)).is(constraint(NotNull.class));
}

@Test
void constraintsWithSpecificGroupInheritanceIncludingDefault() {
List<Constraint> constraints = resolveForProperty("single", ConstrainedFields.class, FullPostal.class);
assertThat(constraints).hasSize(1);
assertThat(constraints.get(0)).is(constraint(NotNull.class));
}

@Test
void constraintsWithNoMatchingGroup() {
List<Constraint> constraints = resolveForProperty("doorCode", ConstrainedFields.class, BasicPostal.class);
assertThat(constraints).hasSize(0);
}

@Test
void constraintsWithMatchingGroup() {
List<Constraint> constraints = resolveForProperty("doorCode", ConstrainedFields.class, FullPostal.class);
assertThat(constraints).hasSize(1);
assertThat(constraints.get(0)).is(constraint(NotNull.class));
}

@Test
void constraintsWithMultipleGroups() {
List<Constraint> constraints = resolveForProperty("street1", ConstrainedFields.class, BasicPostal.class,
FullPostal.class);
assertThat(constraints).hasSize(1);
assertThat(constraints.get(0)).is(constraint(NotNull.class));
}

@Test
void constraintsWithDefaultGroup() {
List<Constraint> constraints = resolveForProperty("single", ConstrainedFields.class, Default.class);
assertThat(constraints).hasSize(1);
}

private List<Constraint> resolveForProperty(String property, Class<?> clazz, Class<?>... groups) {
ValidatorConstraintResolver resolver = new ValidatorConstraintResolver(groups);
return resolver.resolveForProperty(property, clazz);
}

private ConstraintCondition constraint(final Class<? extends Annotation> annotation) {
Expand All @@ -94,6 +146,15 @@ private static final class ConstrainedFields {
@CompositeConstraint
private String composite;

@NotNull(groups = BasicPostal.class)
String street1;

@NotNull(groups = BasicPostal.class)
String zipCode;

@NotNull(groups = FullPostal.class)
String doorCode;

}

@ConstraintComposition(CompositionType.OR)
Expand Down Expand Up @@ -143,4 +204,12 @@ public boolean matches(Constraint constraint) {

}

interface BasicPostal extends Default {

}

interface FullPostal extends BasicPostal {

}

}
Loading